--- /dev/null
+custom: ['https://spi-inc.org/projects/systemd/', 'https://www.paypal.com/donate/?token=fBGzXDOyIGobZH3oEhYQlYlA61OMRXVnF9XXQqNNehRs-nliAU5XxozIh9z-hlmE-xXC-m']
- REPO_ROOT="$TRAVIS_BUILD_DIR"
stages:
+ - name: Build & test
+ if: type != cron
+
+ - name: Fuzzit-Fuzzing
+ if: type = cron
+
+ - name: Fuzzit-Sanity
+ if: type != cron
+
# Run Coverity periodically instead of for each commit/PR
- name: Coverity
if: type = cron
after_script:
- $CI_MANAGERS/debian.sh CLEANUP
+ - stage: Fuzzit-Sanity
+ name: Continuous Fuzzing Sanity via Fuzzit (sanity)
+ language: bash
+ script:
+ - set -e
+ - $CI_MANAGERS/fuzzit.sh sanity
+ - set +e
+
+ - stage: Fuzzit-Fuzzing
+ name: Continuous Fuzzing Sanity via Fuzzit (fuzzing daily)
+ language: bash
+ script:
+ - set -e
+ - $CI_MANAGERS/fuzzit.sh fuzzing
+ - set +e
+
- stage: Coverity
language: bash
env:
script:
- set -e
# Preconfigure with meson to prevent Coverity from capturing meson metadata
- # Set compiler flag to prevent emit failure
- - $DOCKER_EXEC sh -c "CFLAGS='-D_Float128=long\ double -D_Float64=double -D_Float64x=long\ double -D_Float32=float -D_Float32x=double' meson cov-build -Dman=false"
+ - $DOCKER_EXEC meson cov-build -Dman=false
# Run Coverity
- $DOCKER_EXEC tools/coverity.sh build
- $DOCKER_EXEC tools/coverity.sh upload
build/man/man systemctl
build/man/html systemd.index
+ * libidn2 is used by default if both libidn2 and libidn are installed.
+ Please use -Dlibidn=true when libidn is favorable.
+
+ * The D-Bus "wire format" for CPUAffinity attribute is changed on
+ big-endian machines. Before, bytes were written and read in native
+ machine order as exposed by the native libc __cpu_mask interface.
+ Now, little-endian order is always used (CPUs 0–7 are described by
+ bits 0–7 in byte 0, CPUs 8–15 are described by byte 1, and so on).
+ This change fixes D-Bus calls that cross endianness boundary.
+
+ The presentation format used for CPUAffinity by systemctl show and
+ systemd-analyze dump is changed to present CPU indices instead of the
+ raw __cpu_mask bitmask. For example, CPUAffinity=0-1 would be shown
+ as CPUAffinity=03000000000000000000000000000… (on little-endian) or
+ CPUAffinity=00000000000000300000000000000… (on 64-bit big-endian),
+ and is now shown as CPUAffinity=0-1, matching the input format. The
+ maximum integer that will be printed in new format is 8191 (four
+ digits), while the old format always used a very long number (with
+ the length varying by architecture), so they can be unambiguously
+ distinguished.
+
* /usr/sbin/halt.local is no longer supported. Implementation in
distributions was inconsistent and it seems this functionality was
very rarely used.
* When a [Match] section in .link or .network file is empty (contains
no match patterns), a warning will be emitted. Please add any "match
- all" pattern instead, e.g. OriginalName=* or Name=* if case all
+ all" pattern instead, e.g. OriginalName=* or Name=* in case all
interfaces should really be matched.
+ * A new setting NUMAPolicy= may be used to set process memory
+ allocation policy. Setting can be specified in system.conf and
+ hence will set the default policy for PID1. Default policy can be
+ overridden on per-service basis. Related setting NUMAMask= is used to
+ specify NUMA node mask that should be associated with the selected
+ policy.
…
CHANGES WITH 242:
`SYSTEMD_LOG_LEVEL=debug udevadm test-builtin net_setup_link /sys/class/net/<name>`
may be used to view this.
+ Hint: if a bridge interface is created without any slaves, and gains
+ a slave later, then now the bridge does not inherit slave's MAC.
+ To inherit slave's MAC, for example, create the following file:
+ ```
+ # /etc/systemd/network/98-bridge-inherit-mac.link
+ [Match]
+ Type=bridge
+
+ [Link]
+ MACAddressPolicy=none
+ ```
+
* The .device units generated by systemd-fstab-generator and other
generators do not automatically pull in the corresponding .mount unit
as a Wants= dependency. This means that simply plugging in the device
up a private /dev/ file system containing devices nodes — but when
these are opened they don't work.
- At this point is is recommended that container managers utilizing
+ At this point it is recommended that container managers utilizing
user namespaces that intend to run systemd in the payload explicitly
block mknod() with seccomp or similar, so that the graceful fallback
logic works again.
<a href="https://in.waw.pl/systemd-github-state/systemd-systemd-pull-requests.svg"><img align="right" src="https://in.waw.pl/systemd-github-state/systemd-systemd-pull-requests-small.svg" alt="Count of open pull requests over time"></a>
[![Semaphore CI Build Status](https://semaphoreci.com/api/v1/projects/28a5a3ca-3c56-4078-8b5e-7ed6ef912e14/443470/shields_badge.svg)](https://semaphoreci.com/systemd/systemd)<br/>
[![Coverity Scan Status](https://scan.coverity.com/projects/350/badge.svg)](https://scan.coverity.com/projects/350)<br/>
+[![Fuzzit Status](https://app.fuzzit.dev/badge?org_id=RxqRpGNXquIvqrmp4iJS&branch=master)](https://app.fuzzit.dev/admin/RxqRpGNXquIvqrmp4iJS/dashboard)<br/>
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1369/badge)](https://bestpractices.coreinfrastructure.org/projects/1369)<br/>
[![Travis CI Build Status](https://travis-ci.org/systemd/systemd.svg?branch=master)](https://travis-ci.org/systemd/systemd)<br/>
[![Language Grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/systemd/systemd.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/systemd/systemd/context:cpp)<br/>
Features:
+* seed: check if first-boot and then don't do anything
+
+* logind: rework pam_logind to also do a bus call in case of invocation from
+ user@.service, which returns the XDG_RUNTIME_DIR value, and make this
+ behaviour selectable via pam module option.
+
+* introduce a new per-process uuid, similar to the boot id, the machine id, the
+ invocation id, that is derived from process creds, specifically a hashed
+ combination of AT_RANDOM + getpid() + the starttime from
+ /proc/self/status. Then add these ids implicitly when logging. Deriving this
+ uuid from these three things has the benefit that it can be derived easily
+ from /proc/$PID/ in a stable, and unique way that changes on both fork() and
+ exec().
+
+* let's not GC a unit while its ratelimits are still pending
+
* when killing due to service watchdog timeout maybe detect whether target
process is under ptracing and then log loudly and continue instead.
+* introduce a new group to own TPM devices
+
+* make rfkill uaccess controllable by default, i.e. steal rule from
+ gnome-bluetooth and friends
+
+* warn if udev rules files are marked executable (docker?)
+
* tweak journald context caching. In addition to caching per-process attributes
keyed by PID, cache per-cgroup attributes (i.e. the various xattrs we read)
keyed by cgroup path, and guarded by ctime changes. This should provide us
with a nice speed-up on services that have many processes running in the same
cgroup.
-* clean up sleep.c:
- - Make sure resume= and resume_offset= on the kernel cmdline always take
- precedence
-
* make MAINPID= message reception checks even stricter: if service uses User=,
then check sending UID and ignore message if it doesn't match the user or
root.
safe_fork() is to fork(). And then make revert the RLIMIT_NOFILE soft limit
to 1K implicitly, unless explicitly opted-out.
-* rework seccomp/nnp logic that that even if User= is used in combination with
+* rework seccomp/nnp logic that even if User= is used in combination with
a seccomp option we don't have to set NNP. For that, change uid first whil
keeping CAP_SYS_ADMIN, then apply seccomp, the drop cap.
usefaultd() and make systemd-analyze check for it.
* paranoia: whenever we process passwords, call mlock() on the memory
- first. i.e. look for all places we use string_erase()/string_free_erase() and
- augment them with mlock(). Also use MADV_DONTDUMP
+ first. i.e. look for all places we use free_and_erasep() and
+ augment them with mlock(). Also use MADV_DONTDUMP.
* Move RestrictAddressFamily= to the new cgroup create socket
sufficient to build a link by prefixing "http://" and suffixing the
CODE_FILE.
-* when outputting log data with journalctl and the log data includes references
- to configuration files (CONFIG_FILE=), create a clickable link for it.
-
* Augment MESSAGE_ID with MESSAGE_BASE, in a similar fashion so that we can
make clickable links from log messages carrying a MESSAGE_ID, that lead to
some explanatory text online.
/etc/resolv.conf. Should be smart and do something useful on read-only
images, for example fallback to read-only bind mounting the file instead.
-* nspawn's console TTY should be allocated from within the container, not
- mounted in from the outside
-
* show invocation ID in systemd-run output
* bypass SIGTERM state in unit files if KillSignal is SIGKILL
* expose IO accounting data on the bus, show it in systemd-run --wait and log
about it in the resource log message
-* add "systemctl purge" for flushing out configuration, state, logs, ... of a
- unit when it is stopped
-
* show whether a service has out-of-date configuration in "systemctl status" by
using mtime data of ConfigurationDirectory=.
* maybe add support for specifier expansion in user.conf, specifically DefaultEnvironment=
-* consider showing the unit names during boot up in the status output, not just the unit descriptions
-
* maybe allow timer units with an empty Units= setting, so that they
can be used for resuming the system but nothing else.
see the `+1-2` tag, and rename the file to
`4.14.11-300.fc27.x86_64+0-3.conf` and boot it.
-6. If this boot also fails, on the next boot the boot loader will see the the
+6. If this boot also fails, on the next boot the boot loader will see the
tag `+0-3`, i.e. the counter reached zero. At this point the entry will be
considered "bad", and ordered to the end of the list of entries. The next
newest boot entry is now tried, i.e. the system automatically reverted back
device while operating: before starting work on the block device, they should
take an `LOCK_EX` lock on it. This has two effects: first of all, in case
`systemd-udevd` is still processing the device the tool will wait for it to
-finish. Second, after the lock is taken, it can be sure that that
+finish. Second, after the lock is taken, it can be sure that
`systemd-udevd` will refrain from processing the block device, and thus all
other client applications subscribed to it won't get device notifications from
potentially half-written data either. After the operation is complete the
In unit files the `Delegate=` property is superficially exposed as
boolean. However, since v236 it optionally takes a list of controller names
instead. If so, delegation is requested for listed controllers
-specifically. Note hat this only encodes a request. Depending on various
+specifically. Note that this only encodes a request. Depending on various
parameters it might happen that your service actually will get fewer
controllers delegated (for example, because the controller is not available on
the current kernel or was turned off) or more. If no list is specified
array. In that case use STRLEN, which evaluates to a static constant and
doesn't force the compiler to create a VLA.
+- Please use C's downgrade-to-bool feature only for expressions that are
+ actually booleans (or "boolean-like"), and not for variables that are really
+ numeric. Specifically, if you have an `int b` and it's only used in a boolean
+ sense, by all means check its state with `if (b) …` — but if `b` can actually
+ have more than two semantic values, and you want to compare for non-zero,
+ then please write that explicitly with `if (b != 0) …`. This helps readability
+ as the value range and semantical behaviour is directly clear from the
+ condition check. As a special addition: when dealing with pointers which you
+ want to check for non-NULL-ness, you may also use downgrade-to-bool feature.
+
+- Please do not use yoda comparisons, i.e. please prefer the more readable `if
+ (a == 7)` over the less readable `if (7 == a)`.
+
## Destructors
- The destructors always deregister the object from the next bigger object, not
systemd-remount-fs:
-* `$SYSTEMD_REMOUNT_ROOT_RW=1` — if set and and no entry for the root directory
+* `$SYSTEMD_REMOUNT_ROOT_RW=1` — if set and no entry for the root directory
exists in /etc/fstab (this file always takes precedence), then the root
directory is remounted writable. This is primarily used by
systemd-gpt-auto-generator to ensure the root partition is mounted writable
## Fuzzers
systemd includes fuzzers in `src/fuzz/` that use libFuzzer and are automatically
-run by [OSS-Fuzz](https://github.com/google/oss-fuzz) with sanitizers. To add a
-fuzz target, create a new `src/fuzz/fuzz-foo.c` file with a `LLVMFuzzerTestOneInput`
+run by [OSS-Fuzz](https://github.com/google/oss-fuzz) and [Fuzzit](https://fuzzit.dev) with sanitizers.
+To add a fuzz target, create a new `src/fuzz/fuzz-foo.c` file with a `LLVMFuzzerTestOneInput`
function and add it to the list in `src/fuzz/meson.build`.
Whenever possible, a seed corpus and a dictionary should also be added with new
python infra/helper.py run_fuzzer systemd fuzz-foo
```
+When you add a new target you should also add the target on [Fuzzit](https://app.fuzzit.dev/admin/RxqRpGNXquIvqrmp4iJS/dashboard)
+ (Please ask someone with permissions). One the target is configured on Fuzzit you need to add it to
+ `travis-ci/managers/fuzzit.sh` so the new target will run sanity tests on every pull-request and periodic fuzzing jobs.
+
If you find a bug that impacts the security of systemd, please follow the
guidance in [CONTRIBUTING.md](CONTRIBUTING.md) on how to report a security vulnerability.
--- /dev/null
+You are looking for the traditional text log files in /var/log, and they are
+gone?
+
+Here's an explanation on what's going on:
+
+You are running a systemd-based OS where traditional syslog has been replaced
+with the Journal. The journal stores the same (and more) information as classic
+syslog. To make use of the journal and access the collected log data simply
+invoke "journalctl", which will output the logs in the identical text-based
+format the syslog files in /var/log used to be. For further details, please
+refer to journalctl(1).
+
+Alternatively, consider installing one of the traditional syslog
+implementations available for your distribution, which will generate the
+classic log files for you. Syslog implementations such as syslog-ng or rsyslog
+may be installed side-by-side with the journal and will continue to function
+the way they always did.
+
+Thank you!
+
+Further reading:
+ man:journalctl(1)
+ man:systemd-journald.service(8)
+ man:journald.conf(5)
+ http://0pointer.de/blog/projects/the-journal.html
+++ /dev/null
-You are looking for the traditional text log files in @VARLOGDIR@, and
-they are gone?
-
-Here's an explanation on what's going on:
-
-You are running a systemd-based OS where traditional syslog has been
-replaced with the Journal. The journal stores the same (and more)
-information as classic syslog. To make use of the journal and access
-the collected log data simply invoke "journalctl", which will output
-the logs in the identical text-based format the syslog files in
-@VARLOGDIR@ used to be. For further details, please refer to
-journalctl(1).
-
-Alternatively, consider installing one of the traditional syslog
-implementations available for your distribution, which will generate
-the classic log files for you. Syslog implementations such as
-syslog-ng or rsyslog may be installed side-by-side with the journal
-and will continue to function the way they always did.
-
-Thank you!
-
-Further reading:
- man:journalctl(1)
- man:systemd-journald.service(8)
- man:journald.conf(5)
- http://0pointer.de/blog/projects/the-journal.html
# SPDX-License-Identifier: LGPL-2.1+
-file = configure_file(
- input : 'README.in',
- output : 'README',
- configuration : substs)
-
-if conf.get('HAVE_SYSV_COMPAT') == 1
- install_data(file,
- install_dir : varlogdir)
+if conf.get('HAVE_SYSV_COMPAT') == 1 and get_option('create-log-dirs')
+ install_data('README',
+ install_dir : '/var/log')
endif
sensor:modalias:acpi:INVN6500*:dmi:*svn*ASUSTeK*:*pn*TP300LD*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+sensor:modalias:acpi:INVN6500*:dmi:*svnASUSTeK*:*pn*Q551LN*
+ ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
+
sensor:modalias:acpi:KXJ2109*:dmi:*:svnASUSTeK*:pnME176C*
ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1
sensor:modalias:acpi:BMA250*:dmi:*:bvritWORKS.G.WI71C.JGBMRB*:*:svnInsyde:pni71c:*
ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1
+#########################################
+# Irbis TW90
+#########################################
+sensor:modalias:acpi:BOSC0200*:dmi:*:svnIRBIS:pnTW90:*
+ ACCEL_MOUNT_MATRIX=0, 1, 0; -1, 0, 0; 0, 0, 1
+
#########################################
# iOTA 360
#########################################
<!ENTITY CERTIFICATE_ROOT @CERTIFICATE_ROOT@>
<!ENTITY MEMORY_ACCOUNTING_DEFAULT @MEMORY_ACCOUNTING_DEFAULT_YES_NO@>
<!ENTITY KILL_USER_PROCESSES @KILL_USER_PROCESSES_YES_NO@>
+<!ENTITY DEBUGTTY @DEBUGTTY@>
<term><varname>systemd.confirm_spawn</varname></term>
<term><varname>systemd.service_watchdogs</varname></term>
<term><varname>systemd.show_status</varname></term>
+ <term><varname>systemd.status_unit_format=</varname></term>
<term><varname>systemd.log_target=</varname></term>
<term><varname>systemd.log_level=</varname></term>
<term><varname>systemd.log_location=</varname></term>
<varlistentry>
<term><varname>resume=</varname></term>
+ <term><varname>resumeflags=</varname></term>
<listitem>
<para>Enables resume from hibernation using the specified
- device. All
+ device and mount options. All
<citerefentry project='man-pages'><refentrytitle>fstab</refentrytitle><manvolnum>5</manvolnum></citerefentry>-like
paths are supported. For details, see
<citerefentry><refentrytitle>systemd-hibernate-resume-generator</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<varlistentry>
<term><varname>DNSOverTLS=</varname></term>
<listitem>
- <para>Takes false or
- <literal>opportunistic</literal>. When set to <literal>opportunistic</literal>
+ <para>Takes a boolean argument or <literal>opportunistic</literal>.
+ If true all connections to the server will be encrypted. Note that
+ this mode requires a DNS server that supports DNS-over-TLS and has
+ a valid certificate for it's IP. If the DNS server does not support
+ DNS-over-TLS all DNS requests will fail. When set to <literal>opportunistic</literal>
DNS request are attempted to send encrypted with DNS-over-TLS.
If the DNS server does not support TLS, DNS-over-TLS is disabled.
Note that this mode makes DNS-over-TLS vulnerable to "downgrade"
<replaceable>userdata</replaceable> parameter contains a pointer that will be passed to various callback
functions. It may be specified as <constant>NULL</constant> if no value is necessary.</para>
- <para><function>sd_bus_add_object_vtable()</function> is similar to
+ <para><function>sd_bus_add_fallback_vtable()</function> is similar to
<function>sd_bus_add_object_vtable()</function>, but is used to register "fallback" attributes. When
looking for an attribute declaration, bus object paths registered with
<function>sd_bus_add_object_vtable()</function> are checked first. If no match is found, the fallback
</varlistentry>
<varlistentry>
- <term><constant>SD_BUS_VTABLE_CONST</constant></term>
- <term><constant>SD_BUS_VTABLE_EMITS_CHANGE</constant></term>
- <term><constant>SD_BUS_VTABLE_EMITS_INVALIDATION</constant></term>
+ <term><constant>SD_BUS_VTABLE_PROPERTY_CONST</constant></term>
+ <term><constant>SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE</constant></term>
+ <term><constant>SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION</constant></term>
<listitem><para>Those three flags correspond to different values of the
<constant>org.freedesktop.DBus.Property.EmitsChangedSignal</constant> annotation, which specifies
whether the <constant>org.freedesktop.DBus.Properties.PropertiesChanged</constant> signal is
- emitted whenever the property changes. <constant>SD_BUS_VTABLE_CONST</constant> corresponds to
+ emitted whenever the property changes. <constant>SD_BUS_VTABLE_PROPERTY_CONST</constant> corresponds to
<constant>const</constant> and means that the property never changes during the lifetime of the
object it belongs to, so no signal needs to be emitted.
- <constant>SD_BUS_VTABLE_EMITS_CHANGE</constant> corresponds to <constant>true</constant> and means
- that the signal is emitted. <constant>SD_BUS_VTABLE_EMITS_INVALIDATION</constant> corresponds to
- <constant>invalides</constant> and means that the signal is emitted, but the value is not included
+ <constant>SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE</constant> corresponds to <constant>true</constant> and means
+ that the signal is emitted. <constant>SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION</constant> corresponds to
+ <constant>invalidates</constant> and means that the signal is emitted, but the value is not included
in the signal.</para>
</listitem>
</varlistentry>
<listitem><para>Mark this vtable property entry as requiring explicit request to for the value to
be shown (generally because the value is large or slow to calculate). This entry cannot be combined
- with <constant>SD_BUS_VTABLE_EMITS_CHANGE</constant>, and will not be shown in property listings by
+ with <constant>SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE</constant>, and will not be shown in property listings by
default (e.g. <command>busctl introspect</command>). This corresponds to the
<constant>org.freedesktop.systemd1.Explicit</constant> annotation in introspection data.</para>
</listitem>
<filename>/proc/<replaceable>pid</replaceable>/task/<replaceable>tid</replaceable>/comm</filename>).
</para>
- <para><function>sd_bus_creds_get_exe()</function> will retrieve
- the path to the program executable (as stored in the
- <filename>/proc/<replaceable>pid</replaceable>/exe</filename>
- link, but with the <literal> (deleted)</literal> suffix removed). Note
- that kernel threads do not have an executable path, in which case
- -ENXIO is returned.</para>
+ <para><function>sd_bus_creds_get_exe()</function> will retrieve the path to the program executable (as
+ stored in the <filename>/proc/<replaceable>pid</replaceable>/exe</filename> link, but with the <literal>
+ (deleted)</literal> suffix removed). Note that kernel threads do not have an executable path, in which
+ case -ENXIO is returned. Note that this property should not be used for more than explanatory
+ information, in particular it should not be used for security-relevant decisions. That's because the
+ executable might have been replaced or removed by the time the value can be processed. Moreover, the
+ kernel exports this information in an ambiguous way (i.e. a deleted executable cannot be safely
+ distinguished from one whose name suffix is <literal> (deleted)</literal>.</para>
<para><function>sd_bus_creds_get_cmdline()</function> will
retrieve an array of command line arguments (as stored in
<!--
SPDX-License-Identifier: LGPL-2.1+
-
- Copyright © 2016 Jan Synáček
-->
<refentry id="sd_journal_has_runtime_files" xmlns:xi="http://www.w3.org/2001/XInclude">
<listitem><para>Tells the service manager to extend the startup, runtime or shutdown service timeout
corresponding the current state. The value specified is a time in microseconds during which the service must
send a new message. A service timeout will occur if the message isn't received, but only if the runtime of the
- current state is beyond the original maximium times of <varname>TimeoutStartSec=</varname>, <varname>RuntimeMaxSec=</varname>,
+ current state is beyond the original maximum times of <varname>TimeoutStartSec=</varname>, <varname>RuntimeMaxSec=</varname>,
and <varname>TimeoutStopSec=</varname>.
See <citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for effects on the service timeouts.</para></listitem>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><option>--what=</option></term>
+
+ <listitem>
+ <para>Select what type of per-unit resources to remove when the <command>clean</command> command is
+ invoked, see below. Takes one of <constant>configuration</constant>, <constant>state</constant>,
+ <constant>cache</constant>, <constant>logs</constant>, <constant>runtime</constant> to select the
+ type of resource. This option may be specified more than once, in which case all specified resource
+ types are removed. Also accepts the special value <constant>all</constant> as a shortcut for
+ specifiying all five resource types. If this option is not specified defaults to the combination of
+ <constant>cache</constant> and <constant>runtime</constant>, i.e. the two kinds of resources that
+ are generally considered to be redundant and can be reconstructed on next invocation.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><option>-f</option></term>
<term><option>--force</option></term>
the signal to send.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><command>clean <replaceable>PATTERN</replaceable>…</command></term>
+
+ <listitem>
+ <para>Remove the configuration, state, cache, logs or runtime data of the specified units. Use
+ <option>--what=</option> to select which kind of resource to remove. For service units this may
+ be used to remove the directories configured with <varname>ConfigurationDirectory=</varname>,
+ <varname>StateDirectory=</varname>, <varname>CacheDirectory=</varname>,
+ <varname>LogsDirectory=</varname> and <varname>RuntimeDirectory=</varname>, see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for details. For timer units this may be used to clear out the persistent timestamp data if
+ <varname>Persistent=</varname> is used and <option>--what=state</option> is selected, see
+ <citerefentry><refentrytitle>systemd.timer</refentrytitle><manvolnum>5</manvolnum></citerefentry>. This
+ command only applies to units that use either of these settings. If <option>--what=</option> is
+ not specified, both the cache and runtime data are removed (as these two types of data are
+ generally redundant and reproducible on the next invocation of the unit).</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><command>is-active <replaceable>PATTERN</replaceable>…</command></term>
</row>
<row>
<entry><literal>indirect</literal></entry>
- <entry>The unit file itself is not enabled, but it has a non-empty <varname>Also=</varname> setting in the <literal>[Install]</literal> unit file section, listing other unit files that might be enabled, or it has an alias under a different name through a symlink that is not specified in Also=. For template unit file, an instance different than the one specified in <varname>DefaultInstance=</varname> is enabled.</entry>
+ <entry>The unit file itself is not enabled, but it has a non-empty <varname>Also=</varname> setting in the <literal>[Install]</literal> unit file section, listing other unit files that might be enabled, or it has an alias under a different name through a symlink that is not specified in <varname>Also=</varname>. For template unit file, an instance different than the one specified in <varname>DefaultInstance=</varname> is enabled.</entry>
<entry>0</entry>
</row>
<row>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="plain">unit-paths</arg>
</cmdsynopsis>
+ <cmdsynopsis>
+ <command>systemd-analyze</command>
+ <arg choice="opt" rep="repeat">OPTIONS</arg>
+ <arg choice="plain">condition</arg>
+ <arg choice="plain"><replaceable>CONDITION</replaceable>…</arg>
+ </cmdsynopsis>
<cmdsynopsis>
<command>systemd-analyze</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
to retrieve the actual list that the manager uses, with any empty directories omitted.</para>
</refsect2>
+ <refsect2>
+ <title><command>systemd-analyze condition <replaceable>CONDITION</replaceable>...</command></title>
+
+ <para>This command will evaluate <varname noindex='true'>Condition*=...</varname> and
+ <varname noindex='true'>Assert*=...</varname> assignments, and print their values, and
+ the resulting value of the combined condition set. See
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for a list of available conditions and asserts.</para>
+
+ <example>
+ <title>Evaluate conditions that check kernel versions</title>
+
+ <programlisting>$ systemd-analyze condition 'ConditionKernelVersion = ! <4.0' \
+ 'ConditionKernelVersion = >=5.1' \
+ 'ConditionACPower=|false' \
+ 'ConditionArchitecture=|!arm' \
+ 'AssertPathExists=/etc/os-release'
+test.service: AssertPathExists=/etc/os-release succeeded.
+Asserts succeeded.
+test.service: ConditionArchitecture=|!arm succeeded.
+test.service: ConditionACPower=|false failed.
+test.service: ConditionKernelVersion=>=5.1 succeeded.
+test.service: ConditionKernelVersion=!<4.0 succeeded.
+Conditions succeeded.</programlisting>
+ </example>
+ </refsect2>
+
<refsect2>
<title><command>systemd-analyze syscall-filter <optional><replaceable>SET</replaceable>...</optional></command></title>
<?xml version="1.0"?>
<!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
+<!ENTITY % entities SYSTEM "custom-entities.ent" >
+%entities;
+]>
<!-- SPDX-License-Identifier: LGPL-2.1+ -->
<refentry id="systemd-debug-generator">
<option>rd.systemd.debug_shell</option> option is
specified, the debug shell service
<literal>debug-shell.service</literal> is pulled into the boot
- transaction. It will spawn a debug shell on tty9 during early
- system startup. Note that the shell may also be turned on
- persistently by enabling it with
+ transaction and a debug shell will be spawned during early boot.
+ By default, <filename>&DEBUGTTY;</filename> is used, but a specific tty can also be set,
+ either with or without the <filename>/dev/</filename> prefix.
+ Note that the shell may also be turned on persistently by enabling it with
<citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>'s
<command>enable</command> command.
<option>rd.systemd.debug_shell=</option> is honored only by initial
<filename>/dev/mapper/home</filename> and <filename>/dev/mapper/srv</filename>. Note that this might
create conflicts if the same partition is listed in <filename>/etc/crypttab</filename> with a different
device mapper device name.</para>
+
+ <para>When systemd is running in the initrd the <filename>/</filename> partition may be encrypted in LUKS
+ format as well. In this case, a device mapper device is set up under the name <filename>/dev/mapper/root</filename>,
+ and a <filename>sysroot.mount</filename> is set up that mounts the device under <filename>/sysroot</filename>.
+ For more information, see <citerefentry><refentrytitle>bootup</refentrytitle><manvolnum>7</manvolnum></citerefentry>.
+ </para>
<para>Mount and automount units for the EFI System Partition (ESP) are generated on EFI systems. The ESP
is mounted to <filename>/boot/</filename> (except if an Extended Boot Loader partition exists, see
supported.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>resumeflags=</varname></term>
+
+ <listitem><para>Takes the resume device mount options to
+ use. Defaults <varname>rootflags=</varname> if not specified.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>noresume</varname></term>
<title>Boot a minimal
<ulink url="https://www.archlinux.org">Arch Linux</ulink> distribution in a container</title>
- <programlisting># pacstrap -c -d ~/arch-tree/ base
+ <programlisting># pacstrap -c ~/arch-tree/ base
# systemd-nspawn -bD ~/arch-tree/</programlisting>
<para>This installs a minimal Arch Linux distribution into the
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>NUMAPolicy=</varname></term>
+
+ <listitem><para>Configures the NUMA memory policy for the service manager and the default NUMA memory policy
+ for all forked off processes. Individual services may override the default policy with the
+ <varname>NUMAPolicy=</varname> setting in unit files, see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>NUMAMask=</varname></term>
+
+ <listitem><para>Configures the NUMA node mask that will be associated with the selected NUMA policy. Note that
+ <option>default</option> and <option>local</option> NUMA policies don't require explicit NUMA node mask and
+ value of the option can be empty. Similarly to <varname>NUMAPolicy=</varname>, value can be overridden
+ by individual services in unit files, see
+ <citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>RuntimeWatchdogSec=</varname></term>
<term><varname>ShutdownWatchdogSec=</varname></term>
understood too.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>StatusUnitFormat=</varname></term>
+
+ <listitem><para>Takes either <option>name</option> or <option>description</option> as the value. If
+ <option>name</option>, the system manager will use unit names in status messages, instead of the
+ longer and more informative descriptions set with <varname>Description=</varname>, see
+ <citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>DefaultTimerAccuracySec=</varname></term>
<title>Description</title>
<para><filename>systemd-veritysetup-generator</filename> is a generator that translates kernel command line options
- configuring integrity protected block devices (verity) into native systemd units early at boot and when
+ configuring integrity-protected block devices (verity) into native systemd units early at boot and when
configuration of the system manager is reloaded. This will create
<citerefentry><refentrytitle>systemd-veritysetup@.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
units as necessary.</para>
- <para>Currently, only a single verity device may be se up with this generator, backing the root file system of the
+ <para>Currently, only a single verity device may be set up with this generator, backing the root file system of the
OS.</para>
<para><filename>systemd-veritysetup-generator</filename> implements
<term><varname>roothash=</varname></term>
<listitem><para>Takes a root hash value for the root file system. Expects a hash value formatted in hexadecimal
- characters, of the appropriate length (i.e. most likely 256 bit/64 characters, or longer). If not specified via
+ characters of the appropriate length (i.e. most likely 256 bit/64 characters, or longer). If not specified via
<varname>systemd.verity_root_data=</varname> and <varname>systemd.verity_root_hash=</varname>, the hash and
data devices to use are automatically derived from the specified hash value. Specifically, the data partition
device is looked for under a GPT partition UUID derived from the first 128bit of the root hash, the hash
<term><varname>systemd.verity_root_data=</varname></term>
<term><varname>systemd.verity_root_hash=</varname></term>
- <listitem><para>These two settings take block device paths as arguments, and may be use to explicitly configure
- the data partition and hash partition to use for setting up the integrity protection for the root file
+ <listitem><para>These two settings take block device paths as arguments and may be used to explicitly
+ configure the data partition and hash partition to use for setting up the integrity protection for the root file
system. If not specified, these paths are automatically derived from the <varname>roothash=</varname> argument
(see above).</para></listitem>
</varlistentry>
this unit type. See
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the common options of all unit configuration files. The common
- configuration items are configured in the generic [Unit] and
- [Install] sections. The automount specific configuration options
- are configured in the [Automount] section.</para>
+ configuration items are configured in the generic <literal>[Unit]</literal> and
+ <literal>[Install]</literal> sections. The automount specific configuration options
+ are configured in the <literal>[Automount]</literal> section.</para>
<para>Automount units must be named after the automount directories they control. Example: the automount point
<filename noindex='true'>/home/lennart</filename> must be configured in a unit file
part of a unit for which dynamic users/groups are enabled do not leave files or directories owned by
these users/groups around, as a different unit might get the same UID/GID assigned later on, and thus
gain access to these files or directories. If <varname>DynamicUser=</varname> is enabled,
- <varname>RemoveIPC=</varname>, <varname>PrivateTmp=</varname> are implied. This ensures that the
- lifetime of IPC objects and temporary files created by the executed processes is bound to the runtime
- of the service, and hence the lifetime of the dynamic user/group. Since <filename>/tmp</filename> and
- <filename>/var/tmp</filename> are usually the only world-writable directories on a system this
- ensures that a unit making use of dynamic user/group allocation cannot leave files around after unit
- termination. Furthermore <varname>NoNewPrivileges=</varname> and <varname>RestrictSUIDSGID=</varname>
- are implicitly enabled to ensure that processes invoked cannot take benefit or create SUID/SGID files
- or directories. Moreover <varname>ProtectSystem=strict</varname> and
+ <varname>RemoveIPC=</varname> and <varname>PrivateTmp=</varname> are implied (and cannot be turned
+ off). This ensures that the lifetime of IPC objects and temporary files created by the executed
+ processes is bound to the runtime of the service, and hence the lifetime of the dynamic
+ user/group. Since <filename>/tmp/</filename> and <filename>/var/tmp/</filename> are usually the only
+ world-writable directories on a system this ensures that a unit making use of dynamic user/group
+ allocation cannot leave files around after unit termination. Furthermore
+ <varname>NoNewPrivileges=</varname> and <varname>RestrictSUIDSGID=</varname> are implicitly enabled
+ (and cannot be disabled), to ensure that processes invoked cannot take benefit or create SUID/SGID
+ files or directories. Moreover <varname>ProtectSystem=strict</varname> and
<varname>ProtectHome=read-only</varname> are implied, thus prohibiting the service to write to
arbitrary file system locations. In order to allow the service to write to certain directories, they
have to be whitelisted using <varname>ReadWritePaths=</varname>, but care must be taken so that
details.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>NUMAPolicy=</varname></term>
+
+ <listitem><para>Controls the NUMA memory policy of the executed processes. Takes a policy type, one of:
+ <option>default</option>, <option>preferred</option>, <option>bind</option>, <option>interleave</option> and
+ <option>local</option>. A list of NUMA nodes that should be associated with the policy must be specified
+ in <varname>NUMAMask=</varname>. For more details on each policy please see,
+ <citerefentry><refentrytitle>set_mempolicy</refentrytitle><manvolnum>2</manvolnum></citerefentry>. For overall
+ overview of NUMA support in Linux see,
+ <citerefentry><refentrytitle>numa</refentrytitle><manvolnum>7</manvolnum></citerefentry>
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>NUMAMask=</varname></term>
+
+ <listitem><para>Controls the NUMA node list which will be applied alongside with selected NUMA policy.
+ Takes a list of NUMA nodes and has the same syntax as a list of CPUs for <varname>CPUAffinity=</varname>
+ option. Note that the list of NUMA nodes is not required for <option>default</option> and <option>local</option>
+ policies and for <option>preferred</option> policy we expect a single NUMA node.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>IOSchedulingClass=</varname></term>
configuration or lifetime guarantees, please consider using
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
+ <para>The directories defined by these options are always created under the standard paths used by systemd
+ (<filename>/var</filename>, <filename>/run</filename>, <filename>/etc</filename>, …). If the service needs
+ directories in a different location, a different mechanism has to be used to create them.</para>
+
+ <para><citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry> provides
+ functionality that overlaps with these options. Using these options is recommended, because the lifetime of
+ the directories is tied directly to the lifetime of the unit, and it is not necessary to ensure that the
+ <filename>tmpfiles.d</filename> configuration is executed before the unit is started.</para>
+
+ <para>To remove any of the directories created by these settings, use the <command>systemctl clean
+ …</command> command on the relevant units, see
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ details.</para>
+
<para>Example: if a system service unit has the following,
<programlisting>RuntimeDirectory=foo/bar baz</programlisting>
the service manager creates <filename>/run/foo</filename> (if it does not exist),
<varlistentry>
<term><varname>SystemCallFilter=</varname></term>
- <listitem><para>Takes a space-separated list of system call names. If this setting is used, all system calls
- executed by the unit processes except for the listed ones will result in immediate process termination with the
- <constant>SIGSYS</constant> signal (whitelisting). If the first character of the list is <literal>~</literal>,
- the effect is inverted: only the listed system calls will result in immediate process termination
- (blacklisting). Blacklisted system calls and system call groups may optionally be suffixed with a colon
- (<literal>:</literal>) and <literal>errno</literal> error number (between 0 and 4095) or errno name such as
- <constant>EPERM</constant>, <constant>EACCES</constant> or <constant>EUCLEAN</constant>. This value will be
- returned when a blacklisted system call is triggered, instead of terminating the processes immediately. This
- value takes precedence over the one given in <varname>SystemCallErrorNumber=</varname>. If running in user
- mode, or in system mode, but without the <constant>CAP_SYS_ADMIN</constant> capability (e.g. setting
- <varname>User=nobody</varname>), <varname>NoNewPrivileges=yes</varname> is implied. This feature makes use of
- the Secure Computing Mode 2 interfaces of the kernel ('seccomp filtering') and is useful for enforcing a
- minimal sandboxing environment. Note that the <function>execve</function>, <function>exit</function>,
- <function>exit_group</function>, <function>getrlimit</function>, <function>rt_sigreturn</function>,
- <function>sigreturn</function> system calls and the system calls for querying time and sleeping are implicitly
- whitelisted and do not need to be listed explicitly. This option may be specified more than once, in which case
- the filter masks are merged. If the empty string is assigned, the filter is reset, all prior assignments will
- have no effect. This does not affect commands prefixed with <literal>+</literal>.</para>
+ <listitem><para>Takes a space-separated list of system call names. If this setting is used, all
+ system calls executed by the unit processes except for the listed ones will result in immediate
+ process termination with the <constant>SIGSYS</constant> signal (whitelisting). (See
+ <varname>SystemCallErrorNumber=</varname> below for changing the default action). If the first
+ character of the list is <literal>~</literal>, the effect is inverted: only the listed system calls
+ will result in immediate process termination (blacklisting). Blacklisted system calls and system call
+ groups may optionally be suffixed with a colon (<literal>:</literal>) and <literal>errno</literal>
+ error number (between 0 and 4095) or errno name such as <constant>EPERM</constant>,
+ <constant>EACCES</constant> or <constant>EUCLEAN</constant> (see <citerefentry
+ project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry> for a
+ full list). This value will be returned when a blacklisted system call is triggered, instead of
+ terminating the processes immediately. This value takes precedence over the one given in
+ <varname>SystemCallErrorNumber=</varname>, see below. If running in user mode, or in system mode,
+ but without the <constant>CAP_SYS_ADMIN</constant> capability (e.g. setting
+ <varname>User=nobody</varname>), <varname>NoNewPrivileges=yes</varname> is implied. This feature
+ makes use of the Secure Computing Mode 2 interfaces of the kernel ('seccomp filtering') and is useful
+ for enforcing a minimal sandboxing environment. Note that the <function>execve</function>,
+ <function>exit</function>, <function>exit_group</function>, <function>getrlimit</function>,
+ <function>rt_sigreturn</function>, <function>sigreturn</function> system calls and the system calls
+ for querying time and sleeping are implicitly whitelisted and do not need to be listed
+ explicitly. This option may be specified more than once, in which case the filter masks are
+ merged. If the empty string is assigned, the filter is reset, all prior assignments will have no
+ effect. This does not affect commands prefixed with <literal>+</literal>.</para>
<para>Note that on systems supporting multiple ABIs (such as x86/x86-64) it is recommended to turn off
alternative ABIs for services, so that they cannot be used to circumvent the restrictions of this
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM</programlisting>
+ <para>Note that various kernel system calls are defined redundantly: there are multiple system calls
+ for executing the same operation. For example, the <function>pidfd_send_signal()</function> system
+ call may be used to execute operations similar to what can be done with the older
+ <function>kill()</function> system call, hence blocking the latter without the former only provides
+ weak protection. Since new system calls are added regularly to the kernel as development progresses,
+ keeping system call blacklists comprehensive requires constant work. It is thus recommended to use
+ whitelisting instead, which offers the benefit that new system calls are by default implicitly
+ blocked until the whitelist is updated.</para>
+
+ <para>Also note that a number of system calls are required to be accessible for the dynamic linker to
+ work. The dynamic linker is required for running most regular programs (specifically: all dynamic ELF
+ binaries, which is how most distributions build packaged programs). This means that blocking these
+ system calls (which include <function>open()</function>, <function>openat()</function> or
+ <function>mmap()</function>) will make most programs typically shipped with generic distributions
+ unusable.</para>
+
<para>It is recommended to combine the file system namespacing related options with
<varname>SystemCallFilter=~@mount</varname>, in order to prohibit the unit's processes to undo the
mappings. Specifically these are the options <varname>PrivateTmp=</varname>,
<varlistentry>
<term><varname>SystemCallErrorNumber=</varname></term>
- <listitem><para>Takes an <literal>errno</literal> error number (between 1 and 4095) or errno name such as
- <constant>EPERM</constant>, <constant>EACCES</constant> or <constant>EUCLEAN</constant>, to return when the
- system call filter configured with <varname>SystemCallFilter=</varname> is triggered, instead of terminating
- the process immediately. When this setting is not used, or when the empty string is assigned, the process will
- be terminated immediately when the filter is triggered.</para></listitem>
+ <listitem><para>Takes an <literal>errno</literal> error number (between 1 and 4095) or errno name
+ such as <constant>EPERM</constant>, <constant>EACCES</constant> or <constant>EUCLEAN</constant>, to
+ return when the system call filter configured with <varname>SystemCallFilter=</varname> is triggered,
+ instead of terminating the process immediately. See <citerefentry
+ project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry> for a
+ full list of error codes. When this setting is not used, or when the empty string is assigned, the
+ process will be terminated immediately when the filter is triggered.</para></listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>StandardOutput=</varname></term>
- <listitem><para>Controls where file descriptor 1 (STDOUT) of the executed processes is connected to. Takes one
- of <option>inherit</option>, <option>null</option>, <option>tty</option>, <option>journal</option>,
- <option>syslog</option>, <option>kmsg</option>, <option>journal+console</option>,
- <option>syslog+console</option>, <option>kmsg+console</option>,
- <option>file:<replaceable>path</replaceable></option>, <option>append:<replaceable>path</replaceable></option>,
- <option>socket</option> or <option>fd:<replaceable>name</replaceable></option>.</para>
+ <listitem><para>Controls where file descriptor 1 (STDOUT) of the executed processes is connected
+ to. Takes one of <option>inherit</option>, <option>null</option>, <option>tty</option>,
+ <option>journal</option>, <option>kmsg</option>, <option>journal+console</option>,
+ <option>kmsg+console</option>, <option>file:<replaceable>path</replaceable></option>,
+ <option>append:<replaceable>path</replaceable></option>, <option>socket</option> or
+ <option>fd:<replaceable>name</replaceable></option>.</para>
<para><option>inherit</option> duplicates the file descriptor of standard input for standard output.</para>
see below). If the TTY is used for output only, the executed process will not become the controlling process of
the terminal, and will not fail or wait for other processes to release the terminal.</para>
- <para><option>journal</option> connects standard output with the journal which is accessible via
- <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. Note that
- everything that is written to syslog or kmsg (see below) is implicitly stored in the journal as well, the
- specific two options listed below are hence supersets of this one.</para>
-
- <para><option>syslog</option> connects standard output to the <citerefentry
- project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> system syslog
- service, in addition to the journal. Note that the journal daemon is usually configured to forward everything
- it receives to syslog anyway, in which case this option is no different from <option>journal</option>.</para>
+ <para><option>journal</option> connects standard output with the journal, which is accessible via
+ <citerefentry><refentrytitle>journalctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>. Note
+ that everything that is written to kmsg (see below) is implicitly stored in the journal as well, the
+ specific option listed below is hence a superset of this one. (Also note that any external,
+ additional syslog daemons receive their log data from the journal, too, hence this is the option to
+ use when logging shall be processed with such a daemon.)</para>
<para><option>kmsg</option> connects standard output with the kernel log buffer which is accessible via
<citerefentry project='man-pages'><refentrytitle>dmesg</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
in addition to the journal. The journal daemon might be configured to send all logs to kmsg anyway, in which
case this option is no different from <option>journal</option>.</para>
- <para><option>journal+console</option>, <option>syslog+console</option> and <option>kmsg+console</option> work
- in a similar way as the three options above but copy the output to the system console as well.</para>
+ <para><option>journal+console</option> and <option>kmsg+console</option> work in a similar way as the
+ two options above but copy the output to the system console as well.</para>
<para>The <option>file:<replaceable>path</replaceable></option> option may be used to connect a specific file
system object to standard output. The semantics are similar to the same option of
<citerefentry><refentrytitle>systemd.socket</refentrytitle><manvolnum>5</manvolnum></citerefentry> for more
details about named descriptors and their ordering.</para>
- <para>If the standard output (or error output, see below) of a unit is connected to the journal, syslog or the
- kernel log buffer, the unit will implicitly gain a dependency of type <varname>After=</varname> on
- <filename>systemd-journald.socket</filename> (also see the "Implicit Dependencies" section above). Also note
- that in this case stdout (or stderr, see below) will be an <constant>AF_UNIX</constant> stream socket, and not
- a pipe or FIFO that can be re-opened. This means when executing shell scripts the construct <command>echo
- "hello" > /dev/stderr</command> for writing text to stderr will not work. To mitigate this use the construct
- <command>echo "hello" >&2</command> instead, which is mostly equivalent and avoids this pitfall.</para>
+ <para>If the standard output (or error output, see below) of a unit is connected to the journal or
+ the kernel log buffer, the unit will implicitly gain a dependency of type <varname>After=</varname>
+ on <filename>systemd-journald.socket</filename> (also see the "Implicit Dependencies" section
+ above). Also note that in this case stdout (or stderr, see below) will be an
+ <constant>AF_UNIX</constant> stream socket, and not a pipe or FIFO that can be re-opened. This means
+ when executing shell scripts the construct <command>echo "hello" > /dev/stderr</command> for
+ writing text to stderr will not work. To mitigate this use the construct <command>echo "hello"
+ >&2</command> instead, which is mostly equivalent and avoids this pitfall.</para>
<para>This setting defaults to the value set with <varname>DefaultStandardOutput=</varname> in
<citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>, which
<varlistentry>
<term><varname>SyslogIdentifier=</varname></term>
- <listitem><para>Sets the process name ("<command>syslog</command> tag") to prefix log lines sent to the logging
- system or the kernel log buffer with. If not set, defaults to the process name of the executed process. This
- option is only useful when <varname>StandardOutput=</varname> or <varname>StandardError=</varname> are set to
- <option>journal</option>, <option>syslog</option> or <option>kmsg</option> (or to the same settings in
- combination with <option>+console</option>) and only applies to log messages written to stdout or
- stderr.</para></listitem>
+ <listitem><para>Sets the process name ("<command>syslog</command> tag") to prefix log lines sent to
+ the logging system or the kernel log buffer with. If not set, defaults to the process name of the
+ executed process. This option is only useful when <varname>StandardOutput=</varname> or
+ <varname>StandardError=</varname> are set to <option>journal</option> or <option>kmsg</option> (or to
+ the same settings in combination with <option>+console</option>) and only applies to log messages
+ written to stdout or stderr.</para></listitem>
</varlistentry>
<varlistentry>
<option>auth</option>, <option>syslog</option>, <option>lpr</option>, <option>news</option>,
<option>uucp</option>, <option>cron</option>, <option>authpriv</option>, <option>ftp</option>,
<option>local0</option>, <option>local1</option>, <option>local2</option>, <option>local3</option>,
- <option>local4</option>, <option>local5</option>, <option>local6</option> or <option>local7</option>. See
- <citerefentry project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry>
- for details. This option is only useful when <varname>StandardOutput=</varname> or
- <varname>StandardError=</varname> are set to <option>journal</option>, <option>syslog</option> or
- <option>kmsg</option> (or to the same settings in combination with <option>+console</option>), and only applies
- to log messages written to stdout or stderr. Defaults to <option>daemon</option>.</para></listitem>
+ <option>local4</option>, <option>local5</option>, <option>local6</option> or
+ <option>local7</option>. See <citerefentry
+ project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
+ details. This option is only useful when <varname>StandardOutput=</varname> or
+ <varname>StandardError=</varname> are set to <option>journal</option> or <option>kmsg</option> (or to
+ the same settings in combination with <option>+console</option>), and only applies to log messages
+ written to stdout or stderr. Defaults to <option>daemon</option>.</para></listitem>
</varlistentry>
<varlistentry>
<option>debug</option>. See <citerefentry
project='man-pages'><refentrytitle>syslog</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
details. This option is only useful when <varname>StandardOutput=</varname> or
- <varname>StandardError=</varname> are set to <option>journal</option>, <option>syslog</option> or
+ <varname>StandardError=</varname> are set to <option>journal</option> or
<option>kmsg</option> (or to the same settings in combination with <option>+console</option>), and only applies
to log messages written to stdout or stderr. Note that individual lines output by executed processes may be
prefixed with a different log level which can be used to override the default log level specified here. The
<term><varname>SyslogLevelPrefix=</varname></term>
<listitem><para>Takes a boolean argument. If true and <varname>StandardOutput=</varname> or
- <varname>StandardError=</varname> are set to <option>journal</option>, <option>syslog</option> or
- <option>kmsg</option> (or to the same settings in combination with <option>+console</option>), log lines
- written by the executed process that are prefixed with a log level will be processed with this log level set
- but the prefix removed. If set to false, the interpretation of these prefixes is disabled and the logged lines
- are passed on as-is. This only applies to log messages written to stdout or stderr. For details about this
- prefixing see <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
+ <varname>StandardError=</varname> are set to <option>journal</option> or <option>kmsg</option> (or to
+ the same settings in combination with <option>+console</option>), log lines written by the executed
+ process that are prefixed with a log level will be processed with this log level set but the prefix
+ removed. If set to false, the interpretation of these prefixes is disabled and the logged lines are
+ passed on as-is. This only applies to log messages written to stdout or stderr. For details about
+ this prefixing see
+ <citerefentry><refentrytitle>sd-daemon</refentrytitle><manvolnum>3</manvolnum></citerefentry>.
Defaults to true.</para></listitem>
</varlistentry>
<entry><constant>EXIT_CONFIGURATION_DIRECTORY</constant></entry>
<entry>Failed to set up unit's configuration directory. See <varname>ConfigurationDirectory=</varname> above.</entry>
</row>
+ <row>
+ <entry>242</entry>
+ <entry><constant>EXIT_NUMA_POLICY</constant></entry>
+ <entry>Failed to set up unit's NUMA memory policy. See <varname>NUMAPolicy=</varname> and <varname>NUMAMask=</varname>above.</entry>
+ </row>
+
</tbody>
</tgroup>
</table>
<term><varname>Driver=</varname></term>
<listitem>
<para>A whitespace-separated list of shell-style globs matching the driver currently bound to the
- device, as exposed by the udev property <varname>DRIVER</varname> of its parent device, or if that
+ device, as exposed by the udev property <varname>ID_NET_DRIVER</varname> of its parent device, or if that
is not set, the driver as exposed by <command>ethtool -i</command> of the device itself.</para>
</listitem>
</varlistentry>
property <varname>DEVTYPE</varname>.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>Property=</varname></term>
+ <listitem>
+ <para>A whitespace-separated list of udev property name with its value after a equal
+ (<literal>=</literal>). If multiple properties are specified, the test results are ANDed.
+ If the list is prefixed with a "!", the test is inverted. If a value contains white
+ spaces, then please quote whole key and value pair. If a value contains quotation, then
+ please escape the quotation with <literal>\</literal>.</para>
+
+ <para>Example: if a .link file has the following:
+ <programlisting>Property=ID_MODEL_ID=9999 "ID_VENDOR_FROM_DATABASE=vendor name" "KEY=with \"quotation\""</programlisting>
+ then, the .link file matches only when an interface has all the above three properties.
+ </para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><varname>Host=</varname></term>
<listitem>
this unit type. See
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the common options of all unit configuration files. The common
- configuration items are configured in the generic [Unit] and
- [Install] sections. The mount specific configuration options are
- configured in the [Mount] section.</para>
+ configuration items are configured in the generic <literal>[Unit]</literal> and
+ <literal>[Install]</literal> sections. The mount specific configuration options are
+ configured in the <literal>[Mount]</literal> section.</para>
<para>Additional options are listed in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<refsect1>
<title>Description</title>
- <para>Network interfaces may be renamed to give them predictable names when there's enough information to
- generate appropriate names and the use of certain types of names is configured. This page describes the
- first part, i.e. what possible names may be generated. Those names are generated by the
+ <para>Network interfaces names and MAC addresses may be generated based on certain stable interface
+ attributes. This is possible when there is enough information about the device to generate those
+ attributes and the use of this information is configured. This page describes interface naming, i.e. what
+ possible names may be generated. Those names are generated by the
<citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
builtin <command>net_id</command> and exported as udev properties
(<varname>ID_NET_NAME_ONBOARD=</varname>, <varname>ID_NET_LABEL_ONBOARD=</varname>,
<varname>ID_NET_NAME_PATH=</varname>, <varname>ID_NET_NAME_SLOT=</varname>).</para>
- <para>Names are derived from various device metadata attributes. Newer versions of udev take more of
- these attributes into account, improving (and thus possibly changing) the names used for the same
- devices. Differents version of the naming rules are called "naming schemes". The default naming scheme is
- chosen at compilation time. Usually this will be the latest implemented version, but it is also possible
- to set one of the older versions to preserve compatibility. This may be useful for example for
- distributions, which may introduce new versions of systemd in stable releases without changing the naming
- scheme. The naming scheme may also be overriden using the <varname>net.naming-scheme=</varname> kernel
- command line switch, see
+ <para>Names and MAC addresses are derived from various stable device metadata attributes. Newer versions
+ of udev take more of these attributes into account, improving (and thus possibly changing) the names and
+ addresses used for the same devices. Different versions of those generation rules are called "naming
+ schemes". The default naming scheme is chosen at compilation time. Usually this will be the latest
+ implemented version, but it is also possible to set one of the older versions to preserve
+ compatibility. This may be useful for example for distributions, which may introduce new versions of
+ systemd in stable releases without changing the naming scheme. The naming scheme may also be overridden
+ using the <varname>net.naming-scheme=</varname> kernel command line switch, see
<citerefentry><refentrytitle>systemd-udevd.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.
Available naming schemes are described below.</para>
<para>After the udev proprties have been generated, appropriate udev rules may be used to actually rename
- devices based on those properties. See the description of <varname>NamePolicy=</varname> in
- <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
- </para>
+ devices based on those properties. See the description of <varname>NamePolicy=</varname> and
+ <varname>MACAddressPolicy=</varname> in
+ <citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
</refsect1>
<refsect1>
<varlistentry>
<term><constant>v240</constant></term>
- <para>The <literal>ib</literal> prefix and stable names for infiniband devices are
+ <listitem><para>The <literal>ib</literal> prefix and stable names for infiniband devices are
introduced. Previously those devices were not renamed.</para>
<para>The ACPI index field (used in <varname>ID_NET_NAME_ONBOARD=</varname>) is now also used when
configuration, even if they have been renamed already, if <constant>keep</constant> is not
specified as the naming policy in the <filename noindex='true'>.link</filename> file. See
<citerefentry><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>
- for a description of <varname>NamePolicy=</varname>.</para>
+ for a description of <varname>NamePolicy=</varname>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><constant>v241</constant></term>
+
+ <listitem><para><option>MACAddressPolicy=persistent</option> was extended to set MAC addresses
+ based on the device name. Previously addresses were only based on the
+ <varname noindex='true'>ID_NET_NAME_*</varname> attributes, which meant that interface names would
+ never be generated for virtual devices. Now a persistent address will be generated for most
+ devices, including in particular bridges.</para>
+
+ <para>Note: when userspace does not set a MAC address for a bridge device, the kernel will
+ initially assign a random address, and then change it when the first device is enslaved to the
+ bridge. With this naming policy change, bridges get a persistent MAC address based on the bridge
+ name instead of the first enslaved device.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>v243</constant></term>
- <para>Support for netdevsim (simulated networking devices) was added. Previously those devices were
- not renamed.</para>
+ <listitem><para>Support for renaming netdevsim (simulated networking) devices was added. Previously
+ those devices were not renamed.</para>
<para>Previously two-letter interface type prefix was prepended to
- <varname>ID_NET_LABEL_ONBOARD=</varname>. This is not done anymore.</para>
+ <varname>ID_NET_LABEL_ONBOARD=</varname>. This is not done anymore.</para></listitem>
</varlistentry>
-
- <para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
- particular version of systemd.</para>
</variablelist>
+
+ <para>Note that <constant>latest</constant> may be used to denote the latest scheme known (to this
+ particular version of systemd.</para>
</refsect1>
<refsect1>
<row><entry><varname>fou</varname></entry>
<entry>Foo-over-UDP tunneling.</entry></row>
+ <row><entry><varname>xfrm</varname></entry>
+ <entry>A virtual tunnel interface like vti/vti6 but with several advantages.</entry></row>
+
</tbody>
</tgroup>
</table>
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>AssignToLoopback=</varname></term>
+ <listitem>
+ <para>Takes a boolean. If set to <literal>yes</literal>, the loopback interface <literal>lo</literal>
+ is used as the underlying device of the tunnel interface. Defaults to <literal>no</literal>.</para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><varname>AllowLocalRemote=</varname></term>
<listitem>
Linux Ethernet Bonding Driver HOWTO</ulink></para>
</refsect1>
+ <refsect1>
+ <title>[Xfrm] Section Options</title>
+
+ <para>The <literal>[Xfrm]</literal> section accepts the following
+ keys:</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>InterfaceId=</varname></term>
+ <listitem>
+ <para>Sets the ID/key of the xfrm interface which needs to be associated with a SA/policy.
+ Can be decimal or hexadecimal, valid range is 0-0xffffffff, defaults to 0.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>Independent=</varname></term>
+ <listitem>
+ <para>Takes a boolean. If set to <literal>no</literal>, the xfrm interface should have an
+ underlying device which can be used for hardware offloading. Defaults to <literal>no</literal>.
+ See <citerefentry><refentrytitle>systemd.network</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for how to configure the underlying device.</para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <para>For more detail information see
+ <ulink url="https://lwn.net/Articles/757391">
+ Virtual xfrm interfaces</ulink></para>
+ </refsect1>
+
<refsect1>
<title>[VRF] Section Options</title>
<para>The <literal>[VRF]</literal> section only applies for
AllowedIPs=fd31:bf08:57cb::/48,192.168.26.0/24
Endpoint=wireguard.example.com:51820</programlisting>
</example>
+
+ <example>
+ <title>/etc/systemd/network/27-xfrm.netdev</title>
+ <programlisting>[Xfrm]
+Name=xfrm0
+Kind=xfrm
+
+[Xfrm]
+Independent=yes</programlisting>
+ </example>
</refsect1>
<refsect1>
<listitem>
<para>A whitespace-separated list of shell-style globs
matching the driver currently bound to the device, as
- exposed by the udev property <literal>DRIVER</literal>
+ exposed by the udev property <literal>ID_NET_DRIVER</literal>
of its parent device, or if that is not set the driver
as exposed by <literal>ethtool -i</literal> of the
device itself. If the list is prefixed with a "!", the
with a "!", the test is inverted.</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>Property=</varname></term>
+ <listitem>
+ <para>A whitespace-separated list of udev property name with its value after a equal
+ (<literal>=</literal>). If multiple properties are specified, the test results are ANDed.
+ If the list is prefixed with a "!", the test is inverted. If a value contains white
+ spaces, then please quote whole key and value pair. If a value contains quotation, then
+ please escape the quotation with <literal>\</literal>.</para>
+
+ <para>Example: if a .network file has the following:
+ <programlisting>Property=ID_MODEL_ID=9999 "ID_VENDOR_FROM_DATABASE=vendor name" "KEY=with \"quotation\""</programlisting>
+ then, the .network file matches only when an interface has all the above three properties.
+ </para>
+ </listitem>
+ </varlistentry>
<varlistentry>
<term><varname>Host=</varname></term>
<listitem>
specified through DHCP is not used for name resolution.
See option <option>UseDomains=</option> below.</para>
- <para>See the <literal>[DHCP]</literal> section below for further configuration options for the DHCP client
- support.</para>
+ <para>See the <literal>[DHCPv4]</literal> or <literal>[DHCPv6]</literal> section below for
+ further configuration options for the DHCP client support.</para>
</listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><varname>DNSOverTLS=</varname></term>
<listitem>
- <para>Takes false or
- <literal>opportunistic</literal>. When set to <literal>opportunistic</literal>, enables
+ <para>Takes a boolean or <literal>opportunistic</literal>.
+ When true, enables
<ulink
url="https://tools.ietf.org/html/rfc7858">DNS-over-TLS</ulink>
- support on the link. This option defines a
- per-interface setting for
+ support on the link.
+ When set to <literal>opportunistic</literal>, compatibility with
+ non-DNS-over-TLS servers is increased, by automatically
+ turning off DNS-over-TLS servers in this case.
+ This option defines a per-interface setting for
<citerefentry><refentrytitle>resolved.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>'s
global <varname>DNSOverTLS=</varname> option. Defaults to
false. This setting is read by
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>Xfrm=</varname></term>
+ <listitem>
+ <para>The name of the xfrm to create on the link. See
+ <citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ This option may be specified more than once.</para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><varname>KeepConfiguration=</varname></term>
+ <listitem>
+ <para>Takes a boolean or one of <literal>static</literal>, <literal>dhcp-on-stop</literal>,
+ <literal>dhcp</literal>. When <literal>static</literal>, <command>systemd-networkd</command>
+ will not drop static addresses and routes on starting up process. When set to
+ <literal>dhcp-on-stop</literal>, <command>systemd-networkd</command> will not drop addresses
+ and routes on stopping the daemon. When <literal>dhcp</literal>,
+ the addresses and routes provided by a DHCP server will never be dropped even if the DHCP
+ lease expires. This is contrary to the DHCP specification, but may be the best choice if,
+ e.g., the root filesystem relies on this connection. The setting <literal>dhcp</literal>
+ implies <literal>dhcp-on-stop</literal>, and <literal>yes</literal> implies
+ <literal>dhcp</literal> and <literal>static</literal>. Defaults to
+ <literal>dhcp-on-stop</literal>.</para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
<refsect1>
- <title>[DHCP] Section Options</title>
- <para>The <literal>[DHCP]</literal> section configures the
- DHCPv4 and DHCP6 client, if it is enabled with the
+ <title>[DHCPv4] Section Options</title>
+ <para>The <literal>[DHCPv4]</literal> section configures the
+ DHCPv4 client, if it is enabled with the
<varname>DHCP=</varname> setting described above:</para>
<variablelist class='network-directives'>
system. Defaults to <literal>no</literal>.</para></listitem>
</varlistentry>
- <varlistentry>
- <term><varname>CriticalConnection=</varname></term>
- <listitem>
- <para>When true, the connection will never be torn down
- even if the DHCP lease expires. This is contrary to the
- DHCP specification, but may be the best choice if, say,
- the root filesystem relies on this connection. Defaults to
- false.</para>
- </listitem>
- </varlistentry>
-
<varlistentry>
<term><varname>ClientIdentifier=</varname></term>
<listitem>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>BlackList=</varname></term>
+ <listitem>
+ <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected.</para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+
+ <refsect1>
+ <title>[DHCPv6] Section Options</title>
+ <para>The <literal>[DHCPv6]</literal> section configures the DHCPv6 client, if it is enabled with the
+ <varname>DHCP=</varname> setting described above, or invoked by the IPv6 Router Advertisement:</para>
+
+ <variablelist class='network-directives'>
+ <varlistentry>
+ <term><varname>UseDNS=</varname></term>
+ <term><varname>UseNTP=</varname></term>
+ <listitem>
+ <para>As in the <literal>[DHCP]</literal> section.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>RapidCommit=</varname></term>
<listitem>
</listitem>
</varlistentry>
- <varlistentry>
- <term><varname>BlackList=</varname></term>
- <listitem>
- <para>A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected.</para>
- </listitem>
- </varlistentry>
-
</variablelist>
- </refsect1>
+ </refsect1>
<refsect1>
<title>[IPv6AcceptRA] Section Options</title>
MACVTAP=macvtap-test
</programlisting>
</example>
+
+ <example>
+ <title>A Xfrm interface with physical underlying device.</title>
+
+ <programlisting># /etc/systemd/network/27-xfrm.netdev
+[NetDev]
+Name=xfrm0
+
+[Xfrm]
+InterfaceId=7</programlisting>
+
+ <programlisting># /etc/systemd/network/27-eth0.network
+[Match]
+Name=eth0
+
+[Network]
+Xfrm=xfrm0</programlisting>
+
+ <para>This creates a <literal>xfrm0</literal> interface and binds it to the <literal>eth0</literal> device.
+ This allows hardware based ipsec offloading to the <literal>eth0</literal> nic.
+ If offloading is not needed, xfrm interfaces can be assigned to the <literal>lo</literal> device.
+ </para>
+ </example>
</refsect1>
<refsect1>
this unit type. See
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the common options of all unit configuration files. The common
- configuration items are configured in the generic [Unit] and
- [Install] sections. The path specific configuration options are
- configured in the [Path] section.</para>
+ configuration items are configured in the generic <literal>[Unit]</literal> and
+ <literal>[Install]</literal> sections. The path specific configuration options are
+ configured in the <literal>[Path]</literal> section.</para>
<para>For each path file, a matching unit file must exist,
describing the unit to activate when the path changes. By default,
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>IPIngressFilterPath=<replaceable>BPF_FS_PROGRAMM_PATH</replaceable></varname></term>
+ <term><varname>IPEgressFilterPath=<replaceable>BPF_FS_PROGRAMM_PATH</replaceable></varname></term>
+
+ <listitem>
+ <para>Add custom network traffic filters implemented as BPF programs, applying to all IP packets
+ sent and received over <constant>AF_INET</constant> and <constant>AF_INET6</constant> sockets.
+ Takes an absolute path to a pinned BPF program in the BPF virtual filesystem (<filename>/sys/fs/bpf/</filename>).
+ </para>
+
+ <para>The filters configured with this option are applied to all sockets created by processes
+ of this unit (or in the case of socket units, associated with it). The filters are loaded in addition
+ to filters any of the parent slice units this unit might be a member of as well as any
+ <varname>IPAddressAllow=</varname> and <varname>IPAddressDeny=</varname> filters in any of these units.
+ By default there are no filters specified.</para>
+
+ <para>If these settings are used multiple times in the same unit all the specified programs are attached. If an
+ empty string is assigned to these settings the program list is reset and all previous specified programs ignored.</para>
+
+ <para>Note that for socket-activated services, the IP filter programs configured on the socket unit apply to
+ all sockets associated with it directly, but not to any sockets created by the ultimately activated services
+ for it. Conversely, the IP filter programs configured for the service are not applied to any sockets passed into
+ the service via socket activation. Thus, it is usually a good idea, to replicate the IP filter programs on both
+ the socket and the service unit, however it often makes sense to maintain one configuration more open and the other
+ one more restricted, depending on the usecase.</para>
+
+ <para>Note that these settings might not be supported on some systems (for example if eBPF control group
+ support is not enabled in the underlying kernel or container manager). These settings will fail the service in
+ that case. If compatibility with such systems is desired it is hence recommended to attach your filter manually
+ (requires <varname>Delegate=</varname><constant>yes</constant>) instead of using this setting.</para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>DeviceAllow=</varname></term>
</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>TimeoutCleanSec=</varname></term>
+ <listitem><para>Configures a timeout on the clean-up operation requested through <command>systemctl
+ clean …</command>, see
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ details. Takes the usual time values and defaults to <constant>infinity</constant>, i.e. by default
+ no time-out is applied. If a time-out is configured the clean operation will be aborted forcibly when
+ the time-out is reached, potentially leaving resources on disk.</para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>RuntimeMaxSec=</varname></term>
inverse of the <varname>Sockets=</varname> setting of the
<filename>.service</filename> it refers to.</para>
- <para>This option may appear more than once, in which case the
- list of socket units is merged. If the empty string is
- assigned to this option, the list of sockets is reset, and all
- prior uses of this setting will have no
- effect.</para></listitem>
+ <para>This option may appear more than once, in which case the list of socket units is merged. Note
+ that once set, clearing the list of sockets again (for example, by assigning the empty string to this
+ option) is not supported.</para></listitem>
</varlistentry>
<varlistentry>
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the common options of all unit configuration
files. The common configuration items are configured
- in the generic [Unit] and [Install] sections. The
+ in the generic <literal>[Unit]</literal> and <literal>[Install]</literal> sections. The
slice specific configuration options are configured in
- the [Slice] section. Currently, only generic resource control settings
+ the <literal>[Slice]</literal> section. Currently, only generic resource control settings
as described in
<citerefentry><refentrytitle>systemd.resource-control</refentrytitle><manvolnum>5</manvolnum></citerefentry> are allowed.
</para>
this unit type. See
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the common options of all unit configuration files. The common
- configuration items are configured in the generic [Unit] and
- [Install] sections. The socket specific configuration options are
- configured in the [Socket] section.</para>
+ configuration items are configured in the generic <literal>[Unit]</literal> and
+ <literal>[Install]</literal> sections. The socket specific configuration options are
+ configured in the <literal>[Socket]</literal> section.</para>
<para>Additional options are listed in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
this unit type. See
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the common options of all unit configuration files. The common
- configuration items are configured in the generic [Unit] and
- [Install] sections. The swap specific configuration options are
- configured in the [Swap] section.</para>
+ configuration items are configured in the generic <literal>[Unit]</literal> and
+ <literal>[Install]</literal> sections. The swap specific configuration options are
+ configured in the <literal>[Swap]</literal> section.</para>
<para>Additional options are listed in
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<para>This unit type has no specific options. See
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the common options of all unit configuration files. The common
- configuration items are configured in the generic [Unit] and
- [Install] sections. A separate [Target] section does not exist,
+ configuration items are configured in the generic <literal>[Unit]</literal> and
+ <literal>[Install]</literal> sections. A separate <literal>[Target]</literal> section does not exist,
since no target-specific options may be configured.</para>
<para>Target units do not offer any additional functionality on
this unit type. See
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the common options of all unit configuration files. The common
- configuration items are configured in the generic [Unit] and
- [Install] sections. The timer specific configuration options are
- configured in the [Timer] section.</para>
+ configuration items are configured in the generic <literal>[Unit]</literal> and
+ <literal>[Install]</literal> sections. The timer specific configuration options are
+ configured in the <literal>[Timer]</literal> section.</para>
<para>For each timer file, a matching unit file must exist,
describing the unit to activate when the timer elapses. By
<varlistentry>
<term><varname>Persistent=</varname></term>
- <listitem><para>Takes a boolean argument. If true, the time
- when the service unit was last triggered is stored on disk.
- When the timer is activated, the service unit is triggered
- immediately if it would have been triggered at least once
- during the time when the timer was inactive. This is useful to
- catch up on missed runs of the service when the machine was
- off. Note that this setting only has an effect on timers
- configured with <varname>OnCalendar=</varname>. Defaults
- to <varname>false</varname>.
- </para></listitem>
+ <listitem><para>Takes a boolean argument. If true, the time when the service unit was last triggered
+ is stored on disk. When the timer is activated, the service unit is triggered immediately if it
+ would have been triggered at least once during the time when the timer was inactive. This is useful
+ to catch up on missed runs of the service when the system was powered down. Note that this setting
+ only has an effect on timers configured with <varname>OnCalendar=</varname>. Defaults to
+ <varname>false</varname>.</para>
+
+ <para>Use <command>systemctl clean --what=state …</command> on the timer unit to remove the timestamp
+ file maintained by this option from disk. In particular, use this command before uninstalling a timer
+ unit. See
+ <citerefentry><refentrytitle>systemctl</refentrytitle><manvolnum>1</manvolnum></citerefentry> for
+ details.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>DefaultDependencies=</varname></term>
<listitem><para>Takes a boolean argument. If
- <option>true</option>, (the default), a few default
+ <option>yes</option>, (the default), a few default
dependencies will implicitly be created for the unit. The
actual dependencies created depend on the unit type. For
example, for service units, these dependencies ensure that the
completed and is properly terminated on system shutdown. See
the respective man pages for details. Generally, only services
involved with early boot or late shutdown should set this
- option to <option>false</option>. It is highly recommended to
+ option to <option>no</option>. It is highly recommended to
leave this option enabled for the majority of common units. If
- set to <option>false</option>, this option does not disable
+ set to <option>no</option>, this option does not disable
all implicit dependencies, just non-essential
ones.</para></listitem>
</varlistentry>
<term><varname>ConditionMemory=</varname></term>
<term><varname>ConditionCPUs=</varname></term>
- <!-- We do not document ConditionNull=
- here, as it is not particularly
- useful and probably just
+ <!-- We do not document ConditionNull= here, as it is not particularly useful and probably just
confusing. -->
<listitem><para>Before starting a unit, verify that the specified condition is true. If it is not true, the
conditions are considered to be in a clean state and will be garbage collected if they are not referenced.
This means, that when queried, the condition failure may or may not show up in the state of the unit.</para>
+ <para>If multiple conditions are specified, the unit will be executed if all of them apply (i.e. a
+ logical AND is applied). Condition checks can be prefixed with a pipe symbol (<literal>|</literal>)
+ in which case a condition becomes a triggering condition. If at least one triggering condition is
+ defined for a unit, then the unit will be executed if at least one of the triggering conditions apply
+ and all of the non-triggering conditions. If you prefix an argument with the pipe symbol and an
+ exclamation mark, the pipe symbol must be passed first, the exclamation second. Except for
+ <varname>ConditionPathIsSymbolicLink=</varname>, all path checks follow symlinks. If any of these
+ options is assigned the empty string, the list of conditions is reset completely, all previous
+ condition settings (of any kind) will have no effect. The <command>condition</command> verb of
+ <citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ can be used to test condition and assert expressions.</para>
+
<para><varname>ConditionArchitecture=</varname> may be used to
check whether the system is running on a specific
architecture. Takes one of
<para><varname>ConditionKernelVersion=</varname> may be used to check whether the kernel version (as
reported by <command>uname -r</command>) matches a certain expression (or if prefixed with the
- exclamation mark does not match it). The argument must be a single string. If the string starts with
- one of <literal><</literal>, <literal><=</literal>, <literal>=</literal>,
- <literal>!=</literal>, <literal>>=</literal>, <literal>></literal> a relative version
- comparison is done, otherwise the specified string is matched with shell-style globs.</para>
+ exclamation mark does not match it). The argument must be a list of (potentially quoted) expressions.
+ For each of the expressions, if it starts with one of <literal><</literal>,
+ <literal><=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>>=</literal>,
+ <literal>></literal> a relative version comparison is done, otherwise the specified string is
+ matched with shell-style globs.</para>
<para>Note that using the kernel version string is an unreliable way to determine which features are supported
by a kernel, because of the widespread practice of backporting drivers, features, and fixes from newer upstream
comparison operator. On physical systems the number of CPUs in the affinity mask of the service
manager usually matches the number of physical CPUs, but in special and virtual environments might
differ. In particular, in containers the affinity mask usually matches the number of CPUs assigned to
- the container and not the physically available ones.</para>
-
- <para>If multiple conditions are specified, the unit will be
- executed if all of them apply (i.e. a logical AND is applied).
- Condition checks can be prefixed with a pipe symbol (|) in
- which case a condition becomes a triggering condition. If at
- least one triggering condition is defined for a unit, then the
- unit will be executed if at least one of the triggering
- conditions apply and all of the non-triggering conditions. If
- you prefix an argument with the pipe symbol and an exclamation
- mark, the pipe symbol must be passed first, the exclamation
- second. Except for
- <varname>ConditionPathIsSymbolicLink=</varname>, all path
- checks follow symlinks. If any of these options is assigned
- the empty string, the list of conditions is reset completely,
- all previous condition settings (of any kind) will have no
- effect.</para></listitem>
+ the container and not the physically available ones.</para></listitem>
</varlistentry>
<varlistentry>
<para>Note that neither assertion nor condition expressions result in unit state changes. Also note that both
are checked at the time the job is to be executed, i.e. long after depending jobs and it itself were
queued. Thus, neither condition nor assertion expressions are suitable for conditionalizing unit
- dependencies.</para></listitem>
+ dependencies.</para>
+
+ <para>The <command>condition</command> verb of
+ <citerefentry><refentrytitle>systemd-analyze</refentrytitle><manvolnum>1</manvolnum></citerefentry>
+ can be used to test condition and assert expressions.</para></listitem>
</varlistentry>
<varlistentry>
<varlistentry>
<term><option>--test</option></term>
- <listitem><para>Determine startup sequence, dump it and exit.
- This is an option useful for debugging only.</para></listitem>
+ <listitem><para>Determine the initial start-up transaction (i.e. the list of jobs enqueued at
+ start-up), dump it and exit — without actually executing any of the determined jobs. This option is
+ useful for debugging only. Note that during regular service manager start-up additional units not
+ shown by this operation may be started, because hardware, socket, bus or other kinds of activation
+ might add additional jobs as the transaction is executed. Use <option>--system</option> to request
+ the initial transaction of the system service manager (this is also the implied default), combine
+ with <option>--user</option> to request the initial transaction of the per-user service manager
+ instead.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--dump-configuration-items</option></term>
<varlistentry>
<term><option>--dump-bus-properties</option></term>
- <listitem><para>Dump exposed bus properties. This outputs
- a terse but complete list of properties exposed to dbus.
- </para></listitem>
+ <listitem><para>Dump exposed bus properties. This outputs a terse but complete list of properties
+ exposed on D-Bus.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--unit=</option></term>
not specified, defaults to
<filename>default.target</filename>.</para></listitem>
</varlistentry>
+
<varlistentry>
<term><option>--system</option></term>
<term><option>--user</option></term>
- <listitem><para>For <option>--system</option>, tell systemd to
- run a system instance, even if the process ID is not 1, i.e.
- systemd is not run as init process. <option>--user</option>
- does the opposite, running a user instance even if the process
- ID is 1. Normally, it should not be necessary to pass these
- options, as systemd automatically detects the mode it is
- started in. These options are hence of little use except for
- debugging. Note that it is not supported booting and
- maintaining a full system with systemd running in
- <option>--system</option> mode, but PID not 1. In practice,
- passing <option>--system</option> explicitly is only useful in
- conjunction with <option>--test</option>.</para></listitem>
+ <listitem><para>When used in conjunction with <option>--test</option>, selects whether to calculate
+ the initial transaction for the system instance or for a per-user instance. These options have no
+ effect when invoked without <option>--test</option>, as during regular
+ (i.e. non-<option>--test</option>) invocations the service manager will automatically detect whether
+ it shall operate in system or per-user mode, by checking whether the PID it is run as is 1 or
+ not. Note that it is not supported booting and maintaining a system with the service manager running
+ in <option>--system</option> mode but with a PID other than 1.</para></listitem>
</varlistentry>
+
<varlistentry>
<term><option>--dump-core</option></term>
<option>tty</option>,
<option>journal</option>,
<option>journal+console</option>,
- <option>syslog</option>,
- <option>syslog+console</option>,
<option>kmsg</option>,
<option>kmsg+console</option>. If the
argument is omitted
configuration file option.</para></listitem>
</varlistentry>
+ <varlistentry>
+ <term><varname>systemd.status_unit_format=</varname></term>
+
+ <listitem><para>Takes either <option>name</option> or <option>description</option> as the value. If
+ <option>name</option>, the system manager will use unit names in status messages. If specified,
+ overrides the system manager configuration file option <option>StatusUnitFormat=</option>, see
+ <citerefentry><refentrytitle>systemd-system.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
+ </para></listitem>
+ </varlistentry>
+
<varlistentry>
<term><varname>systemd.log_target=</varname></term>
<term><varname>systemd.log_level=</varname></term>
lines, when omitted or when set to <literal>-</literal>, the file ownership will not be modified. These
parameters are ignored for <varname>x</varname>, <varname>r</varname>, <varname>R</varname>,
<varname>L</varname>, <varname>t</varname>, and <varname>a</varname> lines.</para>
+
+ <para>This field should generally only reference system users/groups, i.e. users/groups that are
+ guaranteed to be resolvable during early boot. If this field references users/groups that only become
+ resolveable during later boot (i.e. after NIS, LDAP or a similar networked directory service become
+ available), execution of the operations declared by the line will likely fail. Also see <ulink
+ url="https://systemd.io/UIDS-GIDS.html#notes-on-resolvability-of-user-and-group-names">Notes on
+ Resolvability of User and Group Names</ulink> for more information on requirements on system user/group
+ definitions.</para>
</refsect2>
<refsect2>
<para>Type of event to be triggered. Possible actions are <literal>add</literal>,
<literal>remove</literal>, <literal>change</literal>, <literal>move</literal>,
<literal>online</literal>, <literal>offline</literal>, <literal>bind</literal>,
- and <literal>unbind</literal>. The default value is <literal>change</literal>.</para>
+ and <literal>unbind</literal>. Also, the special value <literal>help</literal> can be used
+ to list the possible actions. The default value is <literal>change</literal>.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
<variablelist>
<varlistentry>
<term><option>-a</option></term>
- <term><option>--action=<replaceable>string</replaceable></option></term>
+ <term><option>--action=<replaceable>ACTION</replaceable></option></term>
<listitem>
- <para>The action string.</para>
+ <para>The action string. The special value <literal>help</literal> may be used to list
+ known values.</para>
</listitem>
</varlistentry>
<varlistentry>
# This is to be used instead of meson.source_root(), as the latter will return
# the wrong result when systemd is being built as a meson subproject
project_source_root = meson.current_source_dir()
+relative_source_path = run_command('realpath',
+ '--relative-to=@0@'.format(meson.current_build_dir()),
+ project_source_root).stdout().strip()
+conf.set_quoted('RELATIVE_SOURCE_PATH', relative_source_path)
want_ossfuzz = get_option('oss-fuzz')
want_libfuzzer = get_option('llvm-fuzz')
polkitpolicydir = join_paths(datadir, 'polkit-1/actions')
polkitrulesdir = join_paths(datadir, 'polkit-1/rules.d')
polkitpkladir = join_paths(localstatedir, 'lib/polkit-1/localauthority/10-vendor.d')
-varlogdir = join_paths(localstatedir, 'log')
xinitrcdir = join_paths(sysconfdir, 'X11/xinit/xinitrc.d')
rpmmacrosdir = get_option('rpmmacrosdir')
if rpmmacrosdir != 'no'
endif
memory_accounting_default = get_option('memory-accounting-default')
+status_unit_format_default = get_option('status-unit-format-default')
conf.set_quoted('PKGSYSCONFDIR', pkgsysconfdir)
conf.set_quoted('SYSTEM_CONFIG_UNIT_PATH', join_paths(pkgsysconfdir, 'system'))
conf.set_quoted('DOCUMENT_ROOT', join_paths(pkgdatadir, 'gatewayd'))
conf.set10('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default)
conf.set_quoted('MEMORY_ACCOUNTING_DEFAULT_YES_NO', memory_accounting_default ? 'yes' : 'no')
+conf.set('STATUS_UNIT_FORMAT_DEFAULT', 'STATUS_UNIT_FORMAT_' + status_unit_format_default.to_upper())
substs.set('prefix', prefixdir)
substs.set('rootprefix', rootprefixdir)
substs.set('userenvgeneratordir', userenvgeneratordir)
substs.set('systemshutdowndir', systemshutdowndir)
substs.set('systemsleepdir', systemsleepdir)
-substs.set('VARLOGDIR', varlogdir)
substs.set('CERTIFICATEROOT', get_option('certificate-root'))
substs.set('SYSTEMCTL', join_paths(rootbindir, 'systemctl'))
substs.set('RANDOM_SEED', join_paths(randomseeddir, 'random-seed'))
substs.set('SYSTEM_SYSVRCND_PATH', sysvrcnd_path)
substs.set('RC_LOCAL_SCRIPT_PATH_START', get_option('rc-local'))
substs.set('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_default ? 'yes' : 'no')
+substs.set('STATUS_UNIT_FORMAT_DEFAULT', status_unit_format_default)
substs.set('HIGH_RLIMIT_NOFILE', conf.get('HIGH_RLIMIT_NOFILE'))
substs.set('BUILD_ROOT', meson.current_build_dir())
#include <unistd.h>'''],
['explicit_bzero' , '''#include <string.h>'''],
['reallocarray', '''#include <malloc.h>'''],
+ ['set_mempolicy', '''#include <stdlib.h>
+ #include <unistd.h>'''],
+ ['get_mempolicy', '''#include <stdlib.h>
+ #include <unistd.h>'''],
]
have = cc.has_function(ident[0], prefix : ident[1], args : '-D_GNU_SOURCE')
substs.set('SUSHELL', get_option('debug-shell'))
substs.set('DEBUGTTY', get_option('debug-tty'))
+conf.set_quoted('DEBUGTTY', get_option('debug-tty'))
enable_debug_hashmap = false
enable_debug_mmap_cache = false
enable_debug_siphash = false
-enable_debug_udev = false
foreach name : get_option('debug-extra')
if name == 'hashmap'
enable_debug_hashmap = true
enable_debug_mmap_cache = true
elif name == 'siphash'
enable_debug_siphash = true
- elif name == 'udev'
- enable_debug_udev = true
else
message('unknown debug option "@0@", ignoring'.format(name))
endif
conf.set10('ENABLE_DEBUG_HASHMAP', enable_debug_hashmap)
conf.set10('ENABLE_DEBUG_MMAP_CACHE', enable_debug_mmap_cache)
conf.set10('ENABLE_DEBUG_SIPHASH', enable_debug_siphash)
-conf.set10('ENABLE_DEBUG_UDEV', enable_debug_udev)
conf.set10('VALGRIND', get_option('valgrind'))
conf.set10('LOG_TRACE', get_option('log-trace'))
error('libidn and libidn2 cannot be requested simultaneously')
endif
-if want_libidn != 'false' and want_libidn2 != 'true' and not skip_deps
- libidn = dependency('libidn',
- required : want_libidn == 'true')
+if want_libidn2 != 'false' and want_libidn != 'true' and not skip_deps
+ libidn = dependency('libidn2',
+ required : want_libidn2 == 'true')
have = libidn.found()
else
have = false
libidn = []
endif
-conf.set10('HAVE_LIBIDN', have)
-if not have and want_libidn2 != 'false' and not skip_deps
+conf.set10('HAVE_LIBIDN2', have)
+if not have and want_libidn != 'false' and not skip_deps
# libidn is used for both libidn and libidn2 objects
- libidn = dependency('libidn2',
- required : want_libidn2 == 'true')
+ libidn = dependency('libidn',
+ required : want_libidn == 'true')
have = libidn.found()
else
have = false
endif
-conf.set10('HAVE_LIBIDN2', have)
+conf.set10('HAVE_LIBIDN', have)
want_libiptc = get_option('libiptc')
if want_libiptc != 'false' and not skip_deps
have_openssl = conf.get('HAVE_OPENSSL') == 1
if dns_over_tls != 'auto' and not have_openssl
str = dns_over_tls == 'openssl' ? ' with openssl' : ''
- error('DNS-over-TLS support was requested$0$, but dependencies are not available'.format(str))
+ error('DNS-over-TLS support was requested@0@, but dependencies are not available'.format(str))
endif
endif
have = have_gnutls or have_openssl
default_dns_over_tls = 'no'
endif
if default_dns_over_tls != 'no' and conf.get('ENABLE_DNS_OVER_TLS') == 0
- message('default-dns-over-tls cannot be set to opportunistic when DNS-over-TLS support is disabled. Setting default-dns-over-tls to no.')
+ message('default-dns-over-tls cannot be enabled or set to opportunistic when DNS-over-TLS support is disabled. Setting default-dns-over-tls to no.')
default_dns_over_tls = 'no'
endif
conf.set('DEFAULT_DNS_OVER_TLS_MODE',
meson_apply_m4 = find_program('tools/meson-apply-m4.sh')
includes = include_directories('src/basic',
+ 'src/boot',
'src/shared',
'src/systemd',
'src/journal',
['debug hashmap'],
['debug mmap cache'],
['debug siphash'],
- ['debug udev'],
['valgrind', conf.get('VALGRIND') == 1],
['trace logging', conf.get('LOG_TRACE') == 1],
['link-udev-shared', get_option('link-udev-shared')],
description : 'path to debug shell binary')
option('debug-tty', type : 'string', value : '/dev/tty9',
description : 'specify the tty device for debug shell')
-option('debug-extra', type : 'array', choices : ['hashmap', 'mmap-cache', 'siphash', 'udev'], value : [],
+option('debug-extra', type : 'array', choices : ['hashmap', 'mmap-cache', 'siphash'], value : [],
description : 'enable extra debugging')
option('memory-accounting-default', type : 'boolean',
description : 'enable MemoryAccounting= by default')
description : 'install the systemd-timesyncd daemon')
option('remote', type : 'combo', choices : ['auto', 'true', 'false'],
description : 'support for "journal over the network"')
+option('create-log-dirs', type : 'boolean',
+ description : 'create /var/log/journal{,/remote}')
option('nss-myhostname', type : 'boolean',
description : 'install nss-myhostname module')
option('nss-mymachines', type : 'combo', choices : ['auto', 'true', 'false'],
option('default-net-naming-scheme', type : 'combo',
choices : ['latest', 'v238', 'v239', 'v240'],
description : 'default net.naming-scheme= value')
+option('status-unit-format-default', type : 'combo',
+ choices : ['description', 'name'],
+ description : 'use unit name or description in messages by default')
option('time-epoch', type : 'integer', value : '-1',
description : 'time epoch for time clients')
option('system-uid-max', type : 'integer', value : '-1',
value : 'allow-downgrade')
option('default-dns-over-tls', type : 'combo',
description : 'default DNS-over-TLS mode',
- choices : ['opportunistic', 'no'],
+ choices : ['yes', 'opportunistic', 'no'],
value : 'no')
option('dns-over-tls', type : 'combo', choices : ['auto', 'gnutls', 'openssl', 'true', 'false'],
description : 'DNS-over-TLS support')
sed 's/^deb/deb-src/' /etc/apt/sources.list >> /etc/apt/sources.list.d/sources.list
# wait until online
while [ -z "\$(ip route list 0/0)" ]; do sleep 1; done
-apt-get -q update
+apt-get -q --allow-releaseinfo-change update
apt-get -y dist-upgrade
apt-get install -y eatmydata
EOF
[FAMILY]='tcp udp sctp'
[RESOLVE]='yes no resolve'
[DNSSEC]='yes no allow-downgrade'
- [DNSOVERTLS]='no opportunistic'
+ [DNSOVERTLS]='yes no opportunistic'
)
local interfaces=$( __get_interfaces )
)
local -A VERBS=(
- [STANDALONE]='time blame plot dump unit-paths calendar timespan'
+ [STANDALONE]='time blame plot dump unit-paths calendar timespan condition'
[CRITICAL_CHAIN]='critical-chain'
[DOT]='dot'
[LOG_LEVEL]='log-level'
comps="yes no allow-downgrade"
;;
--set-dnsovertls)
- comps="no opportunistic"
+ comps="yes no opportunistic"
;;
esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
comps='devices subsystems'
;;
-c|--action)
- comps='add change remove bind unbind'
+ comps=$( udevadm trigger --action help )
;;
-y|--sysname-match|-b|--parent-match)
comps=$( __get_all_sysdevs )
if __contains_word "$prev" ${OPTS[TEST]}; then
case $prev in
-a|--action)
- comps='add change remove bind unbind'
+ comps=$( udevadm test --action help )
;;
-N|--resolve-names)
comps='early late never'
"add-requires:Add Requires= dependencies to a unit"
"reenable:Reenable one or more unit files"
"preset:Enable/disable one or more unit files based on preset configuration"
+ "preset-all:Enable/disable all unit files based on preset configuration"
"set-default:Set the default target"
"get-default:Query the default target"
"edit:Edit one or more unit files"
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include <stdlib.h>
+
+#include "analyze-condition.h"
+#include "condition.h"
+#include "conf-parser.h"
+#include "load-fragment.h"
+#include "service.h"
+
+typedef struct condition_definition {
+ const char *name;
+ ConfigParserCallback parser;
+ ConditionType type;
+} condition_definition;
+
+static const condition_definition condition_definitions[] = {
+ { "ConditionPathExists", config_parse_unit_condition_path, CONDITION_PATH_EXISTS },
+ { "ConditionPathExistsGlob", config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB },
+ { "ConditionPathIsDirectory", config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY },
+ { "ConditionPathIsSymbolicLink", config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK },
+ { "ConditionPathIsMountPoint", config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT },
+ { "ConditionPathIsReadWrite", config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE },
+ { "ConditionDirectoryNotEmpty", config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY },
+ { "ConditionFileNotEmpty", config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY },
+ { "ConditionFileIsExecutable", config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE },
+ { "ConditionNeedsUpdate", config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE },
+ { "ConditionFirstBoot", config_parse_unit_condition_string, CONDITION_FIRST_BOOT },
+ { "ConditionKernelCommandLine", config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE },
+ { "ConditionKernelVersion", config_parse_unit_condition_string, CONDITION_KERNEL_VERSION },
+ { "ConditionArchitecture", config_parse_unit_condition_string, CONDITION_ARCHITECTURE },
+ { "ConditionVirtualization", config_parse_unit_condition_string, CONDITION_VIRTUALIZATION },
+ { "ConditionSecurity", config_parse_unit_condition_string, CONDITION_SECURITY },
+ { "ConditionCapability", config_parse_unit_condition_string, CONDITION_CAPABILITY },
+ { "ConditionHost", config_parse_unit_condition_string, CONDITION_HOST },
+ { "ConditionACPower", config_parse_unit_condition_string, CONDITION_AC_POWER },
+ { "ConditionUser", config_parse_unit_condition_string, CONDITION_USER },
+ { "ConditionGroup", config_parse_unit_condition_string, CONDITION_GROUP },
+ { "ConditionControlGroupController", config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER },
+
+ { "AssertPathExists", config_parse_unit_condition_path, CONDITION_PATH_EXISTS },
+ { "AssertPathExistsGlob", config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB },
+ { "AssertPathIsDirectory", config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY },
+ { "AssertPathIsSymbolicLink", config_parse_unit_condition_path, CONDITION_PATH_IS_SYMBOLIC_LINK },
+ { "AssertPathIsMountPoint", config_parse_unit_condition_path, CONDITION_PATH_IS_MOUNT_POINT },
+ { "AssertPathIsReadWrite", config_parse_unit_condition_path, CONDITION_PATH_IS_READ_WRITE },
+ { "AssertDirectoryNotEmpty", config_parse_unit_condition_path, CONDITION_DIRECTORY_NOT_EMPTY },
+ { "AssertFileNotEmpty", config_parse_unit_condition_path, CONDITION_FILE_NOT_EMPTY },
+ { "AssertFileIsExecutable", config_parse_unit_condition_path, CONDITION_FILE_IS_EXECUTABLE },
+ { "AssertNeedsUpdate", config_parse_unit_condition_path, CONDITION_NEEDS_UPDATE },
+ { "AssertFirstBoot", config_parse_unit_condition_string, CONDITION_FIRST_BOOT },
+ { "AssertKernelCommandLine", config_parse_unit_condition_string, CONDITION_KERNEL_COMMAND_LINE },
+ { "AssertKernelVersion", config_parse_unit_condition_string, CONDITION_KERNEL_VERSION },
+ { "AssertArchitecture", config_parse_unit_condition_string, CONDITION_ARCHITECTURE },
+ { "AssertVirtualization", config_parse_unit_condition_string, CONDITION_VIRTUALIZATION },
+ { "AssertSecurity", config_parse_unit_condition_string, CONDITION_SECURITY },
+ { "AssertCapability", config_parse_unit_condition_string, CONDITION_CAPABILITY },
+ { "AssertHost", config_parse_unit_condition_string, CONDITION_HOST },
+ { "AssertACPower", config_parse_unit_condition_string, CONDITION_AC_POWER },
+ { "AssertUser", config_parse_unit_condition_string, CONDITION_USER },
+ { "AssertGroup", config_parse_unit_condition_string, CONDITION_GROUP },
+ { "AssertControlGroupController", config_parse_unit_condition_string, CONDITION_CONTROL_GROUP_CONTROLLER },
+
+ /* deprecated, but we should still parse them */
+ { "ConditionNull", config_parse_unit_condition_null, 0 },
+ { "AssertNull", config_parse_unit_condition_null, 0 },
+};
+
+static int parse_condition(Unit *u, const char *line) {
+ const char *p;
+ Condition **target;
+
+ if ((p = startswith(line, "Condition")))
+ target = &u->conditions;
+ else if ((p = startswith(line, "Assert")))
+ target = &u->asserts;
+ else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot parse \"%s\".", line);
+
+ for (size_t i = 0; i < ELEMENTSOF(condition_definitions); i++) {
+ const condition_definition *c = &condition_definitions[i];
+
+ p = startswith(line, c->name);
+ if (!p)
+ continue;
+ p += strspn(p, WHITESPACE);
+ if (*p != '=')
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Expected \"=\" in \"%s\".", line);
+
+ p += 1 + strspn(p + 1, WHITESPACE);
+
+ return c->parser(NULL, "(stdin)", 0, NULL, 0, c->name, c->type, p, target, u);
+ }
+
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot parse \"%s\".", line);
+}
+
+_printf_(7, 8)
+static int log_helper(void *userdata, int level, int error, const char *file, int line, const char *func, const char *format, ...) {
+ Unit *u = userdata;
+ va_list ap;
+ int r;
+
+ assert(u);
+
+ /* "upgrade" debug messages */
+ level = MIN(LOG_INFO, level);
+
+ va_start(ap, format);
+ r = log_object_internalv(level, error, file, line, func,
+ NULL,
+ u->id,
+ NULL,
+ NULL,
+ format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int verify_conditions(char **lines, UnitFileScope scope) {
+ _cleanup_(manager_freep) Manager *m = NULL;
+ Unit *u;
+ char **line;
+ int r, q = 1;
+
+ r = manager_new(scope, MANAGER_TEST_RUN_MINIMAL, &m);
+ if (r < 0)
+ return log_error_errno(r, "Failed to initialize manager: %m");
+
+ log_debug("Starting manager...");
+ r = manager_startup(m, NULL, NULL);
+ if (r < 0)
+ return r;
+
+ r = unit_new_for_name(m, sizeof(Service), "test.service", &u);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create test.service: %m");
+
+ STRV_FOREACH(line, lines) {
+ r = parse_condition(u, *line);
+ if (r < 0)
+ return r;
+ }
+
+ r = condition_test_list(u->asserts, assert_type_to_string, log_helper, u);
+ if (u->asserts)
+ log_notice("Asserts %s.", r > 0 ? "succeeded" : "failed");
+
+ q = condition_test_list(u->conditions, condition_type_to_string, log_helper, u);
+ if (u->conditions)
+ log_notice("Conditions %s.", q > 0 ? "succeeded" : "failed");
+
+ return r > 0 && q > 0 ? 0 : -EIO;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "install.h"
+
+int verify_conditions(char **lines, UnitFileScope scope);
bool ip_address_allow_localhost;
bool ip_address_allow_other;
+ bool ip_filters_custom_ingress;
+ bool ip_filters_custom_egress;
+
char *keyring_mode;
bool lock_personality;
bool memory_deny_write_execute;
assert(ret_badness);
assert(ret_description);
- if (!info->ip_address_deny_all) {
+ if (info->ip_filters_custom_ingress || info->ip_filters_custom_egress) {
+ d = strdup("Service defines custom ingress/egress IP filters with BPF programs");
+ b = 0;
+ } else if (!info->ip_address_deny_all) {
d = strdup("Service does not define an IP address whitelist");
b = 10;
} else if (info->ip_address_allow_other) {
return sd_bus_message_exit_container(m);
}
+static int property_read_ip_filters(
+ sd_bus *bus,
+ const char *member,
+ sd_bus_message *m,
+ sd_bus_error *error,
+ void *userdata) {
+
+ struct security_info *info = userdata;
+ _cleanup_(strv_freep) char **l = NULL;
+ int r;
+
+ assert(bus);
+ assert(member);
+ assert(m);
+
+ r = sd_bus_message_read_strv(m, &l);
+ if (r < 0)
+ return r;
+
+ if (streq(member, "IPIngressFilterPath"))
+ info->ip_filters_custom_ingress = !strv_isempty(l);
+ else if (streq(member, "IPEgressFilterPath"))
+ info->ip_filters_custom_ingress = !strv_isempty(l);
+
+ return 0;
+}
+
static int property_read_device_allow(
sd_bus *bus,
const char *member,
{ "FragmentPath", "s", NULL, offsetof(struct security_info, fragment_path) },
{ "IPAddressAllow", "a(iayu)", property_read_ip_address_allow, 0 },
{ "IPAddressDeny", "a(iayu)", property_read_ip_address_allow, 0 },
+ { "IPIngressFilterPath", "as", property_read_ip_filters, 0 },
+ { "IPEgressFilterPath", "as", property_read_ip_filters, 0 },
{ "Id", "s", NULL, offsetof(struct security_info, id) },
{ "KeyringMode", "s", NULL, offsetof(struct security_info, keyring_mode) },
{ "LoadState", "s", NULL, offsetof(struct security_info, load_state) },
#include "sd-bus.h"
#include "alloc-util.h"
+#include "analyze-condition.h"
#include "analyze-security.h"
#include "analyze-verify.h"
#include "build.h"
if (t->kernel_done_time > 0)
strpcpyf(&ptr, size, "= %s ", format_timespan(ts, sizeof(ts), t->firmware_time + t->finish_time, USEC_PER_MSEC));
- if (unit_id && activated_time > 0 && activated_time != USEC_INFINITY) {
+ if (unit_id && timestamp_is_set(activated_time)) {
usec_t base = t->userspace_time > 0 ? t->userspace_time : t->reverse_offset;
size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id,
return 0;
}
+static int do_condition(int argc, char *argv[], void *userdata) {
+ return verify_conditions(strv_skip(argv, 1), arg_scope);
+}
+
static int do_verify(int argc, char *argv[], void *userdata) {
return verify_units(strv_skip(argv, 1), arg_scope, arg_man, arg_generators);
}
" cat-config Show configuration file and drop-ins\n"
" unit-paths List load directories for units\n"
" syscall-filter [NAME...] Print list of syscalls in seccomp filter\n"
+ " condition CONDITION... Evaluate conditions and asserts\n"
" verify FILE... Check unit files for correctness\n"
" service-watchdogs [BOOL] Get/set service watchdog state\n"
" calendar SPEC... Validate repetitive calendar time events\n"
{ "cat-config", 2, VERB_ANY, 0, cat_config },
{ "unit-paths", 1, 1, 0, dump_unit_paths },
{ "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters },
+ { "condition", 2, VERB_ANY, 0, do_condition },
{ "verify", 2, VERB_ANY, 0, do_verify },
{ "calendar", 2, VERB_ANY, 0, test_calendar },
{ "timestamp", 2, VERB_ANY, 0, test_timestamp },
systemd_analyze_sources = files('''
analyze.c
+ analyze-condition.c
+ analyze-condition.h
analyze-verify.c
analyze-verify.h
analyze-security.c
assert(ret);
+ if (major(d) == 0)
+ return -ENODEV;
+
/* If it has a queue this is good enough for us */
xsprintf_sys_block_path(p, "/queue", d);
if (access(p, F_OK) >= 0) {
/* If it is a partition find the originating device */
xsprintf_sys_block_path(p, "/partition", d);
if (access(p, F_OK) < 0)
- return -ENOENT;
+ return -errno;
/* Get parent dev_t */
xsprintf_sys_block_path(p, "/../dev", d);
/* Only return this if it is really good enough for us. */
xsprintf_sys_block_path(p, "/queue", devt);
if (access(p, F_OK) < 0)
- return -ENOENT;
+ return -errno;
*ret = devt;
- return 0;
+ return 1;
}
int get_block_device(const char *path, dev_t *dev) {
* setups, however, only if both partitions are on the same physical device. Hence, let's
* verify this. */
- u = strjoin(p, "/", de->d_name, "/../dev");
+ u = path_join(p, de->d_name, "../dev");
if (!u)
return -ENOMEM;
- v = strjoin(p, "/", found->d_name, "/../dev");
+ v = path_join(p, found->d_name, "../dev");
if (!v)
return -ENOMEM;
if (old_child_fd < 0)
return -errno;
- np = strjoin(subvolume, "/", ino_args.name);
+ np = path_join(subvolume, ino_args.name);
if (!np)
return -ENOMEM;
assert(s);
- for (i = 0; i < cap_last_cap(); i++)
+ for (i = 0; i <= cap_last_cap(); i++)
if (set & (UINT64_C(1) << i)) {
const char *p;
size_t add;
_cleanup_free_ char *word = NULL;
int r;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return r;
if (r <= 0)
#include "alloc-util.h"
#include "capability-util.h"
+#include "cap-list.h"
#include "fileio.h"
#include "log.h"
#include "macro.h"
/* Add capabilities in the set to the inherited caps. Do not apply
* them yet. */
- for (i = 0; i < cap_last_cap(); i++) {
+ for (i = 0; i <= cap_last_cap(); i++) {
if (set & (UINT64_C(1) << i)) {
cap_value_t v;
return -errno;
}
- for (i = 0; i < cap_last_cap(); i++) {
+ for (i = 0; i <= cap_last_cap(); i++) {
if (set & (UINT64_C(1) << i)) {
return cache;
}
+bool capability_quintet_mangle(CapabilityQuintet *q) {
+ unsigned long i;
+ uint64_t combined, drop = 0;
+ bool ambient_supported;
+
+ assert(q);
+
+ combined = q->effective | q->bounding | q->inheritable | q->permitted;
+
+ ambient_supported = q->ambient != (uint64_t) -1;
+ if (ambient_supported)
+ combined |= q->ambient;
+
+ for (i = 0; i <= cap_last_cap(); i++) {
+ unsigned long bit = UINT64_C(1) << i;
+ if (!FLAGS_SET(combined, bit))
+ continue;
+
+ if (prctl(PR_CAPBSET_READ, i) > 0)
+ continue;
+
+ drop |= bit;
+
+ log_debug("Not in the current bounding set: %s", capability_to_name(i));
+ }
+
+ q->effective &= ~drop;
+ q->bounding &= ~drop;
+ q->inheritable &= ~drop;
+ q->permitted &= ~drop;
+
+ if (ambient_supported)
+ q->ambient &= ~drop;
+
+ return drop != 0; /* Let the caller know we changed something */
+}
+
int capability_quintet_enforce(const CapabilityQuintet *q) {
_cleanup_cap_free_ cap_t c = NULL, modified = NULL;
int r;
q->ambient != (uint64_t) -1;
}
+/* Mangles the specified caps quintet taking the current bounding set into account:
+ * drops all caps from all five sets if our bounding set doesn't allow them.
+ * Returns true if the quintet was modified. */
+bool capability_quintet_mangle(CapabilityQuintet *q);
+
int capability_quintet_enforce(const CapabilityQuintet *q);
#include "unit-name.h"
#include "user-util.h"
-int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
+static int cg_enumerate_items(const char *controller, const char *path, FILE **_f, const char *item) {
_cleanup_free_ char *fs = NULL;
FILE *f;
int r;
assert(_f);
- r = cg_get_path(controller, path, "cgroup.procs", &fs);
+ r = cg_get_path(controller, path, item, &fs);
if (r < 0)
return r;
return 0;
}
+int cg_enumerate_processes(const char *controller, const char *path, FILE **_f) {
+ return cg_enumerate_items(controller, path, _f, "cgroup.procs");
+}
+
int cg_read_pid(FILE *f, pid_t *_pid) {
unsigned long ul;
const char *controller,
const char *path,
const char *event,
- char **val) {
+ char **ret) {
_cleanup_free_ char *events = NULL, *content = NULL;
- char *p, *line;
int r;
r = cg_get_path(controller, path, "cgroup.events", &events);
if (r < 0)
return r;
- p = content;
- while ((line = strsep(&p, "\n"))) {
- char *key;
+ for (const char *p = content;;) {
+ _cleanup_free_ char *line = NULL, *key = NULL, *val = NULL;
+ const char *q;
- key = strsep(&line, " ");
- if (!key || !line)
+ r = extract_first_word(&p, &line, "\n", 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return -ENOENT;
+
+ q = line;
+ r = extract_first_word(&q, &key, " ", 0);
+ if (r < 0)
+ return r;
+ if (r == 0)
return -EINVAL;
- if (strcmp(key, event))
+ if (!streq(key, event))
continue;
- *val = strdup(line);
+ val = strdup(q);
+ if (!val)
+ return -ENOMEM;
+
+ *ret = TAKE_PTR(val);
return 0;
}
-
- return -ENOENT;
}
bool cg_ns_supported(void) {
return 0;
}
-int cg_kill(
+static int cg_kill_items(
const char *controller,
const char *path,
int sig,
CGroupFlags flags,
Set *s,
cg_kill_log_func_t log_kill,
- void *userdata) {
+ void *userdata,
+ const char *item) {
_cleanup_set_free_ Set *allocated_set = NULL;
bool done = false;
pid_t pid = 0;
done = true;
- r = cg_enumerate_processes(controller, path, &f);
+ r = cg_enumerate_items(controller, path, &f, item);
if (r < 0) {
if (ret >= 0 && r != -ENOENT)
return r;
return ret;
}
+int cg_kill(
+ const char *controller,
+ const char *path,
+ int sig,
+ CGroupFlags flags,
+ Set *s,
+ cg_kill_log_func_t log_kill,
+ void *userdata) {
+ int r;
+
+ r = cg_kill_items(controller, path, sig, flags, s, log_kill, userdata, "cgroup.procs");
+ if (r < 0 || sig != SIGKILL)
+ return r;
+
+ /* Only in case of killing with SIGKILL and when using cgroupsv2, kill remaining threads manually as
+ a workaround for kernel bug. It was fixed in 5.2-rc5 (c03cd7738a83). */
+ r = cg_unified_controller(controller);
+ if (r < 0)
+ return r;
+ if (r == 0) /* doesn't apply to legacy hierarchy */
+ return 0;
+
+ return cg_kill_items(controller, path, sig, flags, s, log_kill, userdata, "cgroup.threads");
+}
+
int cg_kill_recursive(
const char *controller,
const char *path,
while ((r = cg_read_subgroup(d, &fn)) > 0) {
_cleanup_free_ char *p = NULL;
- p = strjoin(path, "/", fn);
+ p = path_join(path, fn);
free(fn);
if (!p)
return -ENOMEM;
while ((r = cg_read_subgroup(d, &fn)) > 0) {
_cleanup_free_ char *p = NULL;
- p = strjoin(pfrom, "/", fn);
+ p = path_join(pfrom, fn);
free(fn);
if (!p)
return -ENOMEM;
dn = controller_to_dirname(controller);
if (isempty(path) && isempty(suffix))
- t = strappend("/sys/fs/cgroup/", dn);
+ t = path_join("/sys/fs/cgroup", dn);
else if (isempty(path))
- t = strjoin("/sys/fs/cgroup/", dn, "/", suffix);
+ t = path_join("/sys/fs/cgroup", dn, suffix);
else if (isempty(suffix))
- t = strjoin("/sys/fs/cgroup/", dn, "/", path);
+ t = path_join("/sys/fs/cgroup", dn, path);
else
- t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix);
+ t = path_join("/sys/fs/cgroup", dn, path, suffix);
if (!t)
return -ENOMEM;
if (isempty(path) && isempty(suffix))
t = strdup("/sys/fs/cgroup");
else if (isempty(path))
- t = strappend("/sys/fs/cgroup/", suffix);
+ t = path_join("/sys/fs/cgroup", suffix);
else if (isempty(suffix))
- t = strappend("/sys/fs/cgroup/", path);
+ t = path_join("/sys/fs/cgroup", path);
else
- t = strjoin("/sys/fs/cgroup/", path, "/", suffix);
+ t = path_join("/sys/fs/cgroup", path, suffix);
if (!t)
return -ENOMEM;
else if (!path)
t = strdup(suffix);
else
- t = strjoin(path, "/", suffix);
+ t = path_join(path, suffix);
if (!t)
return -ENOMEM;
while ((r = cg_read_subgroup(d, &fn)) > 0) {
_cleanup_free_ char *p = NULL;
- p = strjoin(path, "/", fn);
+ p = path_join(path, fn);
free(fn);
if (!p)
return -ENOMEM;
key = p;
} else {
- p = strjoin(dirpath, "/", de->d_name);
+ p = path_join(dirpath, de->d_name);
if (!p)
return -ENOMEM;
_cleanup_free_ char *rdir = NULL;
char *p1, *p2;
- rdir = prefix_root(root, *dir);
+ rdir = path_join(root, *dir);
if (!rdir)
return -ENOMEM;
if (p2) {
/* Our new entry has higher priority */
- t = prefix_root(root, path);
+ t = path_join(root, path);
if (!t)
return log_oom();
}
/* The new file has lower priority than all the existing entries */
- t = prefix_root(root, path);
+ t = path_join(root, path);
if (!t)
return -ENOMEM;
if (r < 0)
return log_error_errno(r, "Failed to extend config file list: %m");
- p = prefix_root(root, replacement);
+ p = path_join(root, replacement);
if (!p)
return log_oom();
}
if (progress_path) {
if (display_path)
- child_display_path = dp = strjoin(display_path, "/", de->d_name);
+ child_display_path = dp = path_join(display_path, de->d_name);
else
child_display_path = de->d_name;
if (e) {
int r;
- r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_QUOTES);
+ r = strv_split_extract(&m, e, WHITESPACE, EXTRACT_RELAX|EXTRACT_UNQUOTE);
if (r < 0) {
ret[k] = NULL;
strv_free(ret);
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
+#include <stdlib.h>
+#include <string.h>
+
#include "macro.h"
static inline void _reset_errno_(int *saved_errno) {
return -errno;
}
+static inline char *strerror_safe(int error) {
+ /* 'safe' here does NOT mean thread safety. */
+ return strerror(abs(error));
+}
+
/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5.
*
* Hint #2: The kernel sends e.g., EHOSTUNREACH or ENONET to userspace in some ICMP error cases. See the
for (;; (*p)++, c = **p) {
if (c == 0)
goto finish_force_terminate;
- else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_QUOTES)) {
+ else if (IN_SET(c, '\'', '"') && (flags & EXTRACT_UNQUOTE)) {
quote = c;
break;
} else if (c == '\\' && !(flags & EXTRACT_RETAIN_ESCAPE)) {
EXTRACT_RELAX = 1 << 0,
EXTRACT_CUNESCAPE = 1 << 1,
EXTRACT_CUNESCAPE_RELAX = 1 << 2,
- EXTRACT_QUOTES = 1 << 3,
+ EXTRACT_UNQUOTE = 1 << 3,
EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 4,
EXTRACT_RETAIN_ESCAPE = 1 << 5,
} ExtractFlags;
_cleanup_free_ char *p = NULL;
FILE *f;
- if (root)
- p = strjoin(root, *i, "/", path);
- else
- p = strjoin(*i, "/", path);
+ p = path_join(root, *i, path);
if (!p)
return -ENOMEM;
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <stdio.h>
+
#include "format-util.h"
#include "memory-util.h"
memzero(buf, IF_NAMESIZE + 1);
return if_indextoname(ifindex, buf);
}
+
+char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag) {
+ typedef struct {
+ const char *suffix;
+ uint64_t factor;
+ } suffix_table;
+ static const suffix_table table_iec[] = {
+ { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
+ { "M", UINT64_C(1024)*UINT64_C(1024) },
+ { "K", UINT64_C(1024) },
+ }, table_si[] = {
+ { "E", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
+ { "P", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
+ { "T", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
+ { "G", UINT64_C(1000)*UINT64_C(1000)*UINT64_C(1000) },
+ { "M", UINT64_C(1000)*UINT64_C(1000) },
+ { "K", UINT64_C(1000) },
+ };
+ const suffix_table *table;
+ size_t n, i;
+
+ assert_cc(ELEMENTSOF(table_iec) == ELEMENTSOF(table_si));
+
+ if (t == (uint64_t) -1)
+ return NULL;
+
+ table = flag & FORMAT_BYTES_USE_IEC ? table_iec : table_si;
+ n = ELEMENTSOF(table_iec);
+
+ for (i = 0; i < n; i++)
+ if (t >= table[i].factor) {
+ if (flag & FORMAT_BYTES_BELOW_POINT) {
+ snprintf(buf, l,
+ "%" PRIu64 ".%" PRIu64 "%s",
+ t / table[i].factor,
+ i != n - 1 ?
+ (t / table[i + 1].factor * UINT64_C(10) / table[n - 1].factor) % UINT64_C(10):
+ (t * UINT64_C(10) / table[i].factor) % UINT64_C(10),
+ table[i].suffix);
+ } else
+ snprintf(buf, l,
+ "%" PRIu64 "%s",
+ t / table[i].factor,
+ table[i].suffix);
+
+ goto finish;
+ }
+
+ snprintf(buf, l, "%" PRIu64 "%s", t, flag & FORMAT_BYTES_TRAILING_B ? "B" : "");
+
+finish:
+ buf[l-1] = 0;
+ return buf;
+
+}
#include <inttypes.h>
#include <net/if.h>
+#include <stdbool.h>
#if SIZEOF_PID_T == 4
# define PID_PRI PRIi32
#endif
char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]);
+
+typedef enum {
+ FORMAT_BYTES_USE_IEC = 1 << 0,
+ FORMAT_BYTES_BELOW_POINT = 1 << 1,
+ FORMAT_BYTES_TRAILING_B = 1 << 2,
+} FormatBytesFlag;
+
+#define FORMAT_BYTES_MAX 8
+char *format_bytes_full(char *buf, size_t l, uint64_t t, FormatBytesFlag flag);
+static inline char *format_bytes(char *buf, size_t l, uint64_t t) {
+ return format_bytes_full(buf, l, t, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | FORMAT_BYTES_TRAILING_B);
+}
}
int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
- char fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
bool do_chown, do_chmod;
struct stat st;
* unaffected if the uid/gid is changed, i.e. it undoes implicit suid/sgid dropping the kernel does
* on chown().
*
- * This call is happy with O_PATH fds, since we always go via /proc/self/fd/ to change
- * ownership/access mode. */
+ * This call is happy with O_PATH fds. */
- xsprintf(fd_path, "/proc/self/fd/%i", fd);
- if (stat(fd_path, &st) < 0)
+ if (fstat(fd, &st) < 0)
return -errno;
do_chown =
mode_t minimal = st.st_mode & mode; /* the subset of the old and the new mask */
if (((minimal ^ st.st_mode) & 07777) != 0)
- if (chmod(fd_path, minimal & 07777) < 0)
+ if (fchmod_opath(fd, minimal & 07777) < 0)
return -errno;
}
if (do_chown)
- if (chown(fd_path, uid, gid) < 0)
+ if (fchownat(fd, "", uid, gid, AT_EMPTY_PATH) < 0)
return -errno;
if (do_chmod)
- if (chmod(fd_path, mode & 07777) < 0)
+ if (fchmod_opath(fd, mode & 07777) < 0)
return -errno;
return do_chown || do_chmod;
/* Prefix what's left to do with what we just read, and start the loop again, but
* remain in the current directory. */
- joined = strjoin(destination, todo);
+ joined = path_join(destination, todo);
} else
- joined = strjoin("/", destination, todo);
+ joined = path_join("/", destination, todo);
if (!joined)
return -ENOMEM;
#include "macro.h"
#include "memory-util.h"
#include "mempool.h"
+#include "missing.h"
#include "process-util.h"
#include "random-util.h"
#include "set.h"
/* The pool is only allocated by the main thread, but the memory can
* be passed to other threads. Let's clean up if we are the main thread
* and no other threads are live. */
- if (!is_main_thread())
+ /* We build our own is_main_thread() here, which doesn't use C11
+ * TLS based caching of the result. That's because valgrind apparently
+ * doesn't like malloc() (which C11 TLS internally uses) to be called
+ * from a GCC destructors. */
+ if (getpid() != gettid())
return;
r = get_proc_field("/proc/self/status", "Threads", WHITESPACE, &t);
#if ENABLE_DEBUG_HASHMAP
# define HASHMAP_DEBUG_PARAMS , const char *func, const char *file, int line
-# define HASHMAP_DEBUG_SRC_ARGS , __func__, __FILE__, __LINE__
+# define HASHMAP_DEBUG_SRC_ARGS , __func__, PROJECT_FILE, __LINE__
# define HASHMAP_DEBUG_PASS_ARGS , func, file, line
#else
# define HASHMAP_DEBUG_PARAMS
iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(x);
return x;
}
+
+char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) {
+ char *x;
+
+ x = set_iovec_string_field(iovec, n_iovec, field, value);
+ free(value);
+ return x;
+}
+
+struct iovec_wrapper *iovw_new(void) {
+ return malloc0(sizeof(struct iovec_wrapper));
+}
+
+void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors) {
+ if (free_vectors)
+ for (size_t i = 0; i < iovw->count; i++)
+ free(iovw->iovec[i].iov_base);
+
+ iovw->iovec = mfree(iovw->iovec);
+ iovw->count = 0;
+ iovw->size_bytes = 0;
+}
+
+struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw) {
+ iovw_free_contents(iovw, true);
+
+ return mfree(iovw);
+}
+
+struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw) {
+ iovw_free_contents(iovw, false);
+
+ return mfree(iovw);
+}
+
+int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len) {
+ if (iovw->count >= IOV_MAX)
+ return -E2BIG;
+
+ if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
+ return log_oom();
+
+ iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len);
+ return 0;
+}
+
+int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value) {
+ _cleanup_free_ char *x = NULL;
+ int r;
+
+ x = strappend(field, value);
+ if (!x)
+ return log_oom();
+
+ r = iovw_put(iovw, x, strlen(x));
+ if (r >= 0)
+ TAKE_PTR(x);
+
+ return r;
+}
+
+int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value) {
+ _cleanup_free_ _unused_ char *free_ptr = value;
+
+ return iovw_put_string_field(iovw, field, value);
+}
+
+void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
+ size_t i;
+
+ for (i = 0; i < iovw->count; i++)
+ iovw->iovec[i].iov_base = (char *)iovw->iovec[i].iov_base - old + new;
+}
+
+size_t iovw_size(struct iovec_wrapper *iovw) {
+ size_t n = 0, i;
+
+ for (i = 0; i < iovw->count; i++)
+ n += iovw->iovec[i].iov_len;
+
+ return n;
+}
#define IOVEC_MAKE_STRING(string) (struct iovec) IOVEC_INIT_STRING(string)
char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value);
+char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value);
+
+struct iovec_wrapper {
+ struct iovec *iovec;
+ size_t count;
+ size_t size_bytes;
+};
+
+struct iovec_wrapper *iovw_new(void);
+struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw);
+struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw);
+void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors);
+int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
+int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value);
+int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value);
+void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);
+size_t iovw_size(struct iovec_wrapper *iovw);
get_log_colors(LOG_PRI(level), &on, &off, NULL);
if (show_location) {
- (void) snprintf(location, sizeof location, "(%s:%i) ", file, line);
+ const char *lon = "", *loff = "";
+ if (show_color) {
+ lon = ANSI_HIGHLIGHT_YELLOW4;
+ loff = ANSI_NORMAL;
+ }
+
+ (void) snprintf(location, sizeof location, "%s%s:%i%s: ", lon, file, line, loff);
iovec[n++] = IOVEC_MAKE_STRING(location);
}
e = getenv("SYSTEMD_LOG_COLOR");
if (e && log_show_color_from_string(e) < 0)
- log_warning("Failed to parse bool '%s'. Ignoring.", e);
+ log_warning("Failed to parse log color '%s'. Ignoring.", e);
e = getenv("SYSTEMD_LOG_LOCATION");
if (e && log_show_location_from_string(e) < 0)
- log_warning("Failed to parse bool '%s'. Ignoring.", e);
+ log_warning("Failed to parse log location '%s'. Ignoring.", e);
}
LogTarget log_get_target(void) {
if (unit)
unit_fmt = getpid_cached() == 1 ? "UNIT=%s" : "USER_UNIT=%s";
- if (config_file)
- return log_struct_internal(
- LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level),
- error,
- file, line, func,
- "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR,
- "CONFIG_FILE=%s", config_file,
- "CONFIG_LINE=%u", config_line,
- LOG_MESSAGE("%s:%u: %s", config_file, config_line, buffer),
- unit_fmt, unit,
- NULL);
- else if (unit)
+ if (config_file) {
+ if (config_line > 0)
+ return log_struct_internal(
+ LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level),
+ error,
+ file, line, func,
+ "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR,
+ "CONFIG_FILE=%s", config_file,
+ "CONFIG_LINE=%u", config_line,
+ LOG_MESSAGE("%s:%u: %s", config_file, config_line, buffer),
+ unit_fmt, unit,
+ NULL);
+ else
+ return log_struct_internal(
+ LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level),
+ error,
+ file, line, func,
+ "MESSAGE_ID=" SD_MESSAGE_INVALID_CONFIGURATION_STR,
+ "CONFIG_FILE=%s", config_file,
+ LOG_MESSAGE("%s: %s", config_file, buffer),
+ unit_fmt, unit,
+ NULL);
+ } else if (unit)
return log_struct_internal(
LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, level),
error,
* for the application itself.
*/
+assert_cc(STRLEN(__FILE__) > STRLEN(RELATIVE_SOURCE_PATH) + 1)
+#define PROJECT_FILE (__FILE__ + STRLEN(RELATIVE_SOURCE_PATH) + 1)
+
int log_open(void);
void log_close(void);
void log_forget_fds(void);
log_assert_failed_return_realm(LOG_REALM, (text), __VA_ARGS__)
#define log_dispatch(level, error, buffer) \
- log_dispatch_internal(level, error, __FILE__, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer)
+ log_dispatch_internal(level, error, PROJECT_FILE, __LINE__, __func__, NULL, NULL, NULL, NULL, buffer)
/* Logging with level */
#define log_full_errno_realm(realm, level, error, ...) \
int _level = (level), _e = (error), _realm = (realm); \
(log_get_max_level_realm(_realm) >= LOG_PRI(_level)) \
? log_internal_realm(LOG_REALM_PLUS_LEVEL(_realm, _level), _e, \
- __FILE__, __LINE__, __func__, __VA_ARGS__) \
+ PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
: -ERRNO_VALUE(_e); \
})
/* Structured logging */
#define log_struct_errno(level, error, ...) \
log_struct_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \
- error, __FILE__, __LINE__, __func__, __VA_ARGS__, NULL)
+ error, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__, NULL)
#define log_struct(level, ...) log_struct_errno(level, 0, __VA_ARGS__)
#define log_struct_iovec_errno(level, error, iovec, n_iovec) \
log_struct_iovec_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \
- error, __FILE__, __LINE__, __func__, iovec, n_iovec)
+ error, PROJECT_FILE, __LINE__, __func__, iovec, n_iovec)
#define log_struct_iovec(level, iovec, n_iovec) log_struct_iovec_errno(level, 0, iovec, n_iovec)
/* This modifies the buffer passed! */
#define log_dump(level, buffer) \
log_dump_internal(LOG_REALM_PLUS_LEVEL(LOG_REALM, level), \
- 0, __FILE__, __LINE__, __func__, buffer)
+ 0, PROJECT_FILE, __LINE__, __func__, buffer)
-#define log_oom() log_oom_internal(LOG_REALM, __FILE__, __LINE__, __func__)
+#define log_oom() log_oom_internal(LOG_REALM, PROJECT_FILE, __LINE__, __func__)
bool log_on_console(void) _pure_;
({ \
int _level = (level), _e = (error); \
(log_get_max_level() >= LOG_PRI(_level)) \
- ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+ ? log_syntax_internal(unit, _level, config_file, config_line, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
: -ERRNO_VALUE(_e); \
})
({ \
int _level = (level); \
(log_get_max_level() >= LOG_PRI(_level)) \
- ? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, __FILE__, __LINE__, __func__, rvalue) \
+ ? log_syntax_invalid_utf8_internal(unit, _level, config_file, config_line, PROJECT_FILE, __LINE__, __func__, rvalue) \
: -EINVAL; \
})
#define assert_message_se(expr, message) \
do { \
if (_unlikely_(!(expr))) \
- log_assert_failed(message, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ log_assert_failed(message, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \
} while (false)
#define assert_log(expr, message) ((_likely_(expr)) \
? (true) \
- : (log_assert_failed_return(message, __FILE__, __LINE__, __PRETTY_FUNCTION__), false))
+ : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__), false))
#endif /* __COVERITY__ */
#define assert_not_reached(t) \
do { \
- log_assert_failed_unreachable(t, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ log_assert_failed_unreachable(t, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \
} while (false)
#if defined(static_assert)
#pragma once
#include <inttypes.h>
+#include <malloc.h>
#include <stdbool.h>
#include <string.h>
#include <sys/types.h>
void *explicit_bzero_safe(void *p, size_t l);
#endif
+static inline void erase_and_freep(void *p) {
+ void *ptr = *(void**) p;
+
+ if (ptr) {
+ size_t l = malloc_usable_size(ptr);
+ explicit_bzero_safe(ptr, l);
+ free(ptr);
+ }
+}
+
/* Use with _cleanup_ to erase a single 'char' when leaving scope */
static inline void erase_char(char *p) {
explicit_bzero_safe(p, sizeof(char));
# define statx missing_statx
#endif
+
+#if !HAVE_SET_MEMPOLICY
+
+enum {
+ MPOL_DEFAULT,
+ MPOL_PREFERRED,
+ MPOL_BIND,
+ MPOL_INTERLEAVE,
+ MPOL_LOCAL,
+};
+
+static inline long missing_set_mempolicy(int mode, const unsigned long *nodemask,
+ unsigned long maxnode) {
+ long i;
+# ifdef __NR_set_mempolicy
+ i = syscall(__NR_set_mempolicy, mode, nodemask, maxnode);
+# else
+ errno = ENOSYS;
+ i = -1;
+# endif
+ return i;
+}
+
+# define set_mempolicy missing_set_mempolicy
+#endif
+
+
+#if !HAVE_GET_MEMPOLICY
+static inline long missing_get_mempolicy(int *mode, unsigned long *nodemask,
+ unsigned long maxnode, void *addr,
+ unsigned long flags) {
+ long i;
+# ifdef __NR_get_mempolicy
+ i = syscall(__NR_get_mempolicy, mode, nodemask, maxnode, addr, flags);
+# else
+ errno = ENOSYS;
+ i = -1;
+# endif
+ return i;
+}
+
+#define get_mempolicy missing_get_mempolicy
+#endif
}
if (pidns_fd)
- *pidns_fd = pidnsfd;
+ *pidns_fd = TAKE_FD(pidnsfd);
if (mntns_fd)
- *mntns_fd = mntnsfd;
+ *mntns_fd = TAKE_FD(mntnsfd);
if (netns_fd)
- *netns_fd = netnsfd;
+ *netns_fd = TAKE_FD(netnsfd);
if (userns_fd)
- *userns_fd = usernsfd;
+ *userns_fd = TAKE_FD(usernsfd);
if (root_fd)
- *root_fd = rfd;
-
- pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
+ *root_fd = TAKE_FD(rfd);
return 0;
}
return 0;
}
-char *format_bytes(char *buf, size_t l, uint64_t t) {
- unsigned i;
-
- /* This only does IEC units so far */
-
- static const struct {
- const char *suffix;
- uint64_t factor;
- } table[] = {
- { "E", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "P", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "T", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "G", UINT64_C(1024)*UINT64_C(1024)*UINT64_C(1024) },
- { "M", UINT64_C(1024)*UINT64_C(1024) },
- { "K", UINT64_C(1024) },
- };
-
- if (t == (uint64_t) -1)
- return NULL;
-
- for (i = 0; i < ELEMENTSOF(table); i++) {
-
- if (t >= table[i].factor) {
- snprintf(buf, l,
- "%" PRIu64 ".%" PRIu64 "%s",
- t / table[i].factor,
- ((t*UINT64_C(10)) / table[i].factor) % UINT64_C(10),
- table[i].suffix);
-
- goto finish;
- }
- }
-
- snprintf(buf, l, "%" PRIu64 "B", t);
-
-finish:
- buf[l-1] = 0;
- return buf;
-
-}
-
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u) {
char *x = NULL;
unsigned long l;
int parse_errno(const char *t);
int parse_syscall_and_errno(const char *in, char **name, int *error);
-#define FORMAT_BYTES_MAX 8
-char *format_bytes(char *buf, size_t l, uint64_t t);
-
int safe_atou_full(const char *s, unsigned base, unsigned *ret_u);
static inline int safe_atou(const char *s, unsigned *ret_u) {
if (path_is_absolute(p) || isempty(prefix))
return strdup(p);
- if (endswith(prefix, "/"))
- return strjoin(prefix, p);
- else
- return strjoin(prefix, "/", p);
+ return path_join(prefix, p);
}
int safe_getcwd(char **ret) {
if (root) {
orig = *s;
- t = prefix_root(root, orig);
+ t = path_join(root, orig);
if (!t) {
enomem = true;
continue;
if (!path_is_absolute(element))
continue;
- j = strjoin(element, "/", name);
+ j = path_join(element, name);
if (!j)
return -ENOMEM;
return binary_is_good(mkfs);
}
-char *prefix_root(const char *root, const char *path) {
- char *n, *p;
- size_t l;
-
- /* If root is passed, prefixes path with it. Otherwise returns
- * it as is. */
-
- assert(path);
-
- /* First, drop duplicate prefixing slashes from the path */
- while (path[0] == '/' && path[1] == '/')
- path++;
-
- if (empty_or_root(root))
- return strdup(path);
-
- l = strlen(root) + 1 + strlen(path) + 1;
-
- n = new(char, l);
- if (!n)
- return NULL;
-
- p = stpcpy(n, root);
-
- while (p > n && p[-1] == '/')
- p--;
-
- if (path[0] != '/')
- *(p++) = '/';
-
- strcpy(p, path);
- return n;
-}
-
int parse_path_argument_and_warn(const char *path, bool suppress_root, char **arg) {
char *p;
int r;
_cleanup_free_ char *path = NULL;
char *c, **name;
- path = prefix_root(root, pattern);
+ path = path_join(root, pattern);
if (!path)
return -ENOMEM;
_slash && ((*_slash = 0), true); \
_slash = strrchr((prefix), '/'))
-char *prefix_root(const char *root, const char *path);
-
-/* Similar to prefix_root(), but returns an alloca() buffer, or
- * possibly a const pointer into the path parameter */
+/* Similar to path_join(), but only works for two components, and only the first one may be NULL and returns
+ * an alloca() buffer, or possibly a const pointer into the path parameter. */
#define prefix_roota(root, path) \
({ \
const char* _path = (path), *_root = (root), *_ret; \
size_t _l; \
while (_path[0] == '/' && _path[1] == '/') \
_path ++; \
- if (empty_or_root(_root)) \
+ if (isempty(_root)) \
_ret = _path; \
else { \
_l = strlen(_root) + 1 + strlen(_path) + 1; \
_cleanup_free_ char *word = NULL;
const char *c;
- r = extract_first_word(&q, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
+ r = extract_first_word(&q, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
if (r < 0)
return r;
if (r == 0)
if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME))
return -EINVAL;
- /* We refuse to clean the root file system with this
- * call. This is extra paranoia to never cause a really
- * seriously broken system. */
+ /* We refuse to clean the root file system with this call. This is extra paranoia to never cause a
+ * really seriously broken system. */
if (path_equal_or_files_same(path, "/", AT_SYMLINK_NOFOLLOW))
return log_error_errno(SYNTHETIC_ERRNO(EPERM),
"Attempted to remove entire root file system (\"%s\"), and we can't allow that.",
if (r >= 0)
return r;
+ if (FLAGS_SET(flags, REMOVE_MISSING_OK) && r == -ENOENT)
+ return 0;
+
if (!IN_SET(r, -ENOTTY, -EINVAL, -ENOTDIR))
return r;
fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (fd < 0) {
+ if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT)
+ return 0;
+
if (!IN_SET(errno, ENOTDIR, ELOOP))
return -errno;
- if (!(flags & REMOVE_PHYSICAL)) {
- if (statfs(path, &s) < 0)
- return -errno;
+ if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES))
+ return 0;
- if (is_physical_fs(&s))
- return log_error_errno(SYNTHETIC_ERRNO(EPERM),
- "Attempted to remove files from a disk file system under \"%s\", refusing.",
- path);
- }
+ if (FLAGS_SET(flags, REMOVE_ROOT)) {
+
+ if (!FLAGS_SET(flags, REMOVE_PHYSICAL)) {
+ if (statfs(path, &s) < 0)
+ return -errno;
+
+ if (is_physical_fs(&s))
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Attempted to remove files from a disk file system under \"%s\", refusing.",
+ path);
+ }
+
+ if (unlink(path) < 0) {
+ if (FLAGS_SET(flags, REMOVE_MISSING_OK) && errno == ENOENT)
+ return 0;
- if ((flags & REMOVE_ROOT) && !(flags & REMOVE_ONLY_DIRECTORIES))
- if (unlink(path) < 0 && errno != ENOENT)
return -errno;
+ }
+ }
return 0;
}
r = rm_rf_children(fd, flags, NULL);
- if (flags & REMOVE_ROOT) {
- if (rmdir(path) < 0) {
- if (r == 0 && errno != ENOENT)
- r = -errno;
- }
- }
+ if (FLAGS_SET(flags, REMOVE_ROOT) &&
+ rmdir(path) < 0 &&
+ r >= 0 &&
+ (!FLAGS_SET(flags, REMOVE_MISSING_OK) || errno != ENOENT))
+ r = -errno;
return r;
}
#include "errno-util.h"
typedef enum RemoveFlags {
- REMOVE_ONLY_DIRECTORIES = 1 << 0,
- REMOVE_ROOT = 1 << 1,
- REMOVE_PHYSICAL = 1 << 2, /* if not set, only removes files on tmpfs, never physical file systems */
- REMOVE_SUBVOLUME = 1 << 3,
+ REMOVE_ONLY_DIRECTORIES = 1 << 0, /* Only remove empty directories, no files */
+ REMOVE_ROOT = 1 << 1, /* Remove the specified directory itself too, not just the contents of it */
+ REMOVE_PHYSICAL = 1 << 2, /* If not set, only removes files on tmpfs, never physical file systems */
+ REMOVE_SUBVOLUME = 1 << 3, /* Drop btrfs subvolumes in the tree too */
+ REMOVE_MISSING_OK = 1 << 4, /* If the top-level directory is missing, ignore the ENOENT for it */
} RemoveFlags;
int rm_rf_children(int fd, RemoveFlags flags, struct stat *root_dev);
}
}
+void siphash24_compress_boolean(bool in, struct siphash *state) {
+ int i = in;
+
+ siphash24_compress(&i, sizeof i, state);
+}
+
uint64_t siphash24_finalize(struct siphash *state) {
uint64_t b;
void siphash24_init(struct siphash *state, const uint8_t k[static 16]);
void siphash24_compress(const void *in, size_t inlen, struct siphash *state);
+void siphash24_compress_boolean(bool in, struct siphash *state);
#define siphash24_compress_byte(byte, state) siphash24_compress((const uint8_t[]) { (byte) }, 1, (state))
uint64_t siphash24_finalize(struct siphash *state);
return 1;
}
-char* string_erase(char *x) {
- if (!x)
- return NULL;
-
- /* A delicious drop of snake-oil! To be called on memory where
- * we stored passphrases or so, after we used them. */
- explicit_bzero_safe(x, strlen(x));
- return x;
-}
-
-char *string_free_erase(char *s) {
- return mfree(string_erase(s));
-}
-
bool string_is_safe(const char *p) {
const char *t;
}
int free_and_strndup(char **p, const char *s, size_t l);
-char *string_erase(char *x);
-
-char *string_free_erase(char *s);
-DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase);
-#define _cleanup_string_free_erase_ _cleanup_(string_free_erasep)
-
bool string_is_safe(const char *p) _pure_;
static inline size_t strlen_ptr(const char *s) {
#include "escape.h"
#include "extract-word.h"
#include "fileio.h"
+#include "memory-util.h"
#include "nulstr-util.h"
#include "sort-util.h"
#include "string-util.h"
char **i;
STRV_FOREACH(i, l)
- string_erase(*i);
+ erase_and_freep(i);
- return strv_free(l);
+ return mfree(l);
}
char **strv_copy(char * const *l) {
p = line;
for (;;) {
- _cleanup_free_ char *tty = NULL;
- char *path;
+ _cleanup_free_ char *tty = NULL, *path = NULL;
r = extract_first_word(&p, &tty, NULL, 0);
if (r < 0)
return r;
}
- path = strappend("/dev/", tty);
+ path = path_join("/dev", tty);
if (!path)
return -ENOMEM;
if (access(path, F_OK) < 0) {
log_debug_errno(errno, "Console device %s is not accessible, skipping: %m", path);
- free(path);
continue;
}
- r = strv_consume(&l, path);
+ r = strv_consume(&l, TAKE_PTR(path));
if (r < 0)
return r;
}
}
}
-int ptsname_namespace(int pty, char **ret) {
+int openpt_allocate(int flags, char **ret_slave) {
+ _cleanup_close_ int fd = -1;
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ fd = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (ret_slave) {
+ r = ptsname_malloc(fd, &p);
+ if (r < 0)
+ return r;
+
+ if (!path_startswith(p, "/dev/pts/"))
+ return -EINVAL;
+ }
+
+ if (unlockpt(fd) < 0)
+ return -errno;
+
+ if (ret_slave)
+ *ret_slave = TAKE_PTR(p);
+
+ return TAKE_FD(fd);
+}
+
+static int ptsname_namespace(int pty, char **ret) {
int no = -1, r;
/* Like ptsname(), but doesn't assume that the path is
return 0;
}
-int openpt_in_namespace(pid_t pid, int flags) {
- _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1;
+int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave) {
+ _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, usernsfd = -1, rootfd = -1, fd = -1;
_cleanup_close_pair_ int pair[2] = { -1, -1 };
pid_t child;
int r;
if (r < 0)
return r;
if (r == 0) {
- int master;
-
pair[0] = safe_close(pair[0]);
- master = posix_openpt(flags|O_NOCTTY|O_CLOEXEC);
- if (master < 0)
- _exit(EXIT_FAILURE);
-
- if (unlockpt(master) < 0)
+ fd = openpt_allocate(flags, NULL);
+ if (fd < 0)
_exit(EXIT_FAILURE);
- if (send_one_fd(pair[1], master, 0) < 0)
+ if (send_one_fd(pair[1], fd, 0) < 0)
_exit(EXIT_FAILURE);
_exit(EXIT_SUCCESS);
if (r != EXIT_SUCCESS)
return -EIO;
- return receive_one_fd(pair[0], 0);
+ fd = receive_one_fd(pair[0], 0);
+ if (fd < 0)
+ return fd;
+
+ if (ret_slave) {
+ r = ptsname_namespace(fd, ret_slave);
+ if (r < 0)
+ return r;
+ }
+
+ return TAKE_FD(fd);
}
int open_terminal_in_namespace(pid_t pid, const char *name, int mode) {
#define ANSI_GREY "\x1B[0;38;5;245m"
/* Bold/highlighted */
-#define ANSI_HIGHLIGHT_RED "\x1B[0;1;31m"
-#define ANSI_HIGHLIGHT_GREEN "\x1B[0;1;32m"
-#define ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;38;5;185m"
-#define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m"
-#define ANSI_HIGHLIGHT_MAGENTA "\x1B[0;1;35m"
-#define ANSI_HIGHLIGHT_GREY "\x1B[0;1;38;5;245m"
+#define ANSI_HIGHLIGHT_RED "\x1B[0;1;31m"
+#define ANSI_HIGHLIGHT_GREEN "\x1B[0;1;32m"
+#define ANSI_HIGHLIGHT_YELLOW "\x1B[0;1;38;5;185m"
+#define ANSI_HIGHLIGHT_BLUE "\x1B[0;1;34m"
+#define ANSI_HIGHLIGHT_MAGENTA "\x1B[0;1;35m"
+#define ANSI_HIGHLIGHT_GREY "\x1B[0;1;38;5;245m"
+#define ANSI_HIGHLIGHT_YELLOW4 "\x1B[0;1;38;5;100m"
/* Underlined */
#define ANSI_HIGHLIGHT_RED_UNDERLINE "\x1B[0;1;4;31m"
int getttyname_harder(int fd, char **r);
int ptsname_malloc(int fd, char **ret);
-int ptsname_namespace(int pty, char **ret);
-int openpt_in_namespace(pid_t pid, int flags);
+int openpt_allocate(int flags, char **ret_slave);
+int openpt_allocate_in_namespace(pid_t pid, int flags, char **ret_slave);
int open_terminal_in_namespace(pid_t pid, const char *name, int mode);
int vt_default_utf8(void);
assert(buf);
- if (l <
- 3 + /* week day */
- 1 + 10 + /* space and date */
- 1 + 8 + /* space and time */
- (us ? 1 + 6 : 0) + /* "." and microsecond part */
- 1 + 1 + /* space and shortest possible zone */
- 1)
+ if (l < (size_t) (3 + /* week day */
+ 1 + 10 + /* space and date */
+ 1 + 8 + /* space and time */
+ (us ? 1 + 6 : 0) + /* "." and microsecond part */
+ 1 + 1 + /* space and shortest possible zone */
+ 1))
return NULL; /* Not enough space even for the shortest form. */
if (t <= 0 || t == USEC_INFINITY)
return NULL; /* Timestamp is unset */
#define TRIPLE_TIMESTAMP_HAS_CLOCK(clock) \
IN_SET(clock, CLOCK_REALTIME, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM)
+static inline bool timestamp_is_set(usec_t timestamp) {
+ return timestamp > 0 && timestamp != USEC_INFINITY;
+}
+
static inline bool dual_timestamp_is_set(const dual_timestamp *ts) {
- return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
- (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY));
+ return timestamp_is_set(ts->realtime) ||
+ timestamp_is_set(ts->monotonic);
}
static inline bool triple_timestamp_is_set(const triple_timestamp *ts) {
- return ((ts->realtime > 0 && ts->realtime != USEC_INFINITY) ||
- (ts->monotonic > 0 && ts->monotonic != USEC_INFINITY) ||
- (ts->boottime > 0 && ts->boottime != USEC_INFINITY));
+ return timestamp_is_set(ts->realtime) ||
+ timestamp_is_set(ts->monotonic) ||
+ timestamp_is_set(ts->boottime);
}
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock);
if (r < 0)
return r;
- p = strjoin(tmp, "/XXXXXX");
+ p = path_join(tmp, "XXXXXX");
}
if (!p)
return -ENOMEM;
[UNIT_INACTIVE] = "inactive",
[UNIT_FAILED] = "failed",
[UNIT_ACTIVATING] = "activating",
- [UNIT_DEACTIVATING] = "deactivating"
+ [UNIT_DEACTIVATING] = "deactivating",
+ [UNIT_MAINTENANCE] = "maintenance",
};
DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
[SERVICE_FINAL_SIGKILL] = "final-sigkill",
[SERVICE_FAILED] = "failed",
[SERVICE_AUTO_RESTART] = "auto-restart",
+ [SERVICE_CLEANING] = "cleaning",
};
DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
UNIT_FAILED,
UNIT_ACTIVATING,
UNIT_DEACTIVATING,
+ UNIT_MAINTENANCE,
_UNIT_ACTIVE_STATE_MAX,
_UNIT_ACTIVE_STATE_INVALID = -1
} UnitActiveState;
SERVICE_FINAL_SIGKILL,
SERVICE_FAILED,
SERVICE_AUTO_RESTART,
+ SERVICE_CLEANING,
_SERVICE_STATE_MAX,
_SERVICE_STATE_INVALID = -1
} ServiceState;
/* Returns -errno, or 0 for domU, or 1 for dom0 */
static int detect_vm_xen_dom0(void) {
_cleanup_free_ char *domcap = NULL;
- char *cap, *i;
int r;
r = read_one_line_file(PATH_FEATURES, &domcap);
if (r < 0)
return r;
- i = domcap;
- while ((cap = strsep(&i, ",")))
- if (streq(cap, "control_d"))
- break;
- if (!cap) {
- log_debug("Virtualization XEN DomU found (/proc/xen/capabilities)");
- return 0;
- }
+ for (const char *i = domcap;;) {
+ _cleanup_free_ char *cap = NULL;
- log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)");
- return 1;
+ r = extract_first_word(&i, &cap, ",", 0);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ log_debug("Virtualization XEN DomU found (/proc/xen/capabilities)");
+ return 0;
+ }
+
+ if (streq(cap, "control_d")) {
+ log_debug("Virtualization XEN Dom0 ignored (/proc/xen/capabilities)");
+ return 1;
+ }
+ }
}
static int detect_vm_hypervisor(void) {
}
static int run(int argc, char *argv[]) {
-
static const Verb verbs[] = {
- { "help", VERB_ANY, VERB_ANY, 0, help },
- { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
- { "good", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_set },
- { "bad", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_set },
- { "indeterminate", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_set },
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
+ { "good", VERB_ANY, 1, 0, verb_set },
+ { "bad", VERB_ANY, 1, 0, verb_set },
+ { "indeterminate", VERB_ANY, 1, 0, verb_set },
{}
};
#include "copy.h"
#include "dirent-util.h"
#include "efivars.h"
+#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) {
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
+ const char *p;
int c = 0, r;
- char *p;
assert(esp_path);
assert(path);
- p = strjoina(esp_path, "/", path);
+ p = prefix_roota(esp_path, path);
d = opendir(p);
if (!d) {
if (errno == ENOENT)
*s,
&status);
if (!strv_isempty(e->options)) {
- _cleanup_free_ char *t;
+ _cleanup_free_ char *t = NULL, *t2 = NULL;
+ _cleanup_strv_free_ char **ts = NULL;
t = strv_join(e->options, " ");
if (!t)
return log_oom();
- printf(" options: %s\n", t);
+
+ ts = strv_split_newlines(t);
+ if (!ts)
+ return log_oom();
+
+ t2 = strv_join(ts, "\n ");
+ if (!t2)
+ return log_oom();
+
+
+ printf(" options: %s\n", t2);
}
if (e->device_tree)
boot_entry_file_list("devicetree", e->root, e->device_tree, &status);
uint32_t part, uint64_t pstart, uint64_t psize,
sd_id128_t uuid, const char *path,
bool first) {
- char *p;
+ const char *p;
uint16_t slot;
int r;
return 0;
}
- p = strjoina(esp_path, path);
+ p = prefix_roota(esp_path, path);
if (access(p, F_OK) < 0) {
if (errno == ENOENT)
return 0;
}
static int remove_boot_efi(const char *esp_path) {
- char *p;
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
+ const char *p;
int r, c = 0;
- p = strjoina(esp_path, "/EFI/BOOT");
+ p = prefix_roota(esp_path, "/EFI/BOOT");
d = opendir(p);
if (!d) {
if (errno == ENOENT)
}
static int rmdir_one(const char *prefix, const char *suffix) {
- char *p;
+ const char *p;
- p = strjoina(prefix, "/", suffix);
+ p = prefix_roota(prefix, suffix);
if (rmdir(p) < 0) {
bool ignore = IN_SET(errno, ENOENT, ENOTEMPTY);
}
static int remove_binaries(const char *esp_path) {
- char *p;
+ const char *p;
int r, q;
- p = strjoina(esp_path, "/EFI/systemd");
+ p = prefix_roota(esp_path, "/EFI/systemd");
r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL);
q = remove_boot_efi(esp_path);
assert(esp_path);
- p = strjoina(esp_path, "/loader/loader.conf");
+ p = prefix_roota(esp_path, "/loader/loader.conf");
if (unlink(p) < 0) {
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to unlink file \"%s\": %m", p);
if (errno != ENOENT)
const char *p;
int r, fd;
- p = strjoina(esp_path, "/loader/loader.conf");
+ p = prefix_roota(esp_path, "/loader/loader.conf");
if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */
return 0;
uint64_t flag;
const char *name;
} flags[] = {
- { EFI_LOADER_FEATURE_BOOT_COUNTING, "Boot counting" },
- { EFI_LOADER_FEATURE_CONFIG_TIMEOUT, "Menu timeout control" },
- { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control" },
- { EFI_LOADER_FEATURE_ENTRY_DEFAULT, "Default entry control" },
- { EFI_LOADER_FEATURE_ENTRY_ONESHOT, "One-shot entry control" },
+ { EFI_LOADER_FEATURE_BOOT_COUNTING, "Boot counting" },
+ { EFI_LOADER_FEATURE_CONFIG_TIMEOUT, "Menu timeout control" },
+ { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT, "One-shot menu timeout control" },
+ { EFI_LOADER_FEATURE_ENTRY_DEFAULT, "Default entry control" },
+ { EFI_LOADER_FEATURE_ENTRY_ONESHOT, "One-shot entry control" },
+ { EFI_LOADER_FEATURE_XBOOTLDR, "Support for XBOOTLDR partition" },
};
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
}
static int bootctl_main(int argc, char *argv[]) {
-
static const Verb verbs[] = {
- { "help", VERB_ANY, VERB_ANY, 0, help },
- { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
- { "install", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
- { "update", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_install },
- { "remove", VERB_ANY, 1, VERB_MUST_BE_ROOT, verb_remove },
- { "list", VERB_ANY, 1, 0, verb_list },
- { "set-default", 2, 2, VERB_MUST_BE_ROOT, verb_set_default },
- { "set-oneshot", 2, 2, VERB_MUST_BE_ROOT, verb_set_default },
+ { "help", VERB_ANY, VERB_ANY, 0, help },
+ { "status", VERB_ANY, 1, VERB_DEFAULT, verb_status },
+ { "install", VERB_ANY, 1, 0, verb_install },
+ { "update", VERB_ANY, 1, 0, verb_install },
+ { "remove", VERB_ANY, 1, 0, verb_remove },
+ { "list", VERB_ANY, 1, 0, verb_list },
+ { "set-default", 2, 2, 0, verb_set_default },
+ { "set-oneshot", 2, 2, 0, verb_set_default },
{}
};
#include "disk.h"
#include "graphics.h"
#include "linux.h"
+#include "loader-features.h"
#include "measure.h"
#include "pe.h"
#include "shim.h"
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
static const UINT64 loader_features =
- (1ULL << 0) | /* I honour the LoaderConfigTimeout variable */
- (1ULL << 1) | /* I honour the LoaderConfigTimeoutOneShot variable */
- (1ULL << 2) | /* I honour the LoaderEntryDefault variable */
- (1ULL << 3) | /* I honour the LoaderEntryOneShot variable */
- (1ULL << 4) | /* I support boot counting */
+ EFI_LOADER_FEATURE_CONFIG_TIMEOUT |
+ EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT |
+ EFI_LOADER_FEATURE_ENTRY_DEFAULT |
+ EFI_LOADER_FEATURE_ENTRY_ONESHOT |
+ EFI_LOADER_FEATURE_BOOT_COUNTING |
+ EFI_LOADER_FEATURE_XBOOTLDR |
0;
_cleanup_freepool_ CHAR16 *infostr = NULL, *typestr = NULL;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#ifndef UINT64_C
+# define UINT64_C(c) (c ## ULL)
+#endif
+
+#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT (UINT64_C(1) << 0)
+#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT (UINT64_C(1) << 1)
+#define EFI_LOADER_FEATURE_ENTRY_DEFAULT (UINT64_C(1) << 2)
+#define EFI_LOADER_FEATURE_ENTRY_ONESHOT (UINT64_C(1) << 3)
+#define EFI_LOADER_FEATURE_BOOT_COUNTING (UINT64_C(1) << 4)
+#define EFI_LOADER_FEATURE_XBOOTLDR (UINT64_C(1) << 5)
#include "alloc-util.h"
#include "busctl-introspect.h"
+#include "path-util.h"
#include "string-util.h"
#include "util.h"
#include "xml.h"
node_path = TAKE_PTR(name);
else {
- if (endswith(prefix, "/"))
- node_path = strappend(prefix, name);
- else
- node_path = strjoin(prefix, "/", name);
+ node_path = path_join(prefix, name);
if (!node_path)
return log_oom();
}
controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
if (p) {
- j = strjoin(root, "/", p);
+ j = path_join(root, p);
if (!j)
return log_oom();
if (r == 0)
break;
- p = strjoin(path, "/", fn);
+ p = path_join(path, fn);
if (!p)
return -ENOMEM;
return 0;
}
+static int add_instructions_for_ip_any(
+ BPFProgram *p,
+ int verdict) {
+ int r;
+
+ assert(p);
+
+ struct bpf_insn insn[] = {
+ BPF_ALU32_IMM(BPF_OR, BPF_REG_8, verdict),
+ };
+
+ r = bpf_program_add_instructions(p, insn, 1);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
static int bpf_firewall_compile_bpf(
Unit *u,
bool is_ingress,
- BPFProgram **ret) {
+ BPFProgram **ret,
+ bool ip_allow_any,
+ bool ip_deny_any) {
struct bpf_insn pre_insn[] = {
/*
u->ipv4_allow_map_fd >= 0 ||
u->ipv6_allow_map_fd >= 0 ||
u->ipv4_deny_map_fd >= 0 ||
- u->ipv6_deny_map_fd >= 0;
+ u->ipv6_deny_map_fd >= 0 ||
+ ip_allow_any ||
+ ip_deny_any;
if (accounting_map_fd < 0 && !access_enabled) {
*ret = NULL;
if (r < 0)
return r;
}
+
+ if (ip_allow_any) {
+ r = add_instructions_for_ip_any(p, ACCESS_ALLOWED);
+ if (r < 0)
+ return r;
+ }
+
+ if (ip_deny_any) {
+ r = add_instructions_for_ip_any(p, ACCESS_DENIED);
+ if (r < 0)
+ return r;
+ }
}
r = bpf_program_add_instructions(p, post_insn, ELEMENTSOF(post_insn));
Unit *u,
int verdict,
int *ret_ipv4_map_fd,
- int *ret_ipv6_map_fd) {
+ int *ret_ipv6_map_fd,
+ bool *ret_has_any) {
_cleanup_close_ int ipv4_map_fd = -1, ipv6_map_fd = -1;
size_t n_ipv4 = 0, n_ipv6 = 0;
+ IPAddressAccessItem *list;
Unit *p;
int r;
assert(ret_ipv4_map_fd);
assert(ret_ipv6_map_fd);
+ assert(ret_has_any);
for (p = u; p; p = UNIT_DEREF(p->slice)) {
CGroupContext *cc;
if (!cc)
continue;
- bpf_firewall_count_access_items(verdict == ACCESS_ALLOWED ? cc->ip_address_allow : cc->ip_address_deny, &n_ipv4, &n_ipv6);
+ list = verdict == ACCESS_ALLOWED ? cc->ip_address_allow : cc->ip_address_deny;
+
+ bpf_firewall_count_access_items(list, &n_ipv4, &n_ipv6);
+
+ /* Skip making the LPM trie map in cases where we are using "any" in order to hack around
+ * needing CAP_SYS_ADMIN for allocating LPM trie map. */
+ if (ip_address_access_item_is_any(list)) {
+ *ret_has_any = true;
+ return 0;
+ }
}
if (n_ipv4 > 0) {
return r;
}
- *ret_ipv4_map_fd = ipv4_map_fd;
- *ret_ipv6_map_fd = ipv6_map_fd;
-
- ipv4_map_fd = ipv6_map_fd = -1;
+ *ret_ipv4_map_fd = TAKE_FD(ipv4_map_fd);
+ *ret_ipv6_map_fd = TAKE_FD(ipv6_map_fd);
+ *ret_has_any = false;
return 0;
}
int bpf_firewall_compile(Unit *u) {
CGroupContext *cc;
int r, supported;
+ bool ip_allow_any = false, ip_deny_any = false;
assert(u);
* means that all configure IP access rules *will* take effect on processes, even though we never
* compile them for inner nodes. */
- r = bpf_firewall_prepare_access_maps(u, ACCESS_ALLOWED, &u->ipv4_allow_map_fd, &u->ipv6_allow_map_fd);
+ r = bpf_firewall_prepare_access_maps(u, ACCESS_ALLOWED, &u->ipv4_allow_map_fd, &u->ipv6_allow_map_fd, &ip_allow_any);
if (r < 0)
return log_unit_error_errno(u, r, "Preparation of eBPF allow maps failed: %m");
- r = bpf_firewall_prepare_access_maps(u, ACCESS_DENIED, &u->ipv4_deny_map_fd, &u->ipv6_deny_map_fd);
+ r = bpf_firewall_prepare_access_maps(u, ACCESS_DENIED, &u->ipv4_deny_map_fd, &u->ipv6_deny_map_fd, &ip_deny_any);
if (r < 0)
return log_unit_error_errno(u, r, "Preparation of eBPF deny maps failed: %m");
}
if (r < 0)
return log_unit_error_errno(u, r, "Preparation of eBPF accounting maps failed: %m");
- r = bpf_firewall_compile_bpf(u, true, &u->ip_bpf_ingress);
+ r = bpf_firewall_compile_bpf(u, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any);
if (r < 0)
return log_unit_error_errno(u, r, "Compilation for ingress BPF program failed: %m");
- r = bpf_firewall_compile_bpf(u, false, &u->ip_bpf_egress);
+ r = bpf_firewall_compile_bpf(u, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any);
if (r < 0)
return log_unit_error_errno(u, r, "Compilation for egress BPF program failed: %m");
return 0;
}
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(filter_prog_hash_ops, void, trivial_hash_func, trivial_compare_func, BPFProgram, bpf_program_unref);
+
+static int load_bpf_progs_from_fs_to_set(Unit *u, char **filter_paths, Set **set) {
+ char **bpf_fs_path;
+
+ set_clear(*set);
+
+ STRV_FOREACH(bpf_fs_path, filter_paths) {
+ _cleanup_free_ BPFProgram *prog = NULL;
+ int r;
+
+ r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &prog);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Can't allocate CGROUP SKB BPF program: %m");
+
+ r = bpf_program_load_from_bpf_fs(prog, *bpf_fs_path);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Loading of ingress BPF program %s failed: %m", *bpf_fs_path);
+
+ r = set_ensure_allocated(set, &filter_prog_hash_ops);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Can't allocate BPF program set: %m");
+
+ r = set_put(*set, prog);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Can't add program to BPF program set: %m");
+ TAKE_PTR(prog);
+ }
+
+ return 0;
+}
+
+int bpf_firewall_load_custom(Unit *u) {
+ CGroupContext *cc;
+ int r, supported;
+
+ assert(u);
+
+ cc = unit_get_cgroup_context(u);
+ if (!cc)
+ return 0;
+
+ if (!(cc->ip_filters_ingress || cc->ip_filters_egress))
+ return 0;
+
+ supported = bpf_firewall_supported();
+ if (supported < 0)
+ return supported;
+
+ if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI)
+ return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "BPF_F_ALLOW_MULTI not supported on this manager, cannot attach custom BPF programs.");
+
+ r = load_bpf_progs_from_fs_to_set(u, cc->ip_filters_ingress, &u->ip_bpf_custom_ingress);
+ if (r < 0)
+ return r;
+ r = load_bpf_progs_from_fs_to_set(u, cc->ip_filters_egress, &u->ip_bpf_custom_egress);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+static int attach_custom_bpf_progs(Unit *u, const char *path, int attach_type, Set **set, Set **set_installed) {
+ BPFProgram *prog;
+ Iterator i;
+ int r;
+
+ assert(u);
+
+ set_clear(*set_installed);
+
+ SET_FOREACH(prog, *set, i) {
+ r = bpf_program_cgroup_attach(prog, attach_type, path, BPF_F_ALLOW_MULTI);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Attaching custom egress BPF program to cgroup %s failed: %m", path);
+ /* Remember that these BPF programs are installed now. */
+ r = set_ensure_allocated(set_installed, &filter_prog_hash_ops);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Can't allocate BPF program set: %m");
+
+ r = set_put(*set_installed, prog);
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Can't add program to BPF program set: %m");
+ bpf_program_ref(prog);
+ }
+
+ return 0;
+}
+
int bpf_firewall_install(Unit *u) {
_cleanup_free_ char *path = NULL;
CGroupContext *cc;
log_unit_debug(u, "BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units.");
return -EOPNOTSUPP;
}
+ if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI &&
+ (!set_isempty(u->ip_bpf_custom_ingress) || !set_isempty(u->ip_bpf_custom_egress)))
+ return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "BPF_F_ALLOW_MULTI not supported on this manager, cannot attach custom BPF programs.");
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &path);
if (r < 0)
u->ip_bpf_ingress_installed = bpf_program_unref(u->ip_bpf_ingress_installed);
if (u->ip_bpf_egress) {
- r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, flags);
+ r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path,
+ flags | (set_isempty(u->ip_bpf_custom_egress) ? 0 : BPF_F_ALLOW_MULTI));
if (r < 0)
return log_unit_error_errno(u, r, "Attaching egress BPF program to cgroup %s failed: %m", path);
}
if (u->ip_bpf_ingress) {
- r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, flags);
+ r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path,
+ flags | (set_isempty(u->ip_bpf_custom_ingress) ? 0 : BPF_F_ALLOW_MULTI));
if (r < 0)
return log_unit_error_errno(u, r, "Attaching ingress BPF program to cgroup %s failed: %m", path);
u->ip_bpf_ingress_installed = bpf_program_ref(u->ip_bpf_ingress);
}
+ r = attach_custom_bpf_progs(u, path, BPF_CGROUP_INET_EGRESS, &u->ip_bpf_custom_egress, &u->ip_bpf_custom_egress_installed);
+ if (r < 0)
+ return r;
+
+ r = attach_custom_bpf_progs(u, path, BPF_CGROUP_INET_INGRESS, &u->ip_bpf_custom_ingress, &u->ip_bpf_custom_ingress_installed);
+ if (r < 0)
+ return r;
+
return 0;
}
_cleanup_(bpf_program_unrefp) BPFProgram *program = NULL;
static int supported = -1;
union bpf_attr attr;
- int fd, r;
+ int r;
- /* Checks whether BPF firewalling is supported. For this, we check five things:
+ /* Checks whether BPF firewalling is supported. For this, we check the following things:
*
- * a) whether we are privileged
- * b) whether the unified hierarchy is being used
- * c) the BPF implementation in the kernel supports BPF LPM TRIE maps, which we require
- * d) the BPF implementation in the kernel supports BPF_PROG_TYPE_CGROUP_SKB programs, which we require
- * e) the BPF implementation in the kernel supports the BPF_PROG_DETACH call, which we require
+ * - whether the unified hierarchy is being used
+ * - the BPF implementation in the kernel supports BPF_PROG_TYPE_CGROUP_SKB programs, which we require
+ * - the BPF implementation in the kernel supports the BPF_PROG_DETACH call, which we require
*/
-
if (supported >= 0)
return supported;
- if (geteuid() != 0) {
- bpf_firewall_unsupported_reason =
- log_debug_errno(SYNTHETIC_ERRNO(EACCES),
- "Not enough privileges, BPF firewalling is not supported.");
- return supported = BPF_FIREWALL_UNSUPPORTED;
- }
-
r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
if (r < 0)
return log_error_errno(r, "Can't determine whether the unified hierarchy is used: %m");
return supported = BPF_FIREWALL_UNSUPPORTED;
}
- fd = bpf_map_new(BPF_MAP_TYPE_LPM_TRIE,
- offsetof(struct bpf_lpm_trie_key, data) + sizeof(uint64_t),
- sizeof(uint64_t),
- 1,
- BPF_F_NO_PREALLOC);
- if (fd < 0) {
- bpf_firewall_unsupported_reason =
- log_debug_errno(fd, "Can't allocate BPF LPM TRIE map, BPF firewalling is not supported: %m");
- return supported = BPF_FIREWALL_UNSUPPORTED;
- }
-
- safe_close(fd);
-
r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &program);
if (r < 0) {
bpf_firewall_unsupported_reason =
int bpf_firewall_compile(Unit *u);
int bpf_firewall_install(Unit *u);
+int bpf_firewall_load_custom(Unit *u);
int bpf_firewall_read_accounting(int map_fd, uint64_t *ret_bytes, uint64_t *ret_packets);
int bpf_firewall_reset_accounting(int map_fd);
c->ip_address_allow = ip_address_access_free_all(c->ip_address_allow);
c->ip_address_deny = ip_address_access_free_all(c->ip_address_deny);
+
+ c->ip_filters_ingress = strv_free(c->ip_filters_ingress);
+ c->ip_filters_egress = strv_free(c->ip_filters_egress);
}
void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
CGroupBlockIODeviceWeight *w;
CGroupDeviceAllow *a;
IPAddressAccessItem *iaai;
+ char **path;
char u[FORMAT_TIMESPAN_MAX];
char v[FORMAT_TIMESPAN_MAX];
(void) in_addr_to_string(iaai->family, &iaai->address, &k);
fprintf(f, "%sIPAddressDeny=%s/%u\n", prefix, strnull(k), iaai->prefixlen);
}
+
+ STRV_FOREACH(path, c->ip_filters_ingress)
+ fprintf(f, "%sIPIngressFilterPath=%s\n", prefix, *path);
+
+ STRV_FOREACH(path, c->ip_filters_egress)
+ fprintf(f, "%sIPEgressFilterPath=%s\n", prefix, *path);
}
int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) {
if (bpf_firewall_compile(u) < 0)
return;
+ (void) bpf_firewall_load_custom(u);
(void) bpf_firewall_install(u);
}
if (c->ip_accounting ||
c->ip_address_allow ||
- c->ip_address_deny)
+ c->ip_address_deny ||
+ c->ip_filters_ingress ||
+ c->ip_filters_egress)
return true;
/* If any parent slice has an IP access list defined, it applies too */
c = unit_get_cgroup_context(u);
+ assert(c);
+
/* Figure out which controllers we need, based on the cgroup context object */
if (c->cpu_accounting)
if (!escaped)
return NULL;
- if (slice)
- return strjoin(u->manager->cgroup_root, "/", slice, "/",
- escaped);
- else
- return strjoin(u->manager->cgroup_root, "/", escaped);
+ return path_join(empty_to_root(u->manager->cgroup_root), slice, escaped);
}
int unit_set_cgroup_path(Unit *u, const char *path) {
if (set_isempty(pids))
return 0;
+ /* Load any custom firewall BPF programs here once to test if they are existing and actually loadable.
+ * Fail here early since later errors in the call chain unit_realize_cgroup to cgroup_context_apply are ignored. */
+ r = bpf_firewall_load_custom(u);
+ if (r < 0)
+ return r;
+
r = unit_realize_cgroup(u);
if (r < 0)
return r;
if (isempty(suffix_path))
p = u->cgroup_path;
else
- p = strjoina(u->cgroup_path, "/", suffix_path);
+ p = prefix_roota(u->cgroup_path, suffix_path);
delegated_mask = unit_get_delegate_mask(u);
is_root_slice = unit_has_name(u, SPECIAL_ROOT_SLICE);
r = cg_trim_everywhere(u->manager->cgroup_supported, u->cgroup_path, !is_root_slice);
- if (r < 0) {
- log_unit_debug_errno(u, r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path);
- return;
- }
+ if (r < 0)
+ /* One reason we could have failed here is, that the cgroup still contains a process.
+ * However, if the cgroup becomes removable at a later time, it might be removed when
+ * the containing slice is stopped. So even if we failed now, this unit shouldn't assume
+ * that the cgroup is still realized the next time it is started. Do not return early
+ * on error, continue cleanup. */
+ log_unit_full(u, r == -EBUSY ? LOG_DEBUG : LOG_WARNING, r, "Failed to destroy cgroup %s, ignoring: %m", u->cgroup_path);
if (is_root_slice)
return;
while ((r = cg_read_subgroup(d, &fn)) > 0) {
_cleanup_free_ char *p = NULL;
- p = strjoin(path, "/", fn);
+ p = path_join(path, fn);
free(fn);
if (!p)
LIST_HEAD(IPAddressAccessItem, ip_address_allow);
LIST_HEAD(IPAddressAccessItem, ip_address_deny);
+ char **ip_filters_ingress;
+ char **ip_filters_egress;
+
/* For legacy hierarchies */
uint64_t cpu_shares;
uint64_t startup_cpu_shares;
SD_BUS_PROPERTY("IPAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, ip_accounting), 0),
SD_BUS_PROPERTY("IPAddressAllow", "a(iayu)", property_get_ip_address_access, offsetof(CGroupContext, ip_address_allow), 0),
SD_BUS_PROPERTY("IPAddressDeny", "a(iayu)", property_get_ip_address_access, offsetof(CGroupContext, ip_address_deny), 0),
+ SD_BUS_PROPERTY("IPIngressFilterPath", "as", NULL, offsetof(CGroupContext, ip_filters_ingress), 0),
+ SD_BUS_PROPERTY("IPEgressFilterPath", "as", NULL, offsetof(CGroupContext, ip_filters_egress), 0),
SD_BUS_PROPERTY("DisableControllers", "as", property_get_cgroup_mask, offsetof(CGroupContext, disable_controllers), 0),
SD_BUS_VTABLE_END
};
}
}
+ return 1;
+ } else if (STR_IN_SET(name, "IPIngressFilterPath", "IPEgressFilterPath")) {
+ char ***filters;
+ size_t n = 0;
+
+ filters = streq(name, "IPIngressFilterPath") ? &c->ip_filters_ingress : &c->ip_filters_egress;
+ r = sd_bus_message_enter_container(message, 'a', "s");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const char *path;
+
+ r = sd_bus_message_read(message, "s", &path);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (!path_is_normalized(path) || !path_is_absolute(path))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "%s= expects a normalized absolute path.", name);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags) && !strv_contains(*filters, path)) {
+ r = strv_extend(filters, path);
+ if (r < 0)
+ return log_oom();
+ }
+ n++;
+ }
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
+ _cleanup_free_ char *buf = NULL;
+ _cleanup_fclose_ FILE *f = NULL;
+ char **entry;
+ size_t size = 0;
+
+ if (n == 0)
+ *filters = strv_free(*filters);
+
+ unit_invalidate_cgroup_bpf(u);
+ f = open_memstream_unlocked(&buf, &size);
+ if (!f)
+ return -ENOMEM;
+
+ fputs(name, f);
+ fputs("=\n", f);
+
+ STRV_FOREACH(entry, *filters)
+ fprintf(f, "%s=%s\n", name, *entry);
+
+ r = fflush_and_check(f);
+ if (r < 0)
+ return r;
+
+ unit_write_setting(u, flags, name, buf);
+
+ if (*filters) {
+ r = bpf_firewall_supported();
+ if (r < 0)
+ return r;
+ if (r != BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
+ static bool warned = false;
+
+ log_full(warned ? LOG_DEBUG : LOG_WARNING,
+ "Transient unit %s configures an IP firewall with BPF, but the local system does not support BPF/cgroup firewalling with mulitiple filters.\n"
+ "Starting this unit will fail! (This warning is only shown for the first started transient unit using IP firewalling.)", u->id);
+ warned = true;
+ }
+ }
+ }
+
return 1;
}
return sd_bus_message_append_array(reply, 'y', array, allocated);
}
+static int property_get_numa_mask(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ ExecContext *c = userdata;
+ _cleanup_free_ uint8_t *array = NULL;
+ size_t allocated;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ (void) cpu_set_to_dbus(&c->numa_policy.nodes, &array, &allocated);
+
+ return sd_bus_message_append_array(reply, 'y', array, allocated);
+}
+
+static int property_get_numa_policy(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+ ExecContext *c = userdata;
+ int32_t policy;
+
+ assert(bus);
+ assert(reply);
+ assert(c);
+
+ policy = numa_policy_get_type(&c->numa_policy);
+
+ return sd_bus_message_append_basic(reply, 'i', &policy);
+}
+
static int property_get_timer_slack_nsec(
sd_bus *bus,
const char *path,
SD_BUS_PROPERTY("CPUSchedulingPolicy", "i", property_get_cpu_sched_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CPUSchedulingPriority", "i", property_get_cpu_sched_priority, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CPUAffinity", "ay", property_get_cpu_affinity, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("NUMAPolicy", "i", property_get_numa_policy, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("NUMAMask", "ay", property_get_numa_mask, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimerSlackNSec", "t", property_get_timer_slack_nsec, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CPUSchedulingResetOnFork", "b", bus_property_get_bool, offsetof(ExecContext, cpu_sched_reset_on_fork), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NonBlocking", "b", bus_property_get_bool, offsetof(ExecContext, non_blocking), SD_BUS_VTABLE_PROPERTY_CONST),
return sd_bus_message_close_container(reply);
}
+static int append_exec_ex_command(sd_bus_message *reply, ExecCommand *c) {
+ _cleanup_strv_free_ char **ex_opts = NULL;
+ int r;
+
+ assert(reply);
+ assert(c);
+
+ if (!c->path)
+ return 0;
+
+ r = sd_bus_message_open_container(reply, 'r', "sasasttttuii");
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "s", c->path);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_strv(reply, c->argv);
+ if (r < 0)
+ return r;
+
+ r = exec_command_flags_to_strv(c->flags, &ex_opts);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append_strv(reply, ex_opts);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(reply, "ttttuii",
+ c->exec_status.start_timestamp.realtime,
+ c->exec_status.start_timestamp.monotonic,
+ c->exec_status.exit_timestamp.realtime,
+ c->exec_status.exit_timestamp.monotonic,
+ (uint32_t) c->exec_status.pid,
+ (int32_t) c->exec_status.code,
+ (int32_t) c->exec_status.status);
+ if (r < 0)
+ return r;
+
+ return sd_bus_message_close_container(reply);
+}
+
int bus_property_get_exec_command(
sd_bus *bus,
const char *path,
return sd_bus_message_close_container(reply);
}
+int bus_property_get_exec_ex_command_list(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *ret_error) {
+
+ ExecCommand *c, *exec_command = *(ExecCommand**) userdata;
+ int r;
+
+ assert(bus);
+ assert(reply);
+
+ r = sd_bus_message_open_container(reply, 'a', "(sasasttttuii)");
+ if (r < 0)
+ return r;
+
+ LIST_FOREACH(command, c, exec_command) {
+ r = append_exec_ex_command(reply, c);
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
+static char *exec_command_flags_to_exec_chars(ExecCommandFlags flags) {
+ char *res = NULL;
+
+ asprintf(&res, "%s%s%s%s%s",
+ FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE) ? "-" : "",
+ FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND) ? ":" : "",
+ FLAGS_SET(flags, EXEC_COMMAND_FULLY_PRIVILEGED) ? "+" : "",
+ FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID) ? "!" : "",
+ FLAGS_SET(flags, EXEC_COMMAND_AMBIENT_MAGIC) ? "!!" : "");
+
+ return res;
+}
+
int bus_set_transient_exec_command(
Unit *u,
const char *name,
sd_bus_message *message,
UnitWriteFlags flags,
sd_bus_error *error) {
+ bool is_ex_prop = endswith(name, "Ex");
unsigned n = 0;
int r;
- r = sd_bus_message_enter_container(message, 'a', "(sasb)");
+ r = sd_bus_message_enter_container(message, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
if (r < 0)
return r;
- while ((r = sd_bus_message_enter_container(message, 'r', "sasb")) > 0) {
- _cleanup_strv_free_ char **argv = NULL;
+ while ((r = sd_bus_message_enter_container(message, 'r', is_ex_prop ? "sasas" : "sasb")) > 0) {
+ _cleanup_strv_free_ char **argv = NULL, **ex_opts = NULL;
const char *path;
int b;
if (r < 0)
return r;
- r = sd_bus_message_read(message, "b", &b);
+ r = is_ex_prop ? sd_bus_message_read_strv(message, &ex_opts) : sd_bus_message_read(message, "b", &b);
if (r < 0)
return r;
c->argv = TAKE_PTR(argv);
- c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0;
+ if (is_ex_prop) {
+ r = exec_command_flags_from_strv(ex_opts, &c->flags);
+ if (r < 0)
+ return r;
+ } else
+ c->flags = b ? EXEC_COMMAND_IGNORE_FAILURE : 0;
path_simplify(c->path, false);
exec_command_append_list(exec_command, c);
fputs("ExecStart=\n", f);
LIST_FOREACH(command, c, *exec_command) {
- _cleanup_free_ char *a = NULL, *t = NULL;
+ _cleanup_free_ char *a = NULL, *t = NULL, *exec_chars = NULL;
const char *p;
p = unit_escape_setting(c->path, UNIT_ESCAPE_C|UNIT_ESCAPE_SPECIFIERS, &t);
if (!a)
return -ENOMEM;
- fprintf(f, "%s=%s@%s %s\n",
- name,
- c->flags & EXEC_COMMAND_IGNORE_FAILURE ? "-" : "",
- p,
- a);
+ exec_chars = exec_command_flags_to_exec_chars(c->flags);
+ if (!exec_chars)
+ return -ENOMEM;
+
+ fprintf(f, "%s=%s@%s %s\n", name, exec_chars, p, a);
}
r = fflush_and_check(f);
return 1;
}
#endif
- if (streq(name, "CPUAffinity")) {
+ if (STR_IN_SET(name, "CPUAffinity", "NUMAMask")) {
const void *a;
size_t n;
+ bool affinity = streq(name, "CPUAffinity");
_cleanup_(cpu_set_reset) CPUSet set = {};
r = sd_bus_message_read_array(message, 'y', &a, &n);
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (n == 0) {
- cpu_set_reset(&c->cpu_set);
+ cpu_set_reset(affinity ? &c->cpu_set : &c->numa_policy.nodes);
unit_write_settingf(u, flags, name, "%s=", name);
} else {
_cleanup_free_ char *str = NULL;
/* We forego any optimizations here, and always create the structure using
* cpu_set_add_all(), because we don't want to care if the existing size we
* got over dbus is appropriate. */
- r = cpu_set_add_all(&c->cpu_set, &set);
+ r = cpu_set_add_all(affinity ? &c->cpu_set : &c->numa_policy.nodes, &set);
if (r < 0)
return r;
return 1;
+ } else if (streq(name, "NUMAPolicy")) {
+ int32_t type;
+
+ r = sd_bus_message_read(message, "i", &type);
+ if (r < 0)
+ return r;
+
+ if (!mpol_is_valid(type))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid NUMAPolicy value: %i", type);
+
+ if (!UNIT_WRITE_FLAGS_NOOP(flags))
+ c->numa_policy.type = type;
+
+ return 1;
} else if (streq(name, "Nice")) {
int32_t q;
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not absolute.", path);
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
- _cleanup_free_ char *q = NULL;
- char *buf;
+ _cleanup_free_ char *q = NULL, *buf = NULL;
buf = strjoin(b ? "-" : "", path);
if (!buf)
return -ENOMEM;
q = specifier_escape(buf);
- if (!q) {
- free(buf);
+ if (!q)
return -ENOMEM;
- }
fprintf(f, "EnvironmentFile=%s\n", q);
- r = strv_consume(&l, buf);
+ r = strv_consume(&l, TAKE_PTR(buf));
if (r < 0)
return r;
}
#define BUS_EXEC_COMMAND_LIST_VTABLE(name, offset, flags) \
SD_BUS_PROPERTY(name, "a(sasbttttuii)", bus_property_get_exec_command_list, offset, flags)
+#define BUS_EXEC_EX_COMMAND_LIST_VTABLE(name, offset, flags) \
+ SD_BUS_PROPERTY(name, "a(sasasttttuii)", bus_property_get_exec_ex_command_list, offset, flags)
+
extern const sd_bus_vtable bus_exec_vtable[];
int bus_property_get_exec_output(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
int bus_property_get_exec_command(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
int bus_property_get_exec_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
+int bus_property_get_exec_ex_command_list(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *ret_error);
int bus_exec_context_set_transient_property(Unit *u, ExecContext *c, const char *name, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
int bus_set_transient_exec_command(Unit *u, const char *name, ExecCommand **exec_command, sd_bus_message *message, UnitWriteFlags flags, sd_bus_error *error);
return method_generic_unit_operation(message, userdata, error, bus_unit_method_kill, 0);
}
+static int method_clean_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ /* Load the unit if necessary, in order to load it, and insist on the unit being loaded to be
+ * cleaned */
+ return method_generic_unit_operation(message, userdata, error, bus_unit_method_clean, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED);
+}
+
static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
/* Don't load the unit (because unloaded units can't be in failed state), and don't insist on the
* unit to be loaded properly (since a failed unit might have its unit file disappeared) */
unit_path,
u->job ? u->job->id : 0,
u->job ? job_type_to_string(u->job->type) : "",
- job_path ? job_path : "/");
+ empty_to_root(job_path));
}
static int method_list_units_by_names(sd_bus_message *message, void *userdata, sd_bus_error *error) {
SD_BUS_METHOD("ReloadOrTryRestartUnit", "ss", "o", method_reload_or_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("EnqueueUnitJob", "sss", "uososa(uosos)", method_enqueue_unit_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("CleanUnit", "sas", NULL, method_clean_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("RefUnit", "s", NULL, method_ref_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_PROPERTY("TimeoutStartUSec", "t", bus_property_get_usec, offsetof(Service, timeout_start_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimeoutStopUSec", "t", bus_property_get_usec, offsetof(Service, timeout_stop_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TimeoutAbortUSec", "t", property_get_timeout_abort_usec, 0, 0),
+ SD_BUS_PROPERTY("TimeoutCleanUSec", "t", bus_property_get_usec, offsetof(Service, timeout_clean_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RuntimeMaxUSec", "t", bus_property_get_usec, offsetof(Service, runtime_max_usec), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("WatchdogUSec", "t", bus_property_get_usec, offsetof(Service, watchdog_usec), SD_BUS_VTABLE_PROPERTY_CONST),
BUS_PROPERTY_DUAL_TIMESTAMP("WatchdogTimestamp", offsetof(Service, watchdog_timestamp), 0),
SD_BUS_PROPERTY("StatusErrno", "i", bus_property_get_int, offsetof(Service, status_errno), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Result", "s", property_get_result, offsetof(Service, result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("ReloadResult", "s", property_get_result, offsetof(Service, reload_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("CleanResult", "s", property_get_result, offsetof(Service, clean_result), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("USBFunctionDescriptors", "s", NULL, offsetof(Service, usb_function_descriptors), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("USBFunctionStrings", "s", NULL, offsetof(Service, usb_function_strings), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UID", "u", bus_property_get_uid, offsetof(Unit, ref_uid), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPre", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+ BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStartPreEx", offsetof(Service, exec_command[SERVICE_EXEC_START_PRE]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStart", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+ BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStartEx", offsetof(Service, exec_command[SERVICE_EXEC_START]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStartPost", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
+ BUS_EXEC_EX_COMMAND_LIST_VTABLE("ExecStartPostEx", offsetof(Service, exec_command[SERVICE_EXEC_START_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecReload", offsetof(Service, exec_command[SERVICE_EXEC_RELOAD]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStop", offsetof(Service, exec_command[SERVICE_EXEC_STOP]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
BUS_EXEC_COMMAND_LIST_VTABLE("ExecStopPost", offsetof(Service, exec_command[SERVICE_EXEC_STOP_POST]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
if (e) {
char *z;
- z = strjoin("/run/", e);
+ z = path_join("/run", e);
if (!z)
return log_oom();
return bus_set_transient_exit_status(u, name, &s->success_status, message, flags, error);
ci = service_exec_command_from_string(name);
+ ci = (ci >= 0) ? ci : service_exec_ex_command_from_string(name);
if (ci >= 0)
return bus_set_transient_exec_command(u, name, &s->exec_command[ci], message, flags, error);
static BUS_DEFINE_PROPERTY_GET(property_get_need_daemon_reload, "b", Unit, unit_need_daemon_reload);
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_strv, "as", 0);
+static int property_get_can_clean(
+ sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *error) {
+
+ Unit *u = userdata;
+ ExecCleanMask mask;
+ int r;
+
+ assert(bus);
+ assert(reply);
+
+ r = unit_can_clean(u, &mask);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_open_container(reply, 'a', "s");
+ if (r < 0)
+ return r;
+
+ for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
+ if (!FLAGS_SET(mask, 1U << t))
+ continue;
+
+ r = sd_bus_message_append(reply, "s", exec_resource_type_to_string(t));
+ if (r < 0)
+ return r;
+ }
+
+ return sd_bus_message_close_container(reply);
+}
+
static int property_get_names(
sd_bus *bus,
const char *path,
return sd_bus_reply_method_return(message, NULL);
}
+int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ ExecCleanMask mask = 0;
+ Unit *u = userdata;
+ int r;
+
+ assert(message);
+ assert(u);
+
+ r = mac_selinux_unit_access_check(u, message, "stop", error);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_enter_container(message, 'a', "s");
+ if (r < 0)
+ return r;
+
+ for (;;) {
+ const char *i;
+
+ r = sd_bus_message_read(message, "s", &i);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+
+ if (streq(i, "all"))
+ mask |= EXEC_CLEAN_ALL;
+ else {
+ ExecDirectoryType t;
+
+ t = exec_resource_type_from_string(i);
+ if (t < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid resource type: %s", i);
+
+ mask |= 1U << t;
+ }
+ }
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ r = bus_verify_manage_units_async_full(
+ u,
+ "clean",
+ CAP_DAC_OVERRIDE,
+ N_("Authentication is required to delete files and directories associated with '$(unit)'."),
+ true,
+ message,
+ error);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
+
+ r = unit_clean(u, mask);
+ if (r == -EOPNOTSUPP)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not supporting cleaning.", u->id);
+ if (r == -EUNATCH)
+ return sd_bus_error_setf(error, BUS_ERROR_NOTHING_TO_CLEAN, "No matching resources found.");
+ if (r == -EBUSY)
+ return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit is not inactive or has pending job.");
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
static int property_get_refs(
sd_bus *bus,
const char *path,
SD_BUS_PROPERTY("CanStop", "b", property_get_can_stop, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("CanClean", "as", property_get_can_clean, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Job", "(uo)", property_get_job, offsetof(Unit, job), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Ref", NULL, NULL, bus_unit_method_ref, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("Unref", NULL, NULL, bus_unit_method_unref, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("Clean", "as", NULL, bus_unit_method_clean, SD_BUS_VTABLE_UNPRIVILEGED),
/* For dependency types we don't support anymore always return an empty array */
SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN),
if (r == 0)
break;
- j = strjoin(p, "/", g);
+ j = path_join(p, g);
if (!j)
return -ENOMEM;
int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error *error);
typedef enum BusUnitQueueFlags {
BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE = 1 << 0,
for (;;) {
_cleanup_free_ char *word = NULL, *k = NULL;
- r = extract_first_word(&wants, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&wants, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
for (;;) {
_cleanup_free_ char *word = NULL;
- r = extract_first_word(&alias, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&alias, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
(type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode != EXEC_PRESERVE_NO))) {
_cleanup_free_ char *private_root = NULL;
- /* So, here's one extra complication when dealing with DynamicUser=1 units. In that case we
- * want to avoid leaving a directory around fully accessible that is owned by a dynamic user
- * whose UID is later on reused. To lock this down we use the same trick used by container
- * managers to prohibit host users to get access to files of the same UID in containers: we
- * place everything inside a directory that has an access mode of 0700 and is owned root:root,
- * so that it acts as security boundary for unprivileged host code. We then use fs namespacing
- * to make this directory permeable for the service itself.
+ /* So, here's one extra complication when dealing with DynamicUser=1 units. In that
+ * case we want to avoid leaving a directory around fully accessible that is owned by
+ * a dynamic user whose UID is later on reused. To lock this down we use the same
+ * trick used by container managers to prohibit host users to get access to files of
+ * the same UID in containers: we place everything inside a directory that has an
+ * access mode of 0700 and is owned root:root, so that it acts as security boundary
+ * for unprivileged host code. We then use fs namespacing to make this directory
+ * permeable for the service itself.
*
- * Specifically: for a service which wants a special directory "foo/" we first create a
- * directory "private/" with access mode 0700 owned by root:root. Then we place "foo" inside of
- * that directory (i.e. "private/foo/"), and make "foo" a symlink to "private/foo". This way,
- * privileged host users can access "foo/" as usual, but unprivileged host users can't look
- * into it. Inside of the namespaceof the container "private/" is replaced by a more liberally
- * accessible tmpfs, into which the host's "private/foo/" is mounted under the same name, thus
- * disabling the access boundary for the service and making sure it only gets access to the
- * dirs it needs but no others. Tricky? Yes, absolutely, but it works!
+ * Specifically: for a service which wants a special directory "foo/" we first create
+ * a directory "private/" with access mode 0700 owned by root:root. Then we place
+ * "foo" inside of that directory (i.e. "private/foo/"), and make "foo" a symlink to
+ * "private/foo". This way, privileged host users can access "foo/" as usual, but
+ * unprivileged host users can't look into it. Inside of the namespace of the unit
+ * "private/" is replaced by a more liberally accessible tmpfs, into which the host's
+ * "private/foo/" is mounted under the same name, thus disabling the access boundary
+ * for the service and making sure it only gets access to the dirs it needs but no
+ * others. Tricky? Yes, absolutely, but it works!
*
- * Note that we don't do this for EXEC_DIRECTORY_CONFIGURATION as that's assumed not to be
- * owned by the service itself.
- * Also, note that we don't do this for EXEC_DIRECTORY_RUNTIME as that's often used for sharing
- * files or sockets with other services. */
+ * Note that we don't do this for EXEC_DIRECTORY_CONFIGURATION as that's assumed not
+ * to be owned by the service itself.
+ *
+ * Also, note that we don't do this for EXEC_DIRECTORY_RUNTIME as that's often used
+ * for sharing files or sockets with other services. */
private_root = path_join(params->prefix[type], "private");
if (!private_root) {
* it over. Most likely the service has been upgraded from one that didn't use
* DynamicUser=1, to one that does. */
+ log_info("Found pre-existing public %s= directory %s, migrating to %s.\n"
+ "Apparently, service previously had DynamicUser= turned off, and has now turned it on.",
+ exec_directory_type_to_string(type), p, pp);
+
if (rename(p, pp) < 0) {
r = -errno;
goto fail;
goto fail;
} else {
+ _cleanup_free_ char *target = NULL;
+
+ if (type != EXEC_DIRECTORY_CONFIGURATION &&
+ readlink_and_make_absolute(p, &target) >= 0) {
+ _cleanup_free_ char *q = NULL;
+
+ /* This already exists and is a symlink? Interesting. Maybe it's one created
+ * by DynamicUser=1 (see above)?
+ *
+ * We do this for all directory types except for ConfigurationDirectory=,
+ * since they all support the private/ symlink logic at least in some
+ * configurations, see above. */
+
+ q = path_join(params->prefix[type], "private", *rt);
+ if (!q) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (path_equal(q, target)) {
+
+ /* Hmm, apparently DynamicUser= was once turned on for this service,
+ * but is no longer. Let's move the directory back up. */
+
+ log_info("Found pre-existing private %s= directory %s, migrating to %s.\n"
+ "Apparently, service previously had DynamicUser= turned on, and has now turned it off.",
+ exec_directory_type_to_string(type), q, p);
+
+ if (unlink(p) < 0) {
+ r = -errno;
+ goto fail;
+ }
+
+ if (rename(q, p) < 0) {
+ r = -errno;
+ goto fail;
+ }
+ }
+ }
+
r = mkdir_label(p, context->directories[type].mode);
if (r < 0) {
if (r != -EEXIST)
* directory. For that we overmount the usually inaccessible "private" subdirectory with a
* tmpfs that makes it accessible and is empty except for the submounts we do this for. */
- private_root = strjoin(params->prefix[t], "/private");
+ private_root = path_join(params->prefix[t], "private");
if (!private_root) {
r = -ENOMEM;
goto finish;
if (context->dynamic_user &&
!IN_SET(t, EXEC_DIRECTORY_RUNTIME, EXEC_DIRECTORY_CONFIGURATION))
- s = strjoin(params->prefix[t], "/private/", *suffix);
+ s = path_join(params->prefix[t], "private", *suffix);
else
- s = strjoin(params->prefix[t], "/", *suffix);
+ s = path_join(params->prefix[t], *suffix);
if (!s) {
r = -ENOMEM;
goto finish;
/* When RootDirectory= or RootImage= are set, then the symbolic link to the private
* directory is not created on the root directory. So, let's bind-mount the directory
* on the 'non-private' place. */
- d = strjoin(params->prefix[t], "/", *suffix);
+ d = path_join(params->prefix[t], *suffix);
else
d = strdup(s);
if (!d) {
const ExecContext *context,
const ExecParameters *params,
const char *home,
- const bool needs_mount_ns,
int *exit_status) {
const char *d, *wd;
else
wd = "/";
- if (params->flags & EXEC_APPLY_CHROOT) {
- if (!needs_mount_ns && context->root_directory)
- if (chroot(context->root_directory) < 0) {
- *exit_status = EXIT_CHROOT;
- return -errno;
- }
-
+ if (params->flags & EXEC_APPLY_CHROOT)
d = wd;
- } else
+ else
d = prefix_roota(context->root_directory, wd);
if (chdir(d) < 0 && !context->working_directory_missing_ok) {
return 0;
}
+static int apply_root_directory(
+ const ExecContext *context,
+ const ExecParameters *params,
+ const bool needs_mount_ns,
+ int *exit_status) {
+
+ assert(context);
+ assert(exit_status);
+
+ if (params->flags & EXEC_APPLY_CHROOT) {
+ if (!needs_mount_ns && context->root_directory)
+ if (chroot(context->root_directory) < 0) {
+ *exit_status = EXIT_CHROOT;
+ return -errno;
+ }
+ }
+
+ return 0;
+}
+
static int setup_keyring(
const Unit *u,
const ExecContext *context,
char *e;
if (t == EXEC_DIRECTORY_RUNTIME)
- e = strjoin(p->prefix[t], "/", *i);
+ e = path_join(p->prefix[t], *i);
else
- e = strjoin(p->prefix[t], "/private/", *i);
+ e = path_join(p->prefix[t], "private", *i);
if (!e)
return -ENOMEM;
using_subcgroup = FLAGS_SET(params->flags, EXEC_CONTROL_CGROUP|EXEC_CGROUP_DELEGATE|EXEC_IS_CONTROL);
if (using_subcgroup)
- p = strjoin(params->cgroup_path, "/.control");
+ p = path_join(params->cgroup_path, ".control");
else
p = strdup(params->cgroup_path);
if (!p)
return log_unit_error_errno(unit, errno, "Failed to set up CPU affinity: %m");
}
+ if (mpol_is_valid(numa_policy_get_type(&context->numa_policy))) {
+ r = apply_numa_policy(&context->numa_policy);
+ if (r == -EOPNOTSUPP)
+ log_unit_debug_errno(unit, r, "NUMA support not available, ignoring.");
+ else if (r < 0) {
+ *exit_status = EXIT_NUMA_POLICY;
+ return log_unit_error_errno(unit, r, "Failed to set NUMA memory policy: %m");
+ }
+ }
+
if (context->ioprio_set)
if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) {
*exit_status = EXIT_IOPRIO;
}
}
+ /* chroot to root directory first, before we lose the ability to chroot */
+ r = apply_root_directory(context, params, needs_mount_namespace, exit_status);
+ if (r < 0)
+ return log_unit_error_errno(unit, r, "Chrooting to the requested root directory failed: %m");
+
if (needs_setuid) {
if (uid_is_valid(uid)) {
r = enforce_user(context, uid);
/* Apply working directory here, because the working directory might be on NFS and only the user running
* this service might have the correct privilege to change to the working directory */
- r = apply_working_directory(context, params, home, needs_mount_namespace, exit_status);
+ r = apply_working_directory(context, params, home, exit_status);
if (r < 0)
return log_unit_error_errno(unit, r, "Changing to the requested working directory failed: %m");
assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL;
c->log_level_max = -1;
+ numa_policy_reset(&c->numa_policy);
}
void exec_context_done(ExecContext *c) {
c->n_temporary_filesystems = 0;
cpu_set_reset(&c->cpu_set);
+ numa_policy_reset(&c->numa_policy);
c->utmp_id = mfree(c->utmp_id);
c->selinux_context = mfree(c->selinux_context);
fprintf(f, "%sCPUAffinity: %s\n", prefix, affinity);
}
+ if (mpol_is_valid(numa_policy_get_type(&c->numa_policy))) {
+ _cleanup_free_ char *nodes = NULL;
+
+ nodes = cpu_set_to_range_string(&c->numa_policy.nodes);
+ fprintf(f, "%sNUMAPolicy: %s\n", prefix, mpol_to_string(numa_policy_get_type(&c->numa_policy)));
+ fprintf(f, "%sNUMAMask: %s\n", prefix, strnull(nodes));
+ }
+
if (c->timer_slack_nsec != NSEC_INFINITY)
fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
}
}
+int exec_context_get_clean_directories(
+ ExecContext *c,
+ char **prefix,
+ ExecCleanMask mask,
+ char ***ret) {
+
+ _cleanup_strv_free_ char **l = NULL;
+ ExecDirectoryType t;
+ int r;
+
+ assert(c);
+ assert(prefix);
+ assert(ret);
+
+ for (t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++) {
+ char **i;
+
+ if (!FLAGS_SET(mask, 1U << t))
+ continue;
+
+ if (!prefix[t])
+ continue;
+
+ STRV_FOREACH(i, c->directories[t].paths) {
+ char *j;
+
+ j = path_join(prefix[t], *i);
+ if (!j)
+ return -ENOMEM;
+
+ r = strv_consume(&l, j);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ *ret = TAKE_PTR(l);
+ return 0;
+}
+
+int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret) {
+ ExecCleanMask mask = 0;
+
+ assert(c);
+ assert(ret);
+
+ for (ExecDirectoryType t = 0; t < _EXEC_DIRECTORY_TYPE_MAX; t++)
+ if (!strv_isempty(c->directories[t].paths))
+ mask |= 1U << t;
+
+ *ret = mask;
+ return 0;
+}
+
void exec_status_start(ExecStatus *s, pid_t pid) {
assert(s);
cmd = exec_command_line(c->argv);
fprintf(f,
"%sCommand Line: %s\n",
- prefix, cmd ? cmd : strerror(ENOMEM));
+ prefix, cmd ? cmd : strerror_safe(ENOMEM));
exec_status_dump(&c->exec_status, f, prefix2);
}
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(exec_preserve_mode, ExecPreserveMode, EXEC_PRESERVE_YES);
+/* This table maps ExecDirectoryType to the setting it is configured with in the unit */
static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
[EXEC_DIRECTORY_RUNTIME] = "RuntimeDirectory",
[EXEC_DIRECTORY_STATE] = "StateDirectory",
DEFINE_STRING_TABLE_LOOKUP(exec_directory_type, ExecDirectoryType);
+/* And this table maps ExecDirectoryType too, but to a generic term identifying the type of resource. This
+ * one is supposed to be generic enough to be used for unit types that don't use ExecContext and per-unit
+ * directories, specifically .timer units with their timestamp touch file. */
+static const char* const exec_resource_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
+ [EXEC_DIRECTORY_RUNTIME] = "runtime",
+ [EXEC_DIRECTORY_STATE] = "state",
+ [EXEC_DIRECTORY_CACHE] = "cache",
+ [EXEC_DIRECTORY_LOGS] = "logs",
+ [EXEC_DIRECTORY_CONFIGURATION] = "configuration",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(exec_resource_type, ExecDirectoryType);
+
+/* And this table also maps ExecDirectoryType, to the environment variable we pass the selected directory to
+ * the service payload in. */
static const char* const exec_directory_env_name_table[_EXEC_DIRECTORY_TYPE_MAX] = {
[EXEC_DIRECTORY_RUNTIME] = "RUNTIME_DIRECTORY",
[EXEC_DIRECTORY_STATE] = "STATE_DIRECTORY",
#include "cgroup-util.h"
#include "cpu-set-util.h"
+#include "exec-util.h"
#include "fdset.h"
#include "list.h"
#include "missing_resource.h"
int status; /* as in sigingo_t::si_status */
};
-typedef enum ExecCommandFlags {
- EXEC_COMMAND_IGNORE_FAILURE = 1 << 0,
- EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1,
- EXEC_COMMAND_NO_SETUID = 1 << 2,
- EXEC_COMMAND_AMBIENT_MAGIC = 1 << 3,
- EXEC_COMMAND_NO_ENV_EXPAND = 1 << 4,
-} ExecCommandFlags;
-
/* Stores information about commands we execute. Covers both configuration settings as well as runtime data. */
struct ExecCommand {
char *path;
mode_t mode;
} ExecDirectory;
+typedef enum ExecCleanMask {
+ /* In case you wonder why the bitmask below doesn't use "directory" in its name: we want to keep this
+ * generic so that .timer timestamp files can nicely be covered by this too, and similar. */
+ EXEC_CLEAN_RUNTIME = 1U << EXEC_DIRECTORY_RUNTIME,
+ EXEC_CLEAN_STATE = 1U << EXEC_DIRECTORY_STATE,
+ EXEC_CLEAN_CACHE = 1U << EXEC_DIRECTORY_CACHE,
+ EXEC_CLEAN_LOGS = 1U << EXEC_DIRECTORY_LOGS,
+ EXEC_CLEAN_CONFIGURATION = 1U << EXEC_DIRECTORY_CONFIGURATION,
+ EXEC_CLEAN_NONE = 0,
+ EXEC_CLEAN_ALL = (1U << _EXEC_DIRECTORY_TYPE_MAX) - 1,
+ _EXEC_CLEAN_MASK_INVALID = -1,
+} ExecCleanMask;
+
/* Encodes configuration parameters applied to invoked commands. Does not carry runtime data, but only configuration
* changes sourced from unit files and suchlike. ExecContext objects are usually embedded into Unit objects, and do not
* change after being loaded. */
int cpu_sched_priority;
CPUSet cpu_set;
+ NUMAPolicy numa_policy;
ExecInput std_input;
ExecOutput std_output;
void exec_context_revert_tty(ExecContext *c);
+int exec_context_get_clean_directories(ExecContext *c, char **prefix, ExecCleanMask mask, char ***ret);
+int exec_context_get_clean_mask(ExecContext *c, ExecCleanMask *ret);
+
void exec_status_start(ExecStatus *s, pid_t pid);
void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int code, int status);
void exec_status_dump(const ExecStatus *s, FILE *f, const char *prefix);
const char* exec_directory_type_to_string(ExecDirectoryType i) _const_;
ExecDirectoryType exec_directory_type_from_string(const char *s) _pure_;
+
+const char* exec_resource_type_to_string(ExecDirectoryType i) _const_;
+ExecDirectoryType exec_resource_type_from_string(const char *s) _pure_;
return first;
}
+
+bool ip_address_access_item_is_any(IPAddressAccessItem *first) {
+ /* Check for exactly two entries */
+ if (!first || !first->items_next || first->items_next->items_next)
+ return false;
+
+ /* Check both entries cover the full range */
+ if (first->prefixlen != 0 || first->items_next->prefixlen != 0)
+ return false;
+
+ /* Check that one of them is the IPv4 and the other IPv6 */
+ if (!((first->family == AF_INET && first->items_next->family == AF_INET6) ||
+ (first->family == AF_INET6 && first->items_next->family == AF_INET)))
+ return false;
+
+ /* No need to check the actual addresses, they don't matter if the prefix is zero */
+ return true;
+}
IPAddressAccessItem* ip_address_access_free_all(IPAddressAccessItem *first);
IPAddressAccessItem* ip_address_access_reduce(IPAddressAccessItem *first);
+
+/* Returns true if a list consists of only the two items necessary for "any"
+ * (0.0.0.0/0 and ::/0). */
+bool ip_address_access_item_is_any(IPAddressAccessItem *first);
if (j->type == JOB_NOP)
return true;
- if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) {
- /* Immediate result is that the job is or might be
- * started. In this case let's wait for the
- * dependencies, regardless whether they are
- * starting or stopping something. */
-
- HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i)
- if (other->job)
- return false;
- }
-
- /* Also, if something else is being stopped and we should
- * change state after it, then let's wait. */
+ HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i)
+ if (other->job && job_compare(j, other->job, UNIT_AFTER) > 0)
+ return false;
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i)
- if (other->job &&
- IN_SET(other->job->type, JOB_STOP, JOB_RESTART))
+ if (other->job && job_compare(j, other->job, UNIT_BEFORE) > 0)
return false;
- /* This means that for a service a and a service b where b
- * shall be started after a:
- *
- * start a + start b → 1st step start a, 2nd step start b
- * start a + stop b → 1st step stop b, 2nd step start a
- * stop a + start b → 1st step stop a, 2nd step start b
- * stop a + stop b → 1st step stop b, 2nd step stop a
- *
- * This has the side effect that restarts are properly
- * synchronized too. */
-
return true;
}
format = job_get_begin_status_message_format(u, t);
DISABLE_WARNING_FORMAT_NONLITERAL;
- (void) snprintf(buf, sizeof buf, format, unit_description(u));
+ (void) snprintf(buf, sizeof buf, format, unit_status_string(u));
REENABLE_WARNING;
mid = t == JOB_START ? "MESSAGE_ID=" SD_MESSAGE_UNIT_STARTING_STR :
/* Show condition check message if the job did not actually do anything due to failed condition. */
if (t == JOB_START && result == JOB_DONE && !u->condition_result) {
log_struct(LOG_INFO,
- "MESSAGE=Condition check resulted in %s being skipped.", unit_description(u),
+ "MESSAGE=Condition check resulted in %s being skipped.", unit_status_string(u),
"JOB_ID=%" PRIu32, job_id,
"JOB_TYPE=%s", job_type_to_string(t),
"JOB_RESULT=%s", job_result_to_string(result),
* xsprintf() on purpose here: we are fine with truncation and don't
* consider that an error. */
DISABLE_WARNING_FORMAT_NONLITERAL;
- (void) snprintf(buf, sizeof(buf), format, unit_description(u));
+ (void) snprintf(buf, sizeof(buf), format, unit_status_string(u));
REENABLE_WARNING;
switch (t) {
if (j->unit->job_timeout != USEC_INFINITY)
timeout_time = usec_add(j->begin_usec, j->unit->job_timeout);
- if (j->begin_running_usec > 0 && j->unit->job_running_timeout != USEC_INFINITY)
+ if (timestamp_is_set(j->begin_running_usec))
timeout_time = MIN(timeout_time, usec_add(j->begin_running_usec, j->unit->job_running_timeout));
if (timeout_time == USEC_INFINITY)
if (j->type == JOB_NOP)
return false;
- /* If a job is ordered after ours, and is to be started, then it needs to wait for us, regardless if we stop or
- * start, hence let's not GC in that case. */
- HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
- if (!other->job)
- continue;
-
- if (other->job->ignore_order)
- continue;
-
- if (IN_SET(other->job->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD))
+ /* The logic is inverse to job_is_runnable, we cannot GC as long as we block any job. */
+ HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i)
+ if (other->job && job_compare(j, other->job, UNIT_BEFORE) < 0)
return false;
- }
-
- /* If we are going down, but something else is ordered After= us, then it needs to wait for us */
- if (IN_SET(j->type, JOB_STOP, JOB_RESTART))
- HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
- if (!other->job)
- continue;
-
- if (other->job->ignore_order)
- continue;
+ HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i)
+ if (other->job && job_compare(j, other->job, UNIT_AFTER) < 0)
return false;
- }
-
- /* The logic above is kinda the inverse of the job_is_runnable() logic. Specifically, if the job "we" is
- * ordered before the job "other":
- *
- * we start + other start → stay
- * we start + other stop → gc
- * we stop + other start → stay
- * we stop + other stop → gc
- *
- * "we" are ordered after "other":
- *
- * we start + other start → gc
- * we start + other stop → gc
- * we stop + other start → stay
- * we stop + other stop → stay
- */
return true;
}
j->in_gc_queue = true;
}
-static int job_compare(Job * const *a, Job * const *b) {
+static int job_compare_id(Job * const *a, Job * const *b) {
return CMP((*a)->id, (*b)->id);
}
size_t a, b;
/* Order by numeric IDs */
- typesafe_qsort(list, n, job_compare);
+ typesafe_qsort(list, n, job_compare_id);
/* Filter out duplicates */
for (a = 0, b = 0; a < n; a++) {
return 0;
}
- if (IN_SET(j->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD)) {
-
- HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
- if (!other->job)
- continue;
+ HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
+ if (!other->job)
+ continue;
+ if (job_compare(j, other->job, UNIT_AFTER) <= 0)
+ continue;
- if (!GREEDY_REALLOC(list, n_allocated, n+1))
- return -ENOMEM;
- list[n++] = other->job;
- }
+ if (!GREEDY_REALLOC(list, n_allocated, n+1))
+ return -ENOMEM;
+ list[n++] = other->job;
}
HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_BEFORE], i) {
if (!other->job)
continue;
-
- if (!IN_SET(other->job->type, JOB_STOP, JOB_RESTART))
+ if (job_compare(j, other->job, UNIT_BEFORE) <= 0)
continue;
if (!GREEDY_REALLOC(list, n_allocated, n+1))
if (other->job->ignore_order)
continue;
- if (!IN_SET(other->job->type, JOB_START, JOB_VERIFY_ACTIVE, JOB_RELOAD))
+ if (job_compare(j, other->job, UNIT_BEFORE) >= 0)
continue;
if (!GREEDY_REALLOC(list, n_allocated, n+1))
list[n++] = other->job;
}
- if (IN_SET(j->type, JOB_STOP, JOB_RESTART)) {
- HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
- if (!other->job)
- continue;
+ HASHMAP_FOREACH_KEY(v, other, j->unit->dependencies[UNIT_AFTER], i) {
+ if (!other->job)
+ continue;
+
+ if (other->job->ignore_order)
+ continue;
- if (other->job->ignore_order)
- continue;
+ if (job_compare(j, other->job, UNIT_AFTER) >= 0)
+ continue;
- if (!GREEDY_REALLOC(list, n_allocated, n+1))
- return -ENOMEM;
- list[n++] = other->job;
- }
+ if (!GREEDY_REALLOC(list, n_allocated, n+1))
+ return -ENOMEM;
+ list[n++] = other->job;
}
n = sort_job_list(list, n);
else
return "reload";
}
+
+/*
+ * assume_dep assumed dependency between units (a is before/after b)
+ *
+ * Returns
+ * 0 jobs are independent,
+ * >0 a should run after b,
+ * <0 a should run before b,
+ *
+ * The logic means that for a service a and a service b where b.After=a:
+ *
+ * start a + start b → 1st step start a, 2nd step start b
+ * start a + stop b → 1st step stop b, 2nd step start a
+ * stop a + start b → 1st step stop a, 2nd step start b
+ * stop a + stop b → 1st step stop b, 2nd step stop a
+ *
+ * This has the side effect that restarts are properly
+ * synchronized too.
+ */
+int job_compare(Job *a, Job *b, UnitDependency assume_dep) {
+ assert(a->type < _JOB_TYPE_MAX_IN_TRANSACTION);
+ assert(b->type < _JOB_TYPE_MAX_IN_TRANSACTION);
+ assert(IN_SET(assume_dep, UNIT_AFTER, UNIT_BEFORE));
+
+ /* Trivial cases first */
+ if (a->type == JOB_NOP || b->type == JOB_NOP)
+ return 0;
+
+ if (a->ignore_order || b->ignore_order)
+ return 0;
+
+ if (assume_dep == UNIT_AFTER)
+ return -job_compare(b, a, UNIT_BEFORE);
+
+ /* Let's make it simple, JOB_STOP goes always first (in case both ua and ub stop,
+ * then ub's stop goes first anyway).
+ * JOB_RESTART is JOB_STOP in disguise (before it is patched to JOB_START). */
+ if (IN_SET(b->type, JOB_STOP, JOB_RESTART))
+ return 1;
+ else
+ return -1;
+}
JobResult job_result_from_string(const char *s) _pure_;
const char* job_type_to_access_method(JobType t);
+
+int job_compare(Job *a, Job *b, UnitDependency assume_dep);
$1.CPUSchedulingPriority, config_parse_exec_cpu_sched_prio, 0, offsetof($1, exec_context)
$1.CPUSchedulingResetOnFork, config_parse_bool, 0, offsetof($1, exec_context.cpu_sched_reset_on_fork)
$1.CPUAffinity, config_parse_exec_cpu_affinity, 0, offsetof($1, exec_context)
+$1.NUMAPolicy, config_parse_numa_policy, 0, offsetof($1, exec_context.numa_policy.type)
+$1.NUMAMask, config_parse_numa_mask, 0, offsetof($1, exec_context.numa_policy)
$1.UMask, config_parse_mode, 0, offsetof($1, exec_context.umask)
$1.Environment, config_parse_environ, 0, offsetof($1, exec_context.environment)
$1.EnvironmentFile, config_parse_unit_env_file, 0, offsetof($1, exec_context.environment_files)
$1.IPAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.ip_accounting)
$1.IPAddressAllow, config_parse_ip_address_access, 0, offsetof($1, cgroup_context.ip_address_allow)
$1.IPAddressDeny, config_parse_ip_address_access, 0, offsetof($1, cgroup_context.ip_address_deny)
+$1.IPIngressFilterPath, config_parse_ip_filter_bpf_progs, 0, offsetof($1, cgroup_context.ip_filters_ingress)
+$1.IPEgressFilterPath, config_parse_ip_filter_bpf_progs, 0, offsetof($1, cgroup_context.ip_filters_egress)
$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0'
)m4_dnl
Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description)
Unit.FailureActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, failure_action_exit_status)
Unit.SuccessActionExitStatus, config_parse_exit_status, 0, offsetof(Unit, success_action_exit_status)
Unit.RebootArgument, config_parse_unit_string_printf, 0, offsetof(Unit, reboot_arg)
+m4_dnl Also add any conditions to condition_definitions[] in src/analyze/analyze-condition.c.
Unit.ConditionPathExists, config_parse_unit_condition_path, CONDITION_PATH_EXISTS, offsetof(Unit, conditions)
Unit.ConditionPathExistsGlob, config_parse_unit_condition_path, CONDITION_PATH_EXISTS_GLOB, offsetof(Unit, conditions)
Unit.ConditionPathIsDirectory, config_parse_unit_condition_path, CONDITION_PATH_IS_DIRECTORY, offsetof(Unit, conditions)
Service.TimeoutStartSec, config_parse_service_timeout, 0, 0
Service.TimeoutStopSec, config_parse_sec_fix_0, 0, offsetof(Service, timeout_stop_usec)
Service.TimeoutAbortSec, config_parse_service_timeout_abort, 0, 0
+Service.TimeoutCleanSec, config_parse_sec, 0, offsetof(Service, timeout_clean_usec)
Service.RuntimeMaxSec, config_parse_sec, 0, offsetof(Service, runtime_max_usec)
Service.WatchdogSec, config_parse_sec, 0, offsetof(Service, watchdog_usec)
m4_dnl The following five only exist for compatibility, they moved into Unit, see above
#include "af-list.h"
#include "alloc-util.h"
#include "all-units.h"
+#include "bpf-firewall.h"
#include "bus-error.h"
#include "bus-internal.h"
#include "bus-util.h"
return r;
}
+int parse_crash_chvt(const char *value, int *data) {
+ int b;
+
+ if (safe_atoi(value, data) >= 0)
+ return 0;
+
+ b = parse_boolean(value);
+ if (b < 0)
+ return b;
+
+ if (b > 0)
+ *data = 0; /* switch to where kmsg goes */
+ else
+ *data = -1; /* turn off switching */
+
+ return 0;
+}
+
+int parse_confirm_spawn(const char *value, char **console) {
+ char *s;
+ int r;
+
+ r = value ? parse_boolean(value) : 1;
+ if (r == 0) {
+ *console = NULL;
+ return 0;
+ }
+
+ if (r > 0) /* on with default tty */
+ s = strdup("/dev/console");
+ else if (is_path(value)) /* on with fully qualified path */
+ s = strdup(value);
+ else /* on with only a tty file name, not a fully qualified path */
+ s = strjoin("/dev/", value);
+ if (!s)
+ return -ENOMEM;
+
+ *console = s;
+ return 0;
+}
+
DEFINE_CONFIG_PARSE(config_parse_socket_protocol, parse_socket_protocol, "Failed to parse socket protocol");
DEFINE_CONFIG_PARSE(config_parse_exec_secure_bits, secure_bits_from_string, "Failed to parse secure bits");
DEFINE_CONFIG_PARSE_ENUM(config_parse_collect_mode, collect_mode, CollectMode, "Failed to parse garbage collection mode");
DEFINE_CONFIG_PARSE_PTR(config_parse_cg_weight, cg_weight_parse, uint64_t, "Invalid weight");
DEFINE_CONFIG_PARSE_PTR(config_parse_cpu_shares, cg_cpu_shares_parse, uint64_t, "Invalid CPU shares");
DEFINE_CONFIG_PARSE_PTR(config_parse_exec_mount_flags, mount_propagation_flags_from_string, unsigned long, "Failed to parse mount flag");
+DEFINE_CONFIG_PARSE_ENUM_WITH_DEFAULT(config_parse_numa_policy, mpol, int, -1, "Invalid NUMA policy type");
int config_parse_unit_deps(
const char *unit,
for (p = rvalue;;) {
_cleanup_free_ char *word = NULL, *k = NULL;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
return 0;
if (r == -ENOMEM)
semicolon = false;
- r = extract_first_word_and_warn(&p, &firstword, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
+ r = extract_first_word_and_warn(&p, &firstword, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
if (r <= 0)
return 0;
NULSTR_FOREACH(prefix, DEFAULT_PATH_NULSTR) {
_cleanup_free_ char *fullpath = NULL;
- fullpath = strjoin(prefix, "/", path);
+ fullpath = path_join(prefix, path);
if (!fullpath)
return log_oom();
continue;
}
- r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
+ r = extract_first_word_and_warn(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, unit, filename, line, rvalue);
if (r == 0)
break;
if (r < 0)
return 0;
}
+int config_parse_numa_mask(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ int r;
+ NUMAPolicy *p = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = parse_cpu_set_extend(rvalue, &p->nodes, true, unit, filename, line, lvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse NUMA node mask, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ return r;
+}
+
int config_parse_exec_cpu_sched_prio(const char *unit,
const char *filename,
unsigned line,
for (p = rvalue;; ) {
_cleanup_free_ char *word = NULL, *k = NULL;
- r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
if (r == 0)
return 0;
if (r == -ENOMEM)
for (;;) {
_cleanup_free_ char *word = NULL, *k = NULL;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
for (;;) {
_cleanup_free_ char *word = NULL, *k = NULL;
- r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
struct iovec *t;
const char *eq;
- r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
if (r == 0)
return 0;
if (r == -ENOMEM)
return 0;
}
- trigger = rvalue[0] == '|';
+ trigger = *rvalue == '|';
if (trigger)
- rvalue++;
+ rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
- negate = rvalue[0] == '!';
+ negate = *rvalue == '!';
if (negate)
- rvalue++;
+ rvalue += 1 + strspn(rvalue + 1, WHITESPACE);
r = unit_full_printf(u, rvalue, &s);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
return 0;
}
for (;;) {
_cleanup_free_ char *word = NULL, *resolved = NULL;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
return 0;
if (r == -ENOMEM)
_cleanup_free_ char *word = NULL;
uint32_t a;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
return 0;
if (r == -ENOMEM)
_cleanup_free_ char *word = NULL;
int af;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
return 0;
if (r == -ENOMEM)
_cleanup_free_ char *word = NULL;
CGroupController cc;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
return 0;
}
- r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
return 0;
}
- r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
return 0;
}
- r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
return 0;
}
- r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
return 0;
}
- r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
return 0;
}
- r = extract_first_word(&p, &path, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &path, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
for (p = rvalue;;) {
_cleanup_free_ char *word = NULL, *k = NULL;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
const char *w;
bool ignore_enoent = false, shall_prefix = false;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
_cleanup_free_ char *word = NULL, *path = NULL, *resolved = NULL;
const char *w;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
return 0;
if (r == -ENOMEM)
char *s = NULL, *d = NULL;
bool rbind = true, ignore_enoent = false;
- r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
+ r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r == 0)
break;
if (r == -ENOMEM)
/* Optionally, the destination is specified. */
if (p && p[-1] == ':') {
- r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
+ r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
if (p && p[-1] == ':') {
_cleanup_free_ char *options = NULL;
- r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return log_oom();
if (r < 0) {
return 0;
}
+int config_parse_ip_filter_bpf_progs(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ _cleanup_free_ char *resolved = NULL;
+ Unit *u = userdata;
+ char ***paths = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(paths);
+
+ if (isempty(rvalue)) {
+ *paths = strv_free(*paths);
+ return 0;
+ }
+
+ r = unit_full_printf(u, rvalue, &resolved);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue);
+ return 0;
+ }
+
+ r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue);
+ if (r < 0)
+ return 0;
+
+ if (strv_contains(*paths, resolved))
+ return 0;
+
+ r = strv_extend(paths, resolved);
+ if (r < 0)
+ return log_oom();
+
+ r = bpf_firewall_supported();
+ if (r < 0)
+ return r;
+ if (r != BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
+ static bool warned = false;
+
+ log_full(warned ? LOG_DEBUG : LOG_WARNING,
+ "File %s:%u configures an IP firewall with BPF programs (%s=%s), but the local system does not support BPF/cgroup based firewalling with multiple filters.\n"
+ "Starting this unit will fail! (This warning is only shown for the first loaded unit using IP firewalling.)", filename, line, lvalue, rvalue);
+
+ warned = true;
+ }
+
+ return 0;
+}
+
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {
prev = i;
}
}
+
+int config_parse_cpu_affinity2(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ CPUSet *affinity = data;
+
+ assert(affinity);
+
+ (void) parse_cpu_set_extend(rvalue, affinity, true, unit, filename, line, lvalue);
+
+ return 0;
+}
+
+int config_parse_show_status(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int k;
+ ShowStatus *b = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ k = parse_show_status(rvalue, b);
+ if (k < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_output_restricted(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ ExecOutput t, *eo = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ t = exec_output_from_string(rvalue);
+ if (t < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output type, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *eo = t;
+ return 0;
+}
+
+int config_parse_crash_chvt(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = parse_crash_chvt(rvalue, data);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ return 0;
+}
+
+int config_parse_timeout_abort(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ usec_t *timeout_usec = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(timeout_usec);
+
+ rvalue += strspn(rvalue, WHITESPACE);
+ if (isempty(rvalue)) {
+ *timeout_usec = false;
+ return 0;
+ }
+
+ r = parse_sec(rvalue, timeout_usec);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DefaultTimeoutAbortSec= setting, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *timeout_usec = true;
+ return 0;
+}
#include "conf-parser.h"
#include "unit.h"
+/* Config-parsing helpers relevant only for sources under src/core/ */
+int parse_crash_chvt(const char *value, int *data);
+int parse_confirm_spawn(const char *value, char **console);
+
/* Read service data from .desktop file style configuration fragments */
int unit_load_fragment(Unit *u);
CONFIG_PARSER_PROTOTYPE(config_parse_exit_status);
CONFIG_PARSER_PROTOTYPE(config_parse_disable_controllers);
CONFIG_PARSER_PROTOTYPE(config_parse_oom_policy);
+CONFIG_PARSER_PROTOTYPE(config_parse_numa_policy);
+CONFIG_PARSER_PROTOTYPE(config_parse_numa_mask);
+CONFIG_PARSER_PROTOTYPE(config_parse_ip_filter_bpf_progs);
+CONFIG_PARSER_PROTOTYPE(config_parse_cpu_affinity2);
+CONFIG_PARSER_PROTOTYPE(config_parse_show_status);
+CONFIG_PARSER_PROTOTYPE(config_parse_output_restricted);
+CONFIG_PARSER_PROTOTYPE(config_parse_crash_chvt);
+CONFIG_PARSER_PROTOTYPE(config_parse_timeout_abort);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
static bool arg_crash_reboot;
static char *arg_confirm_spawn;
static ShowStatus arg_show_status;
+static StatusUnitFormat arg_status_unit_format;
static bool arg_switched_root;
static PagerFlags arg_pager_flags;
static bool arg_service_watchdogs;
static EmergencyAction arg_cad_burst_action;
static OOMPolicy arg_default_oom_policy;
static CPUSet arg_cpu_affinity;
+static NUMAPolicy arg_numa_policy;
static int parse_configuration(void);
return 0;
}
-static int parse_crash_chvt(const char *value) {
- int b;
-
- if (safe_atoi(value, &arg_crash_chvt) >= 0)
- return 0;
-
- b = parse_boolean(value);
- if (b < 0)
- return b;
-
- if (b > 0)
- arg_crash_chvt = 0; /* switch to where kmsg goes */
- else
- arg_crash_chvt = -1; /* turn off switching */
-
- return 0;
-}
-
-static int parse_confirm_spawn(const char *value, char **console) {
- char *s;
- int r;
-
- r = value ? parse_boolean(value) : 1;
- if (r == 0) {
- *console = NULL;
- return 0;
- }
-
- if (r > 0) /* on with default tty */
- s = strdup("/dev/console");
- else if (is_path(value)) /* on with fully qualified path */
- s = strdup(value);
- else /* on with only a tty file name, not a fully qualified path */
- s = strjoin("/dev/", value);
- if (!s)
- return -ENOMEM;
-
- *console = s;
- return 0;
-}
-
static int set_machine_id(const char *m) {
sd_id128_t t;
assert(m);
if (!value)
arg_crash_chvt = 0; /* turn on */
else {
- r = parse_crash_chvt(value);
+ r = parse_crash_chvt(value, &arg_crash_chvt);
if (r < 0)
log_warning_errno(r, "Failed to parse crash chvt switch %s, ignoring: %m", value);
}
} else
arg_show_status = SHOW_STATUS_YES;
+ } else if (proc_cmdline_key_streq(key, "systemd.status_unit_format")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ r = status_unit_format_from_string(value);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse %s=%s, ignoring: %m", key, value);
+ else
+ arg_status_unit_format = r;
+
} else if (proc_cmdline_key_streq(key, "systemd.default_standard_output")) {
if (proc_cmdline_value_missing(key, value))
DEFINE_SETTER(config_parse_target, log_set_target_from_string, "target");
DEFINE_SETTER(config_parse_color, log_show_color_from_string, "color" );
DEFINE_SETTER(config_parse_location, log_show_location_from_string, "location");
-
-static int config_parse_cpu_affinity2(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- CPUSet *affinity = data;
-
- assert(affinity);
-
- (void) parse_cpu_set_extend(rvalue, affinity, true, unit, filename, line, lvalue);
-
- return 0;
-}
-
-static int config_parse_show_status(
- const char* unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int k;
- ShowStatus *b = data;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- k = parse_show_status(rvalue, b);
- if (k < 0) {
- log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse show status setting, ignoring: %s", rvalue);
- return 0;
- }
-
- return 0;
-}
-
-static int config_parse_output_restricted(
- const char* unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- ExecOutput t, *eo = data;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- t = exec_output_from_string(rvalue);
- if (t < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse output type, ignoring: %s", rvalue);
- return 0;
- }
-
- if (IN_SET(t, EXEC_OUTPUT_SOCKET, EXEC_OUTPUT_NAMED_FD, EXEC_OUTPUT_FILE, EXEC_OUTPUT_FILE_APPEND)) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Standard output types socket, fd:, file:, append: are not supported as defaults, ignoring: %s", rvalue);
- return 0;
- }
-
- *eo = t;
- return 0;
-}
-
-static int config_parse_crash_chvt(
- const char* unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- r = parse_crash_chvt(rvalue);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CrashChangeVT= setting, ignoring: %s", rvalue);
- return 0;
- }
-
- return 0;
-}
-
-static int config_parse_timeout_abort(
- const char* unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- rvalue += strspn(rvalue, WHITESPACE);
- if (isempty(rvalue)) {
- arg_default_timeout_abort_set = false;
- return 0;
- }
-
- r = parse_sec(rvalue, &arg_default_timeout_abort_usec);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse DefaultTimeoutAbortSec= setting, ignoring: %s", rvalue);
- return 0;
- }
-
- arg_default_timeout_abort_set = true;
- return 0;
-}
+DEFINE_SETTER(config_parse_status_unit_format, status_unit_format_from_string, "value");
static int parse_config_file(void) {
const ConfigTableItem items[] = {
- { "Manager", "LogLevel", config_parse_level2, 0, NULL },
- { "Manager", "LogTarget", config_parse_target, 0, NULL },
- { "Manager", "LogColor", config_parse_color, 0, NULL },
- { "Manager", "LogLocation", config_parse_location, 0, NULL },
- { "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core },
- { "Manager", "CrashChVT", /* legacy */ config_parse_crash_chvt, 0, NULL },
- { "Manager", "CrashChangeVT", config_parse_crash_chvt, 0, NULL },
- { "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell },
- { "Manager", "CrashReboot", config_parse_bool, 0, &arg_crash_reboot },
- { "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status },
- { "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, &arg_cpu_affinity },
- { "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL },
- { "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
- { "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
- { "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device },
- { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set },
- { "Manager", "NoNewPrivileges", config_parse_bool, 0, &arg_no_new_privs },
+ { "Manager", "LogLevel", config_parse_level2, 0, NULL },
+ { "Manager", "LogTarget", config_parse_target, 0, NULL },
+ { "Manager", "LogColor", config_parse_color, 0, NULL },
+ { "Manager", "LogLocation", config_parse_location, 0, NULL },
+ { "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core },
+ { "Manager", "CrashChVT", /* legacy */ config_parse_crash_chvt, 0, &arg_crash_chvt },
+ { "Manager", "CrashChangeVT", config_parse_crash_chvt, 0, &arg_crash_chvt },
+ { "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell },
+ { "Manager", "CrashReboot", config_parse_bool, 0, &arg_crash_reboot },
+ { "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status },
+ { "Manager", "StatusUnitFormat", config_parse_status_unit_format, 0, &arg_status_unit_format },
+ { "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, &arg_cpu_affinity },
+ { "Manager", "NUMAPolicy", config_parse_numa_policy, 0, &arg_numa_policy.type },
+ { "Manager", "NUMAMask", config_parse_numa_mask, 0, &arg_numa_policy },
+ { "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL },
+ { "Manager", "RuntimeWatchdogSec", config_parse_sec, 0, &arg_runtime_watchdog },
+ { "Manager", "ShutdownWatchdogSec", config_parse_sec, 0, &arg_shutdown_watchdog },
+ { "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device },
+ { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set },
+ { "Manager", "NoNewPrivileges", config_parse_bool, 0, &arg_no_new_privs },
#if HAVE_SECCOMP
- { "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs },
+ { "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs },
#endif
- { "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec },
- { "Manager", "DefaultTimerAccuracySec", config_parse_sec, 0, &arg_default_timer_accuracy_usec },
- { "Manager", "DefaultStandardOutput", config_parse_output_restricted,0, &arg_default_std_output },
- { "Manager", "DefaultStandardError", config_parse_output_restricted,0, &arg_default_std_error },
- { "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec },
- { "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec },
- { "Manager", "DefaultTimeoutAbortSec", config_parse_timeout_abort, 0, NULL },
- { "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec },
- { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */
- { "Manager", "DefaultStartLimitIntervalSec",config_parse_sec, 0, &arg_default_start_limit_interval },
- { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst },
- { "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment },
- { "Manager", "DefaultLimitCPU", config_parse_rlimit, RLIMIT_CPU, arg_default_rlimit },
- { "Manager", "DefaultLimitFSIZE", config_parse_rlimit, RLIMIT_FSIZE, arg_default_rlimit },
- { "Manager", "DefaultLimitDATA", config_parse_rlimit, RLIMIT_DATA, arg_default_rlimit },
- { "Manager", "DefaultLimitSTACK", config_parse_rlimit, RLIMIT_STACK, arg_default_rlimit },
- { "Manager", "DefaultLimitCORE", config_parse_rlimit, RLIMIT_CORE, arg_default_rlimit },
- { "Manager", "DefaultLimitRSS", config_parse_rlimit, RLIMIT_RSS, arg_default_rlimit },
- { "Manager", "DefaultLimitNOFILE", config_parse_rlimit, RLIMIT_NOFILE, arg_default_rlimit },
- { "Manager", "DefaultLimitAS", config_parse_rlimit, RLIMIT_AS, arg_default_rlimit },
- { "Manager", "DefaultLimitNPROC", config_parse_rlimit, RLIMIT_NPROC, arg_default_rlimit },
- { "Manager", "DefaultLimitMEMLOCK", config_parse_rlimit, RLIMIT_MEMLOCK, arg_default_rlimit },
- { "Manager", "DefaultLimitLOCKS", config_parse_rlimit, RLIMIT_LOCKS, arg_default_rlimit },
- { "Manager", "DefaultLimitSIGPENDING", config_parse_rlimit, RLIMIT_SIGPENDING, arg_default_rlimit },
- { "Manager", "DefaultLimitMSGQUEUE", config_parse_rlimit, RLIMIT_MSGQUEUE, arg_default_rlimit },
- { "Manager", "DefaultLimitNICE", config_parse_rlimit, RLIMIT_NICE, arg_default_rlimit },
- { "Manager", "DefaultLimitRTPRIO", config_parse_rlimit, RLIMIT_RTPRIO, arg_default_rlimit },
- { "Manager", "DefaultLimitRTTIME", config_parse_rlimit, RLIMIT_RTTIME, arg_default_rlimit },
- { "Manager", "DefaultCPUAccounting", config_parse_tristate, 0, &arg_default_cpu_accounting },
- { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_default_io_accounting },
- { "Manager", "DefaultIPAccounting", config_parse_bool, 0, &arg_default_ip_accounting },
- { "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting },
- { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting },
- { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting },
- { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_default_tasks_max },
- { "Manager", "CtrlAltDelBurstAction", config_parse_emergency_action, 0, &arg_cad_burst_action },
- { "Manager", "DefaultOOMPolicy", config_parse_oom_policy, 0, &arg_default_oom_policy },
+ { "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec },
+ { "Manager", "DefaultTimerAccuracySec", config_parse_sec, 0, &arg_default_timer_accuracy_usec },
+ { "Manager", "DefaultStandardOutput", config_parse_output_restricted, 0, &arg_default_std_output },
+ { "Manager", "DefaultStandardError", config_parse_output_restricted, 0, &arg_default_std_error },
+ { "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec },
+ { "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec },
+ { "Manager", "DefaultTimeoutAbortSec", config_parse_timeout_abort, 0, &arg_default_timeout_abort_set },
+ { "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec },
+ { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */
+ { "Manager", "DefaultStartLimitIntervalSec", config_parse_sec, 0, &arg_default_start_limit_interval },
+ { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst },
+ { "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment },
+ { "Manager", "DefaultLimitCPU", config_parse_rlimit, RLIMIT_CPU, arg_default_rlimit },
+ { "Manager", "DefaultLimitFSIZE", config_parse_rlimit, RLIMIT_FSIZE, arg_default_rlimit },
+ { "Manager", "DefaultLimitDATA", config_parse_rlimit, RLIMIT_DATA, arg_default_rlimit },
+ { "Manager", "DefaultLimitSTACK", config_parse_rlimit, RLIMIT_STACK, arg_default_rlimit },
+ { "Manager", "DefaultLimitCORE", config_parse_rlimit, RLIMIT_CORE, arg_default_rlimit },
+ { "Manager", "DefaultLimitRSS", config_parse_rlimit, RLIMIT_RSS, arg_default_rlimit },
+ { "Manager", "DefaultLimitNOFILE", config_parse_rlimit, RLIMIT_NOFILE, arg_default_rlimit },
+ { "Manager", "DefaultLimitAS", config_parse_rlimit, RLIMIT_AS, arg_default_rlimit },
+ { "Manager", "DefaultLimitNPROC", config_parse_rlimit, RLIMIT_NPROC, arg_default_rlimit },
+ { "Manager", "DefaultLimitMEMLOCK", config_parse_rlimit, RLIMIT_MEMLOCK, arg_default_rlimit },
+ { "Manager", "DefaultLimitLOCKS", config_parse_rlimit, RLIMIT_LOCKS, arg_default_rlimit },
+ { "Manager", "DefaultLimitSIGPENDING", config_parse_rlimit, RLIMIT_SIGPENDING, arg_default_rlimit },
+ { "Manager", "DefaultLimitMSGQUEUE", config_parse_rlimit, RLIMIT_MSGQUEUE, arg_default_rlimit },
+ { "Manager", "DefaultLimitNICE", config_parse_rlimit, RLIMIT_NICE, arg_default_rlimit },
+ { "Manager", "DefaultLimitRTPRIO", config_parse_rlimit, RLIMIT_RTPRIO, arg_default_rlimit },
+ { "Manager", "DefaultLimitRTTIME", config_parse_rlimit, RLIMIT_RTTIME, arg_default_rlimit },
+ { "Manager", "DefaultCPUAccounting", config_parse_tristate, 0, &arg_default_cpu_accounting },
+ { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_default_io_accounting },
+ { "Manager", "DefaultIPAccounting", config_parse_bool, 0, &arg_default_ip_accounting },
+ { "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting },
+ { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting },
+ { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting },
+ { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_default_tasks_max },
+ { "Manager", "CtrlAltDelBurstAction", config_parse_emergency_action, 0, &arg_cad_burst_action },
+ { "Manager", "DefaultOOMPolicy", config_parse_oom_policy, 0, &arg_default_oom_policy },
{}
};
m->cad_burst_action = arg_cad_burst_action;
manager_set_show_status(m, arg_show_status);
+ m->status_unit_format = arg_status_unit_format;
}
static int parse_argv(int argc, char *argv[]) {
break;
case ARG_CRASH_CHVT:
- r = parse_crash_chvt(optarg);
+ r = parse_crash_chvt(optarg, &arg_crash_chvt);
if (r < 0)
return log_error_errno(r, "Failed to parse crash virtual terminal index: \"%s\": %m",
optarg);
"Starts up and maintains the system or user services.\n\n"
" -h --help Show this help\n"
" --version Show version\n"
- " --test Determine startup sequence, dump it and exit\n"
+ " --test Determine initial transaction, dump it and exit\n"
+ " --system In combination with --test: operate as system service manager\n"
+ " --user In combination with --test: operate as per-user service manager\n"
" --no-pager Do not pipe output into a pager\n"
" --dump-configuration-items Dump understood unit configuration items\n"
" --dump-bus-properties Dump exposed bus properties\n"
" --unit=UNIT Set default unit\n"
- " --system Run a system instance, even if PID != 1\n"
- " --user Run a user instance\n"
" --dump-core[=BOOL] Dump core on crash\n"
" --crash-vt=NR Change to specified VT on crash\n"
" --crash-reboot[=BOOL] Reboot on crash\n"
#endif
#if BUMP_PROC_SYS_FS_FILE_MAX
- /* I so wanted to use STRINGIFY(ULONG_MAX) here, but alas we can't as glibc/gcc define that as
- * "(0x7fffffffffffffffL * 2UL + 1UL)". Seriously. 😢 */
- if (asprintf(&t, "%lu\n", ULONG_MAX) < 0) {
+ /* The maximum the kernel allows for this since 5.2 is LONG_MAX, use that. (Previously thing where
+ * different but the operation would fail silently.) */
+ if (asprintf(&t, "%li\n", LONG_MAX) < 0) {
log_oom();
return;
}
log_warning_errno(errno, "Failed to set CPU affinity: %m");
}
+static void update_numa_policy(bool skip_setup) {
+ int r;
+ _cleanup_free_ char *nodes = NULL;
+ const char * policy = NULL;
+
+ if (skip_setup || !mpol_is_valid(numa_policy_get_type(&arg_numa_policy)))
+ return;
+
+ if (DEBUG_LOGGING) {
+ policy = mpol_to_string(numa_policy_get_type(&arg_numa_policy));
+ nodes = cpu_set_to_range_string(&arg_numa_policy.nodes);
+ log_debug("Setting NUMA policy to %s, with nodes %s.", strnull(policy), strnull(nodes));
+ }
+
+ r = apply_numa_policy(&arg_numa_policy);
+ if (r == -EOPNOTSUPP)
+ log_debug_errno(r, "NUMA support not available, ignoring.");
+ else if (r < 0)
+ log_warning_errno(r, "Failed to set NUMA memory policy: %m");
+}
+
static void do_reexecute(
int argc,
char *argv[],
set_manager_defaults(m);
update_cpu_affinity(false);
+ update_numa_policy(false);
if (saved_log_level >= 0)
manager_override_log_level(m, saved_log_level);
return 0;
update_cpu_affinity(skip_setup);
+ update_numa_policy(skip_setup);
if (arg_system) {
/* Make sure we leave a core dump without panicking the kernel. */
log_warning_errno(r, "Failed to set watchdog device to %s, ignoring: %m", arg_watchdog_device);
}
- if (arg_runtime_watchdog > 0 && arg_runtime_watchdog != USEC_INFINITY)
+ if (timestamp_is_set(arg_runtime_watchdog))
watchdog_set_timeout(&arg_runtime_watchdog);
}
arg_crash_reboot = false;
arg_confirm_spawn = mfree(arg_confirm_spawn);
arg_show_status = _SHOW_STATUS_INVALID;
+ arg_status_unit_format = STATUS_UNIT_FORMAT_DEFAULT;
arg_switched_root = false;
arg_pager_flags = 0;
arg_service_watchdogs = true;
arg_default_oom_policy = OOM_STOP;
cpu_set_reset(&arg_cpu_affinity);
+ numa_policy_reset(&arg_numa_policy);
}
static int parse_configuration(void) {
"%sA %s job is running for %s (%s / %s)",
strempty(job_of_n),
job_type_to_string(j->type),
- unit_description(j->unit),
+ unit_status_string(j->unit),
time, limit);
}
.unit_file_scope = scope,
.objective = _MANAGER_OBJECTIVE_INVALID,
+ .status_unit_format = STATUS_UNIT_FORMAT_DEFAULT,
+
.default_timer_accuracy_usec = USEC_PER_MINUTE,
.default_memory_accounting = MEMORY_ACCOUNTING_DEFAULT,
.default_tasks_accounting = true,
assert(m);
+ if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
+ return;
+
/* Let's ask every type to load all units from disk/kernel that it might know */
for (c = 0; c < _UNIT_TYPE_MAX; c++) {
if (!unit_type_supported(c)) {
assert(m);
+ if (m->test_run_flags == MANAGER_TEST_RUN_MINIMAL)
+ return;
+
/* Let's ask every type to load all units from disk/kernel that it might know */
for (c = 0; c < _UNIT_TYPE_MAX; c++) {
if (!unit_type_supported(c)) {
FOREACH_DIRENT(de, d, r = -errno; goto fail) {
char *p;
- p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name);
+ p = path_join(*i, de->d_name);
if (!p) {
r = -ENOMEM;
goto fail;
assert(f);
for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
- char buf[FORMAT_TIMESTAMP_MAX];
+ const dual_timestamp *t = m->timestamps + q;
+ char buf[CONST_MAX(FORMAT_TIMESPAN_MAX, FORMAT_TIMESTAMP_MAX)];
- if (dual_timestamp_is_set(m->timestamps + q))
+ if (dual_timestamp_is_set(t))
fprintf(f, "%sTimestamp %s: %s\n",
strempty(prefix),
manager_timestamp_to_string(q),
- format_timestamp(buf, sizeof(buf), m->timestamps[q].realtime));
+ timestamp_is_set(t->realtime) ? format_timestamp(buf, sizeof buf, t->realtime) :
+ format_timespan(buf, sizeof buf, t->monotonic, 1));
}
manager_dump_units(m, f, prefix);
while (m->objective == MANAGER_OK) {
usec_t wait_usec;
- if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m))
+ if (timestamp_is_set(m->runtime_watchdog) && MANAGER_IS_SYSTEM(m))
watchdog_ping();
if (!ratelimit_below(&rl)) {
continue;
/* Sleep for half the watchdog time */
- if (m->runtime_watchdog > 0 && m->runtime_watchdog != USEC_INFINITY && MANAGER_IS_SYSTEM(m)) {
+ if (timestamp_is_set(m->runtime_watchdog) && MANAGER_IS_SYSTEM(m)) {
wait_usec = m->runtime_watchdog / 2;
if (wait_usec <= 0)
wait_usec = 1;
uint8_t return_value;
ShowStatus show_status;
+ StatusUnitFormat status_unit_format;
char *confirm_spawn;
bool no_console_output;
bool service_watchdogs;
in_files = [['macros.systemd', rpmmacrosdir],
['system.conf', pkgsysconfdir],
+ ['user.conf', pkgsysconfdir],
['systemd.pc', pkgconfigdatadir],
['triggers.systemd', '']]
install_data(policy,
install_dir : polkitpolicydir)
-install_data('user.conf',
- install_dir : pkgsysconfdir)
-
meson.add_install_script('sh', '-c', mkdir_p.format(systemshutdowndir))
meson.add_install_script('sh', '-c', mkdir_p.format(systemsleepdir))
meson.add_install_script('sh', '-c', mkdir_p.format(systemgeneratordir))
if (m[i].has_prefix)
continue;
- s = prefix_root(root_directory, mount_entry_path(m+i));
+ s = path_join(root_directory, mount_entry_path(m+i));
if (!s)
return -ENOMEM;
break;
}
- r = log_warning_errno(errno, "Failed to add watch on %s: %s", s->path, errno == ENOSPC ? "too many watches" : strerror(-r));
+ r = log_warning_errno(errno, "Failed to add watch on %s: %s", s->path, errno == ENOSPC ? "too many watches" : strerror_safe(r));
if (cut)
*cut = tmp;
goto fail;
#include "alloc-util.h"
#include "audit-fd.h"
#include "bus-util.h"
+#include "errno-util.h"
#include "format-util.h"
#include "log.h"
#include "path-util.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
log_internalv(LOG_AUTH | callback_type_to_priority(type),
- 0, __FILE__, __LINE__, __FUNCTION__,
+ 0, PROJECT_FILE, __LINE__, __FUNCTION__,
fmt2, ap);
#pragma GCC diagnostic pop
va_end(ap);
/* Return an access denied error, if we couldn't load
* the AVC but enforcing mode was on, or we couldn't
* determine whether it is one. */
- return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to open the SELinux AVC: %s", strerror(saved_errno));
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Failed to open the SELinux AVC: %s", strerror_safe(saved_errno));
}
selinux_set_callback(SELINUX_CB_AUDIT, (union selinux_callback) audit_callback);
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "rm-rf.h"
#include "serialize.h"
#include "service.h"
#include "signal-util.h"
[SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
[SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
[SERVICE_FAILED] = UNIT_FAILED,
- [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
+ [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
+ [SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
/* For Type=idle we never want to delay any other jobs, hence we
[SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
[SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
[SERVICE_FAILED] = UNIT_FAILED,
- [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
+ [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING,
+ [SERVICE_CLEANING] = UNIT_MAINTENANCE,
};
static int service_dispatch_inotify_io(sd_event_source *source, int fd, uint32_t events, void *userdata);
s->timeout_abort_set = u->manager->default_timeout_abort_set;
s->restart_usec = u->manager->default_restart_usec;
s->runtime_max_usec = USEC_INFINITY;
+ s->timeout_clean_usec = USEC_INFINITY;
s->type = _SERVICE_TYPE_INVALID;
s->socket_fd = -1;
s->stdin_fd = s->stdout_fd = s->stderr_fd = -1;
log_unit_warning(UNIT(s), "Service has USBFunctionStrings= setting, but no USBFunctionDescriptors=. Ignoring.");
if (s->runtime_max_usec != USEC_INFINITY && s->type == SERVICE_ONESHOT)
- log_unit_warning(UNIT(s), "MaxRuntimeSec= has no effect in combination with Type=oneshot. Ignoring.");
+ log_unit_warning(UNIT(s), "RuntimeMaxSec= has no effect in combination with Type=oneshot. Ignoring.");
return 0;
}
}
static void service_dump(Unit *u, FILE *f, const char *prefix) {
- char buf_restart[FORMAT_TIMESPAN_MAX], buf_start[FORMAT_TIMESPAN_MAX], buf_stop[FORMAT_TIMESPAN_MAX];
- char buf_runtime[FORMAT_TIMESPAN_MAX], buf_watchdog[FORMAT_TIMESPAN_MAX], buf_abort[FORMAT_TIMESPAN_MAX];
+ char buf_restart[FORMAT_TIMESPAN_MAX], buf_start[FORMAT_TIMESPAN_MAX], buf_stop[FORMAT_TIMESPAN_MAX],
+ buf_runtime[FORMAT_TIMESPAN_MAX], buf_watchdog[FORMAT_TIMESPAN_MAX], buf_abort[FORMAT_TIMESPAN_MAX],
+ buf_clean[FORMAT_TIMESPAN_MAX];
ServiceExecCommand c;
Service *s = SERVICE(u);
const char *prefix2;
"%sService State: %s\n"
"%sResult: %s\n"
"%sReload Result: %s\n"
+ "%sClean Result: %s\n"
"%sPermissionsStartOnly: %s\n"
"%sRootDirectoryStartOnly: %s\n"
"%sRemainAfterExit: %s\n"
prefix, service_state_to_string(s->state),
prefix, service_result_to_string(s->result),
prefix, service_result_to_string(s->reload_result),
+ prefix, service_result_to_string(s->clean_result),
prefix, yes_no(s->permissions_start_only),
prefix, yes_no(s->root_directory_start_only),
prefix, yes_no(s->remain_after_exit),
prefix, format_timespan(buf_abort, sizeof(buf_abort), s->timeout_abort_usec, USEC_PER_SEC));
fprintf(f,
+ "%sTimeoutCleanSec: %s\n"
"%sRuntimeMaxSec: %s\n"
"%sWatchdogSec: %s\n",
+ prefix, format_timespan(buf_clean, sizeof(buf_clean), s->timeout_clean_usec, USEC_PER_SEC),
prefix, format_timespan(buf_runtime, sizeof(buf_runtime), s->runtime_max_usec, USEC_PER_SEC),
prefix, format_timespan(buf_watchdog, sizeof(buf_watchdog), s->watchdog_usec, USEC_PER_SEC));
SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
- SERVICE_AUTO_RESTART))
+ SERVICE_AUTO_RESTART,
+ SERVICE_CLEANING))
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
if (!IN_SET(state,
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
+ SERVICE_CLEANING)) {
service_unwatch_control_pid(s);
s->control_command = NULL;
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
case SERVICE_AUTO_RESTART:
return usec_add(UNIT(s)->inactive_enter_timestamp.monotonic, s->restart_usec);
+ case SERVICE_CLEANING:
+ return usec_add(UNIT(s)->state_change_timestamp.monotonic, s->timeout_clean_usec);
+
default:
return USEC_INFINITY;
}
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
SERVICE_RELOAD,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL)) {
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
+ SERVICE_CLEANING)) {
r = unit_watch_pid(UNIT(s), s->control_pid, false);
if (r < 0)
return r;
}
- if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART)) {
+ if (!IN_SET(s->deserialized_state, SERVICE_DEAD, SERVICE_FAILED, SERVICE_AUTO_RESTART, SERVICE_CLEANING)) {
(void) unit_enqueue_rewatch_pids(u);
(void) unit_setup_dynamic_creds(u);
(void) unit_setup_exec_runtime(u);
* please! */
if (IN_SET(s->state,
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
- SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL))
+ SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL, SERVICE_CLEANING))
return -EAGAIN;
/* Already on it! */
return 0;
}
+ /* If we are currently cleaning, then abort it, brutally. */
+ if (s->state == SERVICE_CLEANING) {
+ service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_SUCCESS);
+ return 0;
+ }
+
assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_EXITED));
service_enter_stop(s, SERVICE_SUCCESS);
return log_oom();
n = strlen(e);
- if (!GREEDY_REALLOC(args, allocated, length + 1 + n + 1))
+ if (!GREEDY_REALLOC(args, allocated, length + 2 + n + 2))
return log_oom();
if (length > 0)
args[length++] = ' ';
+ args[length++] = '"';
memcpy(args + length, e, n);
length += n;
+ args[length++] = '"';
}
if (!GREEDY_REALLOC(args, allocated, length + 1))
for (;;) {
_cleanup_free_ char *arg = NULL;
- r = extract_first_word(&value, &arg, NULL, EXTRACT_CUNESCAPE);
+ r = extract_first_word(&value, &arg, NULL, EXTRACT_CUNESCAPE | EXTRACT_UNQUOTE);
if (r < 0)
return r;
if (r == 0)
service_enter_dead(s, f, true);
break;
+ case SERVICE_CLEANING:
+
+ if (s->clean_result == SERVICE_SUCCESS)
+ s->clean_result = f;
+
+ service_enter_dead(s, SERVICE_SUCCESS, false);
+ break;
+
default:
assert_not_reached("Uh, control process died at wrong time.");
}
service_enter_restart(s);
break;
+ case SERVICE_CLEANING:
+ log_unit_warning(UNIT(s), "Cleaning timed out. killing.");
+
+ if (s->clean_result == SERVICE_SUCCESS)
+ s->clean_result = SERVICE_FAILURE_TIMEOUT;
+
+ service_enter_signal(s, SERVICE_FINAL_SIGKILL, 0);
+ break;
+
default:
assert_not_reached("Timeout at wrong time.");
}
s->result = SERVICE_SUCCESS;
s->reload_result = SERVICE_SUCCESS;
+ s->clean_result = SERVICE_SUCCESS;
s->n_restarts = 0;
s->flush_n_restarts = false;
}
return s->main_exec_status.status;
}
+static int service_clean(Unit *u, ExecCleanMask mask) {
+ _cleanup_strv_free_ char **l = NULL;
+ Service *s = SERVICE(u);
+ pid_t pid;
+ int r;
+
+ assert(s);
+ assert(mask != 0);
+
+ if (s->state != SERVICE_DEAD)
+ return -EBUSY;
+
+ r = exec_context_get_clean_directories(&s->exec_context, u->manager->prefix, mask, &l);
+ if (r < 0)
+ return r;
+
+ if (strv_isempty(l))
+ return -EUNATCH;
+
+ service_unwatch_control_pid(s);
+ s->clean_result = SERVICE_SUCCESS;
+ s->control_command = NULL;
+ s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
+
+ r = service_arm_timer(s, usec_add(now(CLOCK_MONOTONIC), s->timeout_clean_usec));
+ if (r < 0)
+ goto fail;
+
+ r = unit_fork_helper_process(UNIT(s), "(sd-rmrf)", &pid);
+ if (r < 0)
+ goto fail;
+ if (r == 0) {
+ int ret = EXIT_SUCCESS;
+ char **i;
+
+ STRV_FOREACH(i, l) {
+ r = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_MISSING_OK);
+ if (r < 0) {
+ log_error_errno(r, "Failed to remove '%s': %m", *i);
+ ret = EXIT_FAILURE;
+ }
+ }
+
+ _exit(ret);
+ }
+
+ r = unit_watch_pid(u, pid, true);
+ if (r < 0)
+ goto fail;
+
+ s->control_pid = pid;
+
+ service_set_state(s, SERVICE_CLEANING);
+
+ return 0;
+
+fail:
+ log_unit_warning_errno(UNIT(s), r, "Failed to initiate cleaning: %m");
+ s->clean_result = SERVICE_FAILURE_RESOURCES;
+ s->timer_event_source = sd_event_source_unref(s->timer_event_source);
+ return r;
+}
+
+static int service_can_clean(Unit *u, ExecCleanMask *ret) {
+ Service *s = SERVICE(u);
+
+ assert(s);
+
+ return exec_context_get_clean_mask(&s->exec_context, ret);
+}
+
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand);
+static const char* const service_exec_ex_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
+ [SERVICE_EXEC_START_PRE] = "ExecStartPreEx",
+ [SERVICE_EXEC_START] = "ExecStartEx",
+ [SERVICE_EXEC_START_POST] = "ExecStartPostEx",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(service_exec_ex_command, ServiceExecCommand);
+
static const char* const notify_state_table[_NOTIFY_STATE_MAX] = {
[NOTIFY_UNKNOWN] = "unknown",
[NOTIFY_READY] = "ready",
.can_reload = service_can_reload,
.kill = service_kill,
+ .clean = service_clean,
+ .can_clean = service_can_clean,
.serialize = service_serialize,
.deserialize_item = service_deserialize_item,
usec_t timeout_stop_usec;
usec_t timeout_abort_usec;
bool timeout_abort_set;
+ usec_t timeout_clean_usec;
usec_t runtime_max_usec;
dual_timestamp watchdog_timestamp;
/* If we shut down, remember why */
ServiceResult result;
ServiceResult reload_result;
+ ServiceResult clean_result;
bool main_pid_known:1;
bool main_pid_alien:1;
const char* service_exec_command_to_string(ServiceExecCommand i) _const_;
ServiceExecCommand service_exec_command_from_string(const char *s) _pure_;
+const char* service_exec_ex_command_to_string(ServiceExecCommand i) _const_;
+ServiceExecCommand service_exec_ex_command_from_string(const char *s) _pure_;
+
const char* notify_state_to_string(NotifyState i) _const_;
NotifyState notify_state_from_string(const char *s) _pure_;
#include "util.h"
static const char* const show_status_table[_SHOW_STATUS_MAX] = {
- [SHOW_STATUS_NO] = "no",
- [SHOW_STATUS_AUTO] = "auto",
+ [SHOW_STATUS_NO] = "no",
+ [SHOW_STATUS_AUTO] = "auto",
[SHOW_STATUS_TEMPORARY] = "temporary",
- [SHOW_STATUS_YES] = "yes",
+ [SHOW_STATUS_YES] = "yes",
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(show_status, ShowStatus, SHOW_STATUS_YES);
return r;
}
+
+static const char* const status_unit_format_table[_STATUS_UNIT_FORMAT_MAX] = {
+ [STATUS_UNIT_FORMAT_NAME] = "name",
+ [STATUS_UNIT_FORMAT_DESCRIPTION] = "description",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(status_unit_format, StatusUnitFormat);
SHOW_STATUS_EPHEMERAL = 1 << 1,
} ShowStatusFlags;
+typedef enum StatusUnitFormat {
+ STATUS_UNIT_FORMAT_NAME,
+ STATUS_UNIT_FORMAT_DESCRIPTION,
+ _STATUS_UNIT_FORMAT_MAX,
+ _STATUS_UNIT_FORMAT_INVALID = -1,
+} StatusUnitFormat;
+
ShowStatus show_status_from_string(const char *v) _const_;
const char* show_status_to_string(ShowStatus s) _pure_;
int parse_show_status(const char *v, ShowStatus *ret);
+StatusUnitFormat status_unit_format_from_string(const char *v) _const_;
+const char* status_unit_format_to_string(StatusUnitFormat s) _pure_;
+
int status_vprintf(const char *status, ShowStatusFlags flags, const char *format, va_list ap) _printf_(3,0);
int status_printf(const char *status, ShowStatusFlags flags, const char *format, ...) _printf_(3,4);
r = socket_address_print(&p->address, &k);
if (r < 0)
- t = strerror(-r);
+ t = strerror_safe(r);
else
t = k;
#CrashReboot=no
#CtrlAltDelBurstAction=reboot-force
#CPUAffinity=1 2
+#NUMAPolicy=default
+#NUMAMask=
#RuntimeWatchdogSec=0
#ShutdownWatchdogSec=10min
#WatchdogDevice=
#NoNewPrivileges=no
#SystemCallArchitectures=
#TimerSlackNSec=
+#StatusUnitFormat=@STATUS_UNIT_FORMAT_DEFAULT@
#DefaultTimerAccuracySec=1min
#DefaultStandardOutput=journal
#DefaultStandardError=inherit
}
}
+static int timer_clean(Unit *u, ExecCleanMask mask) {
+ Timer *t = TIMER(u);
+ int r;
+
+ assert(t);
+ assert(mask != 0);
+
+ if (t->state != TIMER_DEAD)
+ return -EBUSY;
+
+ if (!IN_SET(mask, EXEC_CLEAN_STATE))
+ return -EUNATCH;
+
+ r = timer_setup_persistent(t);
+ if (r < 0)
+ return r;
+
+ if (!t->stamp_path)
+ return -EUNATCH;
+
+ if (unlink(t->stamp_path) && errno != ENOENT)
+ return log_unit_error_errno(u, errno, "Failed to clean stamp file of timer: %m");
+
+ return 0;
+}
+
+static int timer_can_clean(Unit *u, ExecCleanMask *ret) {
+ Timer *t = TIMER(u);
+
+ assert(t);
+
+ *ret = t->persistent ? EXEC_CLEAN_STATE : 0;
+ return 0;
+}
+
static const char* const timer_base_table[_TIMER_BASE_MAX] = {
[TIMER_ACTIVE] = "OnActiveSec",
[TIMER_BOOT] = "OnBootSec",
.start = timer_start,
.stop = timer_stop,
+ .clean = timer_clean,
+ .can_clean = timer_can_clean,
+
.serialize = timer_serialize,
.deserialize_item = timer_deserialize_item,
Unit *u;
void *v;
int r;
+ static const UnitDependency directions[] = {
+ UNIT_BEFORE,
+ UNIT_AFTER,
+ };
+ size_t d;
assert(tr);
assert(j);
j->marker = from ? from : j;
j->generation = generation;
- /* We assume that the dependencies are bidirectional, and
- * hence can ignore UNIT_AFTER */
- HASHMAP_FOREACH_KEY(v, u, j->unit->dependencies[UNIT_BEFORE], i) {
- Job *o;
-
- /* Is there a job for this unit? */
- o = hashmap_get(tr->jobs, u);
- if (!o) {
- /* Ok, there is no job for this in the
- * transaction, but maybe there is already one
- * running? */
- o = u->job;
- if (!o)
+ /* Actual ordering of jobs depends on the unit ordering dependency and job types. We need to traverse
+ * the graph over 'before' edges in the actual job execution order. We traverse over both unit
+ * ordering dependencies and we test with job_compare() whether it is the 'before' edge in the job
+ * execution ordering. */
+ for (d = 0; d < ELEMENTSOF(directions); d++) {
+ HASHMAP_FOREACH_KEY(v, u, j->unit->dependencies[directions[d]], i) {
+ Job *o;
+
+ /* Is there a job for this unit? */
+ o = hashmap_get(tr->jobs, u);
+ if (!o) {
+ /* Ok, there is no job for this in the
+ * transaction, but maybe there is already one
+ * running? */
+ o = u->job;
+ if (!o)
+ continue;
+ }
+
+ /* Cut traversing if the job j is not really *before* o. */
+ if (job_compare(j, o, directions[d]) >= 0)
continue;
- }
- r = transaction_verify_order_one(tr, o, j, generation, e);
- if (r < 0)
- return r;
+ r = transaction_verify_order_one(tr, o, j, generation, e);
+ if (r < 0)
+ return r;
+ }
}
/* Ok, let's backtrack, and remember that this entry is not on
* before or after the relevant configuration setting. Hence: don't add them.
*/
+ assert(u);
+ assert(format);
+ assert(ret);
+
const Specifier table[] = {
{ 'n', specifier_string, u->id },
{ 'N', specifier_prefix_and_instance, NULL },
{}
};
- assert(u);
- assert(format);
- assert(ret);
-
return specifier_printf(format, table, u, ret);
}
#include "all-units.h"
#include "alloc-util.h"
+#include "bpf-firewall.h"
#include "bus-common-errors.h"
#include "bus-util.h"
#include "cgroup-util.h"
bpf_program_unref(u->ip_bpf_egress);
bpf_program_unref(u->ip_bpf_egress_installed);
+ set_free(u->ip_bpf_custom_ingress);
+ set_free(u->ip_bpf_custom_egress);
+ set_free(u->ip_bpf_custom_ingress_installed);
+ set_free(u->ip_bpf_custom_egress_installed);
+
bpf_program_unref(u->bpf_device_control_installed);
condition_free_list(u->conditions);
STRV_FOREACH(dp, c->directories[dt].paths) {
_cleanup_free_ char *p;
- p = strjoin(u->manager->prefix[dt], "/", *dp);
+ p = path_join(u->manager->prefix[dt], *dp);
if (!p)
return -ENOMEM;
return strna(u->id);
}
+const char *unit_status_string(Unit *u) {
+ assert(u);
+
+ if (u->manager->status_unit_format == STATUS_UNIT_FORMAT_NAME && u->id)
+ return u->id;
+
+ return unit_description(u);
+}
+
static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependencyMask mask, bool *space) {
const struct {
UnitDependencyMask mask;
"%s\tMerged into: %s\n",
prefix, u->merged_into->id);
else if (u->load_state == UNIT_ERROR)
- fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror(-u->load_error));
+ fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror_safe(u->load_error));
for (n = sd_bus_track_first(u->bus_track); n; n = sd_bus_track_next(u->bus_track))
fprintf(f, "%s\tBus Ref: %s\n", prefix, n);
void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg_format) {
const char *d;
- d = unit_description(u);
+ d = unit_status_string(u);
if (log_get_show_color())
d = strjoina(ANSI_HIGHLIGHT, d, ANSI_NORMAL);
state = unit_active_state(u);
if (UNIT_IS_ACTIVE_OR_RELOADING(state))
return -EALREADY;
+ if (state == UNIT_MAINTENANCE)
+ return -EAGAIN;
/* Units that aren't loaded cannot be started */
if (u->load_state != UNIT_LOADED)
(void) mkdir_p_label(u->manager->lookup_paths.transient, 0755);
- path = strjoin(u->manager->lookup_paths.transient, "/", u->id);
+ path = path_join(u->manager->lookup_paths.transient, u->id);
if (!path)
return -ENOMEM;
assert(u);
+ /* Load any custom firewall BPF programs here once to test if they are existing and actually loadable.
+ * Fail here early since later errors in the call chain unit_realize_cgroup to cgroup_context_apply are ignored. */
+ r = bpf_firewall_load_custom(u);
+ if (r < 0)
+ return r;
+
/* Prepares everything so that we can fork of a process for this unit */
(void) unit_realize_cgroup(u);
return 0;
}
+int unit_clean(Unit *u, ExecCleanMask mask) {
+ UnitActiveState state;
+
+ assert(u);
+
+ /* Special return values:
+ *
+ * -EOPNOTSUPP → cleaning not supported for this unit type
+ * -EUNATCH → cleaning not defined for this resource type
+ * -EBUSY → unit currently can't be cleaned since it's running or not properly loaded, or has
+ * a job queued or similar
+ */
+
+ if (!UNIT_VTABLE(u)->clean)
+ return -EOPNOTSUPP;
+
+ if (mask == 0)
+ return -EUNATCH;
+
+ if (u->load_state != UNIT_LOADED)
+ return -EBUSY;
+
+ if (u->job)
+ return -EBUSY;
+
+ state = unit_active_state(u);
+ if (!IN_SET(state, UNIT_INACTIVE))
+ return -EBUSY;
+
+ return UNIT_VTABLE(u)->clean(u, mask);
+}
+
+int unit_can_clean(Unit *u, ExecCleanMask *ret) {
+ assert(u);
+
+ if (!UNIT_VTABLE(u)->clean ||
+ u->load_state != UNIT_LOADED) {
+ *ret = 0;
+ return 0;
+ }
+
+ /* When the clean() method is set, can_clean() really should be set too */
+ assert(UNIT_VTABLE(u)->can_clean);
+
+ return UNIT_VTABLE(u)->can_clean(u, ret);
+}
+
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
[COLLECT_INACTIVE] = "inactive",
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
#include "emergency-action.h"
#include "install.h"
#include "list.h"
+#include "set.h"
#include "unit-name.h"
#include "cgroup.h"
BPFProgram *ip_bpf_ingress, *ip_bpf_ingress_installed;
BPFProgram *ip_bpf_egress, *ip_bpf_egress_installed;
+ Set *ip_bpf_custom_ingress;
+ Set *ip_bpf_custom_ingress_installed;
+ Set *ip_bpf_custom_egress;
+ Set *ip_bpf_custom_egress_installed;
uint64_t ip_accounting_extra[_CGROUP_IP_ACCOUNTING_METRIC_MAX];
int (*kill)(Unit *u, KillWho w, int signo, sd_bus_error *error);
+ /* Clear out the various runtime/state/cache/logs/configuration data */
+ int (*clean)(Unit *u, ExecCleanMask m);
+
+ /* Return which kind of data can be cleaned */
+ int (*can_clean)(Unit *u, ExecCleanMask *ret);
+
bool (*can_reload)(Unit *u);
/* Write all data that cannot be restored from other sources
int unit_set_default_slice(Unit *u);
const char *unit_description(Unit *u) _pure_;
+const char *unit_status_string(Unit *u) _pure_;
bool unit_has_name(const Unit *u, const char *name);
int unit_test_trigger_loaded(Unit *u);
+int unit_clean(Unit *u, ExecCleanMask mask);
+int unit_can_clean(Unit *u, ExecCleanMask *ret_mask);
+
/* Macros which append UNIT= or USER_UNIT= to the message */
#define log_unit_full(unit, level, error, ...) \
({ \
const Unit *_u = (unit); \
- _u ? log_object_internal(level, error, __FILE__, __LINE__, __func__, _u->manager->unit_log_field, _u->id, _u->manager->invocation_log_field, _u->invocation_id_string, ##__VA_ARGS__) : \
- log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
+ _u ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, _u->manager->unit_log_field, _u->id, _u->manager->invocation_log_field, _u->invocation_id_string, ##__VA_ARGS__) : \
+ log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
})
#define log_unit_debug(unit, ...) log_unit_full(unit, LOG_DEBUG, 0, ##__VA_ARGS__)
#LogLocation=no
#SystemCallArchitectures=
#TimerSlackNSec=
+#StatusUnitFormat=@STATUS_UNIT_FORMAT_DEFAULT@
#DefaultTimerAccuracySec=1min
#DefaultStandardOutput=inherit
#DefaultStandardError=inherit
assert_cc(JOURNAL_SIZE_MAX <= DATA_SIZE_MAX);
enum {
- /* We use this as array indexes for a couple of special fields we use for
- * naming coredump files, and attaching xattrs, and for indexing argv[].
-
- * Our pattern for man:systectl(1) kernel.core_pattern is such that the
- * kernel passes fields until CONTEXT_RLIMIT as arguments in argv[]. After
- * that it gets complicated: the kernel passes "comm" as one or more fields
- * starting at index CONTEXT_COMM (in other words, full "comm" is under index
- * CONTEXT_COMM when it does not contain spaces, which is the common
- * case). This mapping is not reversible, so we prefer to retrieve "comm"
- * from /proc. We only fall back to argv[CONTEXT_COMM...] when that fails.
+ /* We use this as array indexes for a couple of special fields we use for naming
+ * coredump files, and attaching xattrs, and for indexing argv[].
*
- * In the internal context[] array, fields before CONTEXT_COMM are the
- * strings from argv[], so they should not be freed. The strings at indices
- * CONTEXT_COMM and higher are allocated by us and should be freed at the
- * end.
+ * In the internal context[] array, fields before CONTEXT_COMM are the strings
+ * from argv[] passed by the kernel according to our pattern defined in
+ * /proc/sys/kernel/core_pattern (see man:core(5)). So they should not be
+ * freed. The strings at indices CONTEXT_COMM and higher are allocated by us and
+ * should be freed at the end.
*/
CONTEXT_PID,
CONTEXT_UID,
streq_ptr(context[CONTEXT_PID], "1");
}
-#define SUBMIT_COREDUMP_FIELDS 4
-
static int submit_coredump(
const char *context[_CONTEXT_MAX],
- struct iovec *iovec,
- size_t n_iovec_allocated,
- size_t n_iovec,
+ struct iovec_wrapper *iovw,
int input_fd) {
_cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
- _cleanup_free_ char *core_message = NULL, *filename = NULL, *coredump_data = NULL;
+ _cleanup_free_ char *filename = NULL, *coredump_data = NULL;
+ _cleanup_free_ char *stacktrace = NULL;
+ char *core_message;
uint64_t coredump_size = UINT64_MAX;
bool truncated = false, journald_crash;
int r;
assert(context);
- assert(iovec);
- assert(n_iovec_allocated >= n_iovec + SUBMIT_COREDUMP_FIELDS);
+ assert(iovw);
assert(input_fd >= 0);
journald_crash = is_journald_crash(context);
/* Skip whole core dumping part */
goto log;
- /* If we don't want to keep the coredump on disk, remove it now, as later on we will lack the privileges for
- * it. However, we keep the fd to it, so that we can still process it and log it. */
+ /* If we don't want to keep the coredump on disk, remove it now, as later on we
+ * will lack the privileges for it. However, we keep the fd to it, so that we can
+ * still process it and log it. */
r = maybe_remove_external_coredump(filename, coredump_size);
if (r < 0)
return r;
if (r == 0) {
- const char *coredump_filename;
+ iovw_put_string_field(iovw, "COREDUMP_FILENAME=", filename);
- coredump_filename = strjoina("COREDUMP_FILENAME=", filename);
- iovec[n_iovec++] = IOVEC_MAKE_STRING(coredump_filename);
} else if (arg_storage == COREDUMP_STORAGE_EXTERNAL)
log_info("The core will not be stored: size %"PRIu64" is greater than %"PRIu64" (the configured maximum)",
coredump_size, arg_external_size_max);
/* Vacuum again, but exclude the coredump we just created */
(void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use);
- /* Now, let's drop privileges to become the user who owns the segfaulted process and allocate the coredump
- * memory under the user's uid. This also ensures that the credentials journald will see are the ones of the
- * coredumping user, thus making sure the user gets access to the core dump. Let's also get rid of all
+ /* Now, let's drop privileges to become the user who owns the segfaulted process
+ * and allocate the coredump memory under the user's uid. This also ensures that
+ * the credentials journald will see are the ones of the coredumping user, thus
+ * making sure the user gets access to the core dump. Let's also get rid of all
* capabilities, if we run as root, we won't need them anymore. */
r = change_uid_gid(context);
if (r < 0)
#if HAVE_ELFUTILS
/* Try to get a stack trace if we can */
- if (coredump_size <= arg_process_size_max) {
- _cleanup_free_ char *stacktrace = NULL;
-
- r = coredump_make_stack_trace(coredump_fd, context[CONTEXT_EXE], &stacktrace);
- if (r >= 0)
- core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID],
- " (", context[CONTEXT_COMM], ") of user ",
- context[CONTEXT_UID], " dumped core.",
- journald_crash ? "\nCoredump diverted to " : "",
- journald_crash ? filename : "",
- "\n\n", stacktrace);
- else if (r == -EINVAL)
- log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
- else
- log_warning_errno(r, "Failed to generate stack trace: %m");
- } else
- log_debug("Not generating stack trace: core size %"PRIu64" is greater than %"PRIu64" (the configured maximum)",
+ if (coredump_size > arg_process_size_max) {
+ log_debug("Not generating stack trace: core size %"PRIu64" is greater "
+ "than %"PRIu64" (the configured maximum)",
coredump_size, arg_process_size_max);
-
- if (!core_message)
+ } else
+ coredump_make_stack_trace(coredump_fd, context[CONTEXT_EXE], &stacktrace);
#endif
+
log:
- core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID],
- " (", context[CONTEXT_COMM], ") of user ",
- context[CONTEXT_UID], " dumped core.",
- journald_crash && filename ? "\nCoredump diverted to " : NULL,
- journald_crash && filename ? filename : NULL);
- if (!core_message)
- return log_oom();
+ core_message = strjoina("Process ", context[CONTEXT_PID],
+ " (", context[CONTEXT_COMM], ") of user ",
+ context[CONTEXT_UID], " dumped core.",
+ journald_crash && filename ? "\nCoredump diverted to " : NULL,
+ journald_crash && filename ? filename : NULL);
+
+ core_message = strjoina(core_message, stacktrace ? "\n\n" : NULL, stacktrace);
if (journald_crash) {
/* We cannot log to the journal, so just print the message.
* The target was set previously to something safe. */
- assert(startswith(core_message, "MESSAGE="));
- log_dispatch(LOG_ERR, 0, core_message + strlen("MESSAGE="));
+ log_dispatch(LOG_ERR, 0, core_message);
return 0;
}
- iovec[n_iovec++] = IOVEC_MAKE_STRING(core_message);
+ iovw_put_string_field(iovw, "MESSAGE=", core_message);
if (truncated)
- iovec[n_iovec++] = IOVEC_MAKE_STRING("COREDUMP_TRUNCATED=1");
+ iovw_put_string_field(iovw, "COREDUMP_TRUNCATED=", "1");
/* Optionally store the entire coredump in the journal */
if (arg_storage == COREDUMP_STORAGE_JOURNAL) {
/* Store the coredump itself in the journal */
r = allocate_journal_field(coredump_fd, (size_t) coredump_size, &coredump_data, &sz);
- if (r >= 0)
- iovec[n_iovec++] = IOVEC_MAKE(coredump_data, sz);
- else
+ if (r >= 0) {
+ if (iovw_put(iovw, coredump_data, sz) >= 0)
+ TAKE_PTR(coredump_data);
+ } else
log_warning_errno(r, "Failed to attach the core to the journal entry: %m");
} else
log_info("The core will not be stored: size %"PRIu64" is greater than %"PRIu64" (the configured maximum)",
coredump_size, arg_journal_size_max);
}
- assert(n_iovec <= n_iovec_allocated);
-
- r = sd_journal_sendv(iovec, n_iovec);
+ r = sd_journal_sendv(iovw->iovec, iovw->count);
if (r < 0)
return log_error_errno(r, "Failed to log coredump: %m");
static int process_socket(int fd) {
_cleanup_close_ int coredump_fd = -1;
- struct iovec *iovec = NULL;
- size_t n_iovec = 0, n_allocated = 0, i, k;
const char *context[_CONTEXT_MAX] = {};
+ struct iovec_wrapper iovw = {};
+ struct iovec iovec;
+ size_t k;
int r;
assert(fd >= 0);
ssize_t n;
ssize_t l;
- if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + SUBMIT_COREDUMP_FIELDS)) {
- r = log_oom();
- goto finish;
- }
-
l = next_datagram_size_fd(fd);
if (l < 0) {
r = log_error_errno(l, "Failed to determine datagram size to read: %m");
goto finish;
}
- assert(l >= 0);
-
- iovec[n_iovec].iov_len = l;
- iovec[n_iovec].iov_base = malloc(l + 1);
- if (!iovec[n_iovec].iov_base) {
+ iovec.iov_len = l;
+ iovec.iov_base = malloc(l + 1);
+ if (!iovec.iov_base) {
r = log_oom();
goto finish;
}
- mh.msg_iov = iovec + n_iovec;
+ mh.msg_iov = &iovec;
n = recvmsg(fd, &mh, MSG_CMSG_CLOEXEC);
if (n < 0) {
- free(iovec[n_iovec].iov_base);
+ free(iovec.iov_base);
r = log_error_errno(errno, "Failed to receive datagram: %m");
goto finish;
}
+ /* The final zero-length datagram carries the file descriptor and tells us
+ * that we're done. */
if (n == 0) {
struct cmsghdr *cmsg, *found = NULL;
- /* The final zero-length datagram carries the file descriptor and tells us that we're done. */
- free(iovec[n_iovec].iov_base);
+ free(iovec.iov_base);
CMSG_FOREACH(cmsg, &mh) {
if (cmsg->cmsg_level == SOL_SOCKET &&
}
/* Add trailing NUL byte, in case these are strings */
- ((char*) iovec[n_iovec].iov_base)[n] = 0;
- iovec[n_iovec].iov_len = (size_t) n;
+ ((char*) iovec.iov_base)[n] = 0;
+ iovec.iov_len = (size_t) n;
- cmsg_close_all(&mh);
- map_context_fields(iovec + n_iovec, context);
- n_iovec++;
- }
+ r = iovw_put(&iovw, iovec.iov_base, iovec.iov_len);
+ if (r < 0)
+ goto finish;
- if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + SUBMIT_COREDUMP_FIELDS)) {
- r = log_oom();
- goto finish;
+ cmsg_close_all(&mh);
+ map_context_fields(&iovec, context);
}
/* Make sure we got all data we really need */
assert(context[CONTEXT_COMM]);
assert(coredump_fd >= 0);
- /* Small quirk: the journal fields contain the timestamp padded with six zeroes, so that the kernel-supplied 1s
- * granularity timestamps becomes 1µs granularity, i.e. the granularity systemd usually operates in. Since we
- * are reconstructing the original kernel context, we chop this off again, here. */
+ /* Small quirk: the journal fields contain the timestamp padded with six zeroes,
+ * so that the kernel-supplied 1s granularity timestamps becomes 1µs granularity,
+ * i.e. the granularity systemd usually operates in. Since we are reconstructing
+ * the original kernel context, we chop this off again, here. */
k = strlen(context[CONTEXT_TIMESTAMP]);
if (k > 6)
context[CONTEXT_TIMESTAMP] = strndupa(context[CONTEXT_TIMESTAMP], k - 6);
- r = submit_coredump(context, iovec, n_allocated, n_iovec, coredump_fd);
+ r = submit_coredump(context, &iovw, coredump_fd);
finish:
- for (i = 0; i < n_iovec; i++)
- free(iovec[i].iov_base);
- free(iovec);
-
+ iovw_free_contents(&iovw, true);
return r;
}
-static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd) {
+static int send_iovec(const struct iovec_wrapper *iovw, int input_fd) {
static const union sockaddr_union sa = {
.un.sun_family = AF_UNIX,
size_t i;
int r;
- assert(iovec || n_iovec <= 0);
+ assert(iovw);
assert(input_fd >= 0);
fd = socket(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0);
if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
return log_error_errno(errno, "Failed to connect to coredump service: %m");
- for (i = 0; i < n_iovec; i++) {
+ for (i = 0; i < iovw->count; i++) {
struct msghdr mh = {
- .msg_iov = (struct iovec*) iovec + i,
+ .msg_iov = iovw->iovec + i,
.msg_iovlen = 1,
};
struct iovec copy[2];
* own array, consisting of two new iovecs, where the first is a
* (truncated) copy of what we want to send, and the second one
* contains the trailing dots. */
- copy[0] = iovec[i];
+ copy[0] = iovw->iovec[i];
copy[1] = IOVEC_MAKE(((char[]){'.', '.', '.'}), 3);
mh.msg_iov = copy;
return 0;
}
-static char* set_iovec_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value) {
- char *x;
-
- x = set_iovec_string_field(iovec, n_iovec, field, value);
- free(value);
- return x;
-}
-
-static int gather_pid_metadata(
- char* context[_CONTEXT_MAX],
- char **comm_fallback,
- struct iovec *iovec, size_t *n_iovec) {
+static int gather_pid_metadata(char *context[_CONTEXT_MAX], struct iovec_wrapper *iovw) {
- /* We need 27 empty slots in iovec!
- *
- * Note that if we fail on oom later on, we do not roll-back changes to the iovec structure. (It remains valid,
- * with the first n_iovec fields initialized.) */
+ /* Note that if we fail on oom later on, we do not roll-back changes to the iovec
+ * structure. (It remains valid, with the first n_iovec fields initialized.) */
uid_t owner_uid;
pid_t pid;
return log_error_errno(r, "Failed to parse PID \"%s\": %m", context[CONTEXT_PID]);
r = get_process_comm(pid, &context[CONTEXT_COMM]);
- if (r < 0) {
- log_warning_errno(r, "Failed to get COMM, falling back to the command line: %m");
- context[CONTEXT_COMM] = strv_join(comm_fallback, " ");
- if (!context[CONTEXT_COMM])
- return log_oom();
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to get COMM: %m");
r = get_process_exe(pid, &context[CONTEXT_EXE]);
if (r < 0)
disable_coredumps();
}
- set_iovec_string_field(iovec, n_iovec, "COREDUMP_UNIT=", context[CONTEXT_UNIT]);
+ iovw_put_string_field(iovw, "COREDUMP_UNIT=", context[CONTEXT_UNIT]);
}
if (cg_pid_get_user_unit(pid, &t) >= 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_USER_UNIT=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_USER_UNIT=", t);
/* The next few are mandatory */
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_PID=", context[CONTEXT_PID]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_PID=", context[CONTEXT_PID]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_UID=", context[CONTEXT_UID]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_UID=", context[CONTEXT_UID]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_GID=", context[CONTEXT_GID]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_GID=", context[CONTEXT_GID]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_SIGNAL=", context[CONTEXT_SIGNAL]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_RLIMIT=", context[CONTEXT_RLIMIT]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_HOSTNAME=", context[CONTEXT_HOSTNAME]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_HOSTNAME=", context[CONTEXT_HOSTNAME]);
+ if (r < 0)
+ return r;
- if (!set_iovec_string_field(iovec, n_iovec, "COREDUMP_COMM=", context[CONTEXT_COMM]))
- return log_oom();
+ r = iovw_put_string_field(iovw, "COREDUMP_COMM=", context[CONTEXT_COMM]);
+ if (r < 0)
+ return r;
- if (context[CONTEXT_EXE] &&
- !set_iovec_string_field(iovec, n_iovec, "COREDUMP_EXE=", context[CONTEXT_EXE]))
- return log_oom();
+ if (context[CONTEXT_EXE]) {
+ r = iovw_put_string_field(iovw, "COREDUMP_EXE=", context[CONTEXT_EXE]);
+ if (r < 0)
+ return r;
+ }
+ /* The next are optional */
if (sd_pid_get_session(pid, &t) >= 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_SESSION=", t);
+ (void) iovw_put_string_field_free(iovw, "COREDUMP_SESSION=", t);
if (sd_pid_get_owner_uid(pid, &owner_uid) >= 0) {
- r = asprintf(&t, "COREDUMP_OWNER_UID=" UID_FMT, owner_uid);
+ r = asprintf(&t, UID_FMT, owner_uid);
if (r > 0)
- iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(t);
+ (void) iovw_put_string_field_free(iovw, "COREDUMP_OWNER_UID=", t);
}
if (sd_pid_get_slice(pid, &t) >= 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_SLICE=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_SLICE=", t);
if (get_process_cmdline(pid, SIZE_MAX, 0, &t) >= 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_CMDLINE=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_CMDLINE=", t);
if (cg_pid_get_path_shifted(pid, NULL, &t) >= 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_CGROUP=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_CGROUP=", t);
if (compose_open_fds(pid, &t) >= 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_OPEN_FDS=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_OPEN_FDS=", t);
p = procfs_file_alloca(pid, "status");
if (read_full_file(p, &t, NULL) >= 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_STATUS=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_PROC_STATUS=", t);
p = procfs_file_alloca(pid, "maps");
if (read_full_file(p, &t, NULL) >= 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_MAPS=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_PROC_MAPS=", t);
p = procfs_file_alloca(pid, "limits");
if (read_full_file(p, &t, NULL) >= 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_LIMITS=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_PROC_LIMITS=", t);
p = procfs_file_alloca(pid, "cgroup");
if (read_full_file(p, &t, NULL) >=0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_CGROUP=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_PROC_CGROUP=", t);
p = procfs_file_alloca(pid, "mountinfo");
if (read_full_file(p, &t, NULL) >=0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_PROC_MOUNTINFO=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_PROC_MOUNTINFO=", t);
if (get_process_cwd(pid, &t) >= 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_CWD=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_CWD=", t);
if (get_process_root(pid, &t) >= 0) {
bool proc_self_root_is_slash;
proc_self_root_is_slash = strcmp(t, "/") == 0;
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_ROOT=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_ROOT=", t);
/* If the process' root is "/", then there is a chance it has
* mounted own root and hence being containerized. */
if (proc_self_root_is_slash && get_process_container_parent_cmdline(pid, &t) > 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_CONTAINER_CMDLINE=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_CONTAINER_CMDLINE=", t);
}
if (get_process_environ(pid, &t) >= 0)
- set_iovec_field_free(iovec, n_iovec, "COREDUMP_ENVIRON=", t);
+ iovw_put_string_field_free(iovw, "COREDUMP_ENVIRON=", t);
- t = strjoin("COREDUMP_TIMESTAMP=", context[CONTEXT_TIMESTAMP], "000000");
- if (t)
- iovec[(*n_iovec)++] = IOVEC_MAKE_STRING(t);
+ t = strjoina(context[CONTEXT_TIMESTAMP], "000000");
+ (void) iovw_put_string_field(iovw, "COREDUMP_TIMESTAMP=", t);
if (safe_atoi(context[CONTEXT_SIGNAL], &signo) >= 0 && SIGNAL_VALID(signo))
- set_iovec_string_field(iovec, n_iovec, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo));
+ iovw_put_string_field(iovw, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(signo));
return 0; /* we successfully acquired all metadata */
}
static int process_kernel(int argc, char* argv[]) {
char* context[_CONTEXT_MAX] = {};
- struct iovec iovec[29 + SUBMIT_COREDUMP_FIELDS];
- size_t i, n_iovec, n_to_free = 0;
+ struct iovec_wrapper *iovw;
int r;
log_debug("Processing coredump received from the kernel...");
context[CONTEXT_RLIMIT] = argv[1 + CONTEXT_RLIMIT];
context[CONTEXT_HOSTNAME] = argv[1 + CONTEXT_HOSTNAME];
- r = gather_pid_metadata(context, argv + 1 + CONTEXT_COMM, iovec, &n_to_free);
+ iovw = iovw_new();
+ if (!iovw)
+ return log_oom();
+
+ r = gather_pid_metadata(context, iovw);
if (r < 0)
goto finish;
- n_iovec = n_to_free;
-
- iovec[n_iovec++] = IOVEC_MAKE_STRING("MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR);
-
- assert_cc(2 == LOG_CRIT);
- iovec[n_iovec++] = IOVEC_MAKE_STRING("PRIORITY=2");
-
- assert(n_iovec <= ELEMENTSOF(iovec));
+ iovw_put_string_field(iovw, "MESSAGE_ID=", SD_MESSAGE_COREDUMP_STR);
+ iovw_put_string_field(iovw, "PRIORITY=", STRINGIFY(LOG_CRIT));
if (is_journald_crash((const char**) context) || is_pid1_crash((const char**) context))
- r = submit_coredump((const char**) context,
- iovec, ELEMENTSOF(iovec), n_iovec,
- STDIN_FILENO);
+ r = submit_coredump((const char**) context, iovw, STDIN_FILENO);
else
- r = send_iovec(iovec, n_iovec, STDIN_FILENO);
+ r = send_iovec(iovw, STDIN_FILENO);
finish:
- for (i = 0; i < n_to_free; i++)
- free(iovec[i].iov_base);
+ iovw = iovw_free_free(iovw);
/* Those fields are allocated by gather_pid_metadata */
free(context[CONTEXT_COMM]);
static int process_backtrace(int argc, char *argv[]) {
char *context[_CONTEXT_MAX] = {};
- _cleanup_free_ char *message = NULL;
- _cleanup_free_ struct iovec *iovec = NULL;
- size_t n_iovec, n_allocated, n_to_free = 0, i;
+ struct iovec_wrapper *iovw;
+ char *message;
+ size_t i;
int r;
- JournalImporter importer = {
- .fd = STDIN_FILENO,
- };
+ _cleanup_(journal_importer_cleanup) JournalImporter importer = JOURNAL_IMPORTER_INIT(STDIN_FILENO);
log_debug("Processing backtrace on stdin...");
- if (argc < CONTEXT_COMM + 1)
+ if (argc < CONTEXT_COMM + 2)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Not enough arguments passed (%i, expected %i).",
- argc - 1, CONTEXT_COMM + 1 - 1);
+ argc - 1, CONTEXT_COMM + 2 - 1);
context[CONTEXT_PID] = argv[2 + CONTEXT_PID];
context[CONTEXT_UID] = argv[2 + CONTEXT_UID];
context[CONTEXT_RLIMIT] = argv[2 + CONTEXT_RLIMIT];
context[CONTEXT_HOSTNAME] = argv[2 + CONTEXT_HOSTNAME];
- n_allocated = 34 + COREDUMP_STORAGE_EXTERNAL;
- /* 26 metadata, 2 static, +unknown input, 4 storage, rounded up */
- iovec = new(struct iovec, n_allocated);
- if (!iovec)
+ iovw = iovw_new();
+ if (!iovw)
return log_oom();
- r = gather_pid_metadata(context, argv + 2 + CONTEXT_COMM, iovec, &n_to_free);
+ r = gather_pid_metadata(context, iovw);
if (r < 0)
goto finish;
- if (r > 0) {
- /* This was a special crash, and has already been processed. */
- r = 0;
- goto finish;
- }
- n_iovec = n_to_free;
+
+ iovw_put_string_field(iovw, "MESSAGE_ID=", SD_MESSAGE_BACKTRACE_STR);
+ iovw_put_string_field(iovw, "PRIORITY=", STRINGIFY(LOG_CRIT));
for (;;) {
r = journal_importer_process_data(&importer);
break;
}
- if (!GREEDY_REALLOC(iovec, n_allocated, n_iovec + importer.iovw.count + 2))
- return log_oom();
-
if (journal_importer_eof(&importer)) {
log_warning("Did not receive a full journal entry on stdin, ignoring message sent by reporter");
- message = strjoin("MESSAGE=Process ", context[CONTEXT_PID],
+ message = strjoina("Process ", context[CONTEXT_PID],
" (", context[CONTEXT_COMM], ")"
" of user ", context[CONTEXT_UID],
" failed with ", context[CONTEXT_SIGNAL]);
- if (!message) {
- r = log_oom();
- goto finish;
- }
- iovec[n_iovec++] = IOVEC_MAKE_STRING(message);
+
+ r = iovw_put_string_field(iovw, "MESSAGE=", message);
+ if (r < 0)
+ return r;
} else {
- for (i = 0; i < importer.iovw.count; i++)
- iovec[n_iovec++] = importer.iovw.iovec[i];
- }
+ /* The imported iovecs are not supposed to be freed by us so let's store
+ * them at the end of the array so we can skip them while freeing the
+ * rest. */
- iovec[n_iovec++] = IOVEC_MAKE_STRING("MESSAGE_ID=" SD_MESSAGE_BACKTRACE_STR);
- assert_cc(2 == LOG_CRIT);
- iovec[n_iovec++] = IOVEC_MAKE_STRING("PRIORITY=2");
+ for (i = 0; i < importer.iovw.count; i++) {
+ struct iovec *iovec = importer.iovw.iovec + i;
- assert(n_iovec <= n_allocated);
+ iovw_put(iovw, iovec->iov_base, iovec->iov_len);
+ }
+ }
- r = sd_journal_sendv(iovec, n_iovec);
+ r = sd_journal_sendv(iovw->iovec, iovw->count);
if (r < 0)
log_error_errno(r, "Failed to log backtrace: %m");
finish:
- for (i = 0; i < n_to_free; i++)
- free(iovec[i].iov_base);
+ iovw->count -= importer.iovw.count;
+ iovw = iovw_free_free(iovw);
/* Those fields are allocated by gather_pid_metadata */
free(context[CONTEXT_COMM]);
if (r < 0)
return log_error_errno(r, "Failed to acquire temporary directory path: %m");
- temp = strjoin(vt, "/coredump-XXXXXX");
+ temp = path_join(vt, "coredump-XXXXXX");
if (!temp)
return log_oom();
return DWARF_CB_OK;
}
-int coredump_make_stack_trace(int fd, const char *executable, char **ret) {
+static int make_stack_trace(int fd, const char *executable, char **ret) {
static const Dwfl_Callbacks callbacks = {
.find_elf = dwfl_build_id_find_elf,
return r;
}
+
+void coredump_make_stack_trace(int fd, const char *executable, char **ret) {
+ int r;
+
+ r = make_stack_trace(fd, executable, ret);
+ if (r == -EINVAL)
+ log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno()));
+ else if (r < 0)
+ log_warning_errno(r, "Failed to generate stack trace: %m");
+}
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-int coredump_make_stack_trace(int fd, const char *executable, char **ret);
+void coredump_make_stack_trace(int fd, const char *executable, char **ret);
if (r < 0)
return log_error_errno(r, "Failed to generate keydev mount unit: %m");
- p = prefix_root(keydev_mount, password_escaped);
+ p = path_join(keydev_mount, password_escaped);
if (!p)
return log_oom();
continue;
}
- uuid = STARTSWITH_SET(device, "UUID=", "luks-");
+ uuid = startswith(device, "UUID=");
if (!uuid)
uuid = path_startswith(device, "/dev/disk/by-uuid/");
+ if (!uuid)
+ uuid = startswith(name, "luks-");
if (uuid)
d = hashmap_get(arg_disks, uuid);
/* for CRYPT_PLAIN limit reads from keyfile to key length, and ignore keyfile-size */
arg_keyfile_size = arg_key_size;
- /* In contrast to what the name crypt_setup() might suggest this doesn't actually format
+ /* In contrast to what the name crypt_format() might suggest this doesn't actually format
* anything, it just configures encryption parameters when used for plain mode. */
r = crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, arg_keyfile_size, ¶ms);
if (r < 0)
log_setup_service();
+ crypt_set_log_callback(NULL, cryptsetup_log_glue, NULL);
+ if (DEBUG_LOGGING)
+ /* libcryptsetup won't even consider debug messages by default */
+ crypt_set_debug_level(CRYPT_DEBUG_ALL);
+
umask(0022);
if (streq(argv[1], "attach")) {
#include <unistd.h>
#include "alloc-util.h"
+#include "dropin.h"
#include "generator.h"
#include "mkdir.h"
#include "parse-util.h"
+#include "path-util.h"
#include "proc-cmdline.h"
#include "special.h"
#include "string-util.h"
static char *arg_default_unit = NULL;
static char **arg_mask = NULL;
static char **arg_wants = NULL;
-static bool arg_debug_shell = false;
+static char *arg_debug_shell = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_default_unit, freep);
STATIC_DESTRUCTOR_REGISTER(arg_mask, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_wants, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_debug_shell, freep);
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
int r;
return log_oom();
} else if (proc_cmdline_key_streq(key, "systemd.debug_shell")) {
+ const char *t = NULL;
- if (value) {
- r = parse_boolean(value);
- if (r < 0)
- log_error("Failed to parse systemd.debug_shell= argument '%s', ignoring.", value);
- else
- arg_debug_shell = r;
- } else
- arg_debug_shell = true;
+ r = value ? parse_boolean(value) : 1;
+ if (r < 0)
+ t = skip_dev_prefix(value);
+ else if (r > 0)
+ t = skip_dev_prefix(DEBUGTTY);
+
+ if (free_and_strdup(&arg_debug_shell, t) < 0)
+ return log_oom();
} else if (streq(key, "systemd.unit")) {
STRV_FOREACH(u, arg_mask) {
_cleanup_free_ char *p = NULL;
- p = strjoin(arg_dest, "/", *u);
+ p = path_join(empty_to_root(arg_dest), *u);
if (!p)
return log_oom();
return r;
}
+static void install_debug_shell_dropin(const char *dir) {
+ int r;
+
+ if (streq(arg_debug_shell, skip_dev_prefix(DEBUGTTY)))
+ return;
+
+ r = write_drop_in_format(dir, "debug-shell.service", 50, "tty",
+ "[Unit]\n"
+ "Description=Early root shell on /dev/%s FOR DEBUGGING ONLY\n"
+ "ConditionPathExists=\n"
+ "[Service]\n"
+ "TTYPath=/dev/%s",
+ arg_debug_shell, arg_debug_shell);
+ if (r < 0)
+ log_warning_errno(r, "Failed to write drop-in for debug-shell.service, ignoring: %m");
+}
+
static int run(const char *dest, const char *dest_early, const char *dest_late) {
int r, q;
r = strv_extend(&arg_wants, "debug-shell.service");
if (r < 0)
return log_oom();
+
+ install_debug_shell_dropin(arg_dest);
}
r = generate_mask_symlinks();
assert(!endswith(drop, "/"));
- path = strjoin(toppath, "/", drop);
+ path = path_join(toppath, drop);
if (!path)
return -ENOMEM;
if (!endswith(*file, ".conf"))
continue;
- p = strjoin(path, "/", *file);
+ p = path_join(path, *file);
if (!p)
return -ENOMEM;
d = p + strlen(toppath) + 1;
STRV_FOREACH(t, files) {
_cleanup_free_ char *p = NULL;
- p = strjoin(path, "/", *t);
+ p = path_join(path, *t);
if (!p)
return -ENOMEM;
return 0;
}
-static bool should_skip_path(const char *prefix, const char *suffix) {
+static int should_skip_path(const char *prefix, const char *suffix) {
#if HAVE_SPLIT_USR
_cleanup_free_ char *target = NULL;
- const char *p;
- char *dirname;
+ const char *dirname, *p;
- dirname = strjoina(prefix, "/", suffix);
+ dirname = prefix_roota(prefix, suffix);
if (chase_symlinks(dirname, NULL, 0, &target) < 0)
return false;
NULSTR_FOREACH(p, prefixes) {
+ _cleanup_free_ char *tmp = NULL;
+
if (path_startswith(dirname, p))
continue;
- if (path_equal(target, strjoina(p, "/", suffix))) {
+ tmp = path_join(p, suffix);
+ if (!tmp)
+ return -ENOMEM;
+
+ if (path_equal(target, tmp)) {
log_debug("%s redirects to %s, skipping.", dirname, target);
return true;
}
NULSTR_FOREACH(p, prefixes) {
_cleanup_free_ char *t = NULL;
- if (should_skip_path(p, suffix))
+ if (should_skip_path(p, suffix) > 0)
continue;
- t = strjoin(p, "/", suffix);
+ t = path_join(p, suffix);
if (!t) {
r = -ENOMEM;
goto finish;
#include "kbd-util.h"
#include "locale-util.h"
#include "main-func.h"
+#include "memory-util.h"
#include "mkdir.h"
#include "os-util.h"
#include "parse-util.h"
STATIC_DESTRUCTOR_REGISTER(arg_keymap, freep);
STATIC_DESTRUCTOR_REGISTER(arg_timezone, freep);
STATIC_DESTRUCTOR_REGISTER(arg_hostname, freep);
-STATIC_DESTRUCTOR_REGISTER(arg_root_password, string_free_erasep);
+STATIC_DESTRUCTOR_REGISTER(arg_root_password, erase_and_freep);
static bool press_any_key(void) {
char k = 0;
#include "device-util.h"
#include "fd-util.h"
#include "fs-util.h"
+#include "fsck-util.h"
#include "main-func.h"
#include "parse-util.h"
#include "path-util.h"
#include "stdio-util.h"
#include "util.h"
-/* exit codes as defined in fsck(8) */
-enum {
- FSCK_SUCCESS = 0,
- FSCK_ERROR_CORRECTED = 1 << 0,
- FSCK_SYSTEM_SHOULD_REBOOT = 1 << 1,
- FSCK_ERRORS_LEFT_UNCORRECTED = 1 << 2,
- FSCK_OPERATIONAL_ERROR = 1 << 3,
- FSCK_USAGE_OR_SYNTAX_ERROR = 1 << 4,
- FSCK_USER_CANCELLED = 1 << 5,
- FSCK_SHARED_LIB_ERROR = 1 << 7,
-};
-
static bool arg_skip = false;
static bool arg_force = false;
static bool arg_show_progress = false;
"sclp_line0",
"ttysclp0",
"3270!tty1") {
- const char *p;
+ _cleanup_free_ char *p = NULL;
- p = strjoina("/sys/class/tty/", j);
+ p = path_join("/sys/class/tty", j);
+ if (!p)
+ return -ENOMEM;
if (access(p, F_OK) < 0)
continue;
if (!what_escaped)
return log_oom();
- p = strjoina(arg_dest, "/", n);
+ p = prefix_roota(arg_dest, n);
f = fopen(p, "wxe");
if (!f)
return log_error_errno(errno, "Failed to create unit file %s: %m", p);
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- p = strjoin(arg_dest, "/", unit);
+ p = path_join(empty_to_root(arg_dest), unit);
if (!p)
return log_oom();
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- unit = strjoin(arg_dest, "/", name);
+ unit = path_join(empty_to_root(arg_dest), name);
if (!unit)
return log_oom();
if (r < 0)
return log_error_errno(r, "Failed to generate unit name: %m");
- p = strjoina(arg_dest, "/", unit);
+ p = prefix_roota(arg_dest, unit);
f = fopen(p, "wxe");
if (!f)
return log_error_errno(errno, "Failed to create unit file %s: %m", unit);
static const char *arg_dest = "/tmp";
static char *arg_resume_device = NULL;
+static char *arg_resume_options = NULL;
+static char *arg_root_options = NULL;
static bool arg_noresume = false;
STATIC_DESTRUCTOR_REGISTER(arg_resume_device, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_resume_options, freep);
+STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
free_and_replace(arg_resume_device, s);
+ } else if (streq(key, "resumeflags")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ if (!strextend_with_separator(&arg_resume_options, ",", value, NULL))
+ return log_oom();
+
+ } else if (streq(key, "rootflags")) {
+
+ if (proc_cmdline_value_missing(key, value))
+ return 0;
+
+ if (!strextend_with_separator(&arg_root_options, ",", value, NULL))
+ return log_oom();
+
} else if (streq(key, "noresume")) {
if (value) {
log_warning("\"noresume\" kernel command line switch specified with an argument, ignoring.");
static int process_resume(void) {
_cleanup_free_ char *name = NULL, *lnk = NULL;
+ const char *opts;
int r;
if (!arg_resume_device)
if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-hibernate-resume@.service", lnk) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
+ if (arg_resume_options)
+ opts = arg_resume_options;
+ else
+ opts = arg_root_options;
+
+ r = generator_write_timeouts(arg_dest, arg_resume_device, arg_resume_device, opts, NULL);
+ if (r < 0)
+ return r;
+
return 0;
}
static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
CurlGlue *g = userdata;
- int action, k = 0, translated_fd;
+ int action, k = 0;
assert(s);
assert(g);
- translated_fd = PTR_TO_FD(hashmap_get(g->translate_fds, FD_TO_PTR(fd)));
-
if (FLAGS_SET(revents, EPOLLIN | EPOLLOUT))
action = CURL_POLL_INOUT;
else if (revents & EPOLLIN)
else
action = 0;
- if (curl_multi_socket_action(g->curl, translated_fd, action, &k) != CURLM_OK)
+ if (curl_multi_socket_action(g->curl, fd, action, &k) != CURLM_OK)
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
"Failed to propagate IO event.");
}
static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, void *userdata, void *socketp) {
- sd_event_source *io;
+ sd_event_source *io = socketp;
CurlGlue *g = userdata;
uint32_t events = 0;
int r;
assert(curl);
assert(g);
- io = hashmap_get(g->ios, FD_TO_PTR(s));
-
if (action == CURL_POLL_REMOVE) {
if (io) {
- int fd;
-
- fd = sd_event_source_get_io_fd(io);
- assert(fd >= 0);
-
sd_event_source_disable_unref(io);
hashmap_remove(g->ios, FD_TO_PTR(s));
- hashmap_remove(g->translate_fds, FD_TO_PTR(fd));
-
- safe_close(fd);
}
return 0;
return -1;
}
- r = hashmap_ensure_allocated(&g->translate_fds, &trivial_hash_ops);
- if (r < 0) {
- log_oom();
- return -1;
- }
-
if (action == CURL_POLL_IN)
events = EPOLLIN;
else if (action == CURL_POLL_OUT)
if (sd_event_source_set_enabled(io, SD_EVENT_ON) < 0)
return -1;
} else {
- _cleanup_close_ int fd = -1;
-
- /* When curl needs to remove an fd from us it closes
- * the fd first, and only then calls into us. This is
- * nasty, since we cannot pass the fd on to epoll()
- * anymore. Hence, duplicate the fds here, and keep a
- * copy for epoll which we control after use. */
-
- fd = fcntl(s, F_DUPFD_CLOEXEC, 3);
- if (fd < 0)
+ if (sd_event_add_io(g->event, &io, s, events, curl_glue_on_io, g) < 0)
return -1;
- if (sd_event_add_io(g->event, &io, fd, events, curl_glue_on_io, g) < 0)
+ if (curl_multi_assign(g->curl, s, io) != CURLM_OK)
return -1;
(void) sd_event_source_set_description(io, "curl-io");
sd_event_source_unref(io);
return -1;
}
-
- r = hashmap_put(g->translate_fds, FD_TO_PTR(fd), FD_TO_PTR(s));
- if (r < 0) {
- log_oom();
- hashmap_remove(g->ios, FD_TO_PTR(s));
- sd_event_source_unref(io);
- return -1;
- }
-
- fd = -1;
}
return 0;
curl_multi_cleanup(g->curl);
while ((io = hashmap_steal_first(g->ios))) {
- int fd;
-
- fd = sd_event_source_get_io_fd(io);
- assert(fd >= 0);
-
- hashmap_remove(g->translate_fds, FD_TO_PTR(fd));
-
- safe_close(fd);
sd_event_source_unref(io);
}
CURLM *curl;
sd_event_source *timer;
Hashmap *ios;
- Hashmap *translate_fds;
void (*on_finished)(CurlGlue *g, CURL *curl, CURLcode code);
void *userdata;
return 0;
}
- joined = strjoina(path, "/", child);
+ joined = prefix_roota(path, child);
r = path_is_os_tree(joined);
if (r == -ENOTDIR) {
log_debug("Directory '%s' does not look like a directory tree, and contains a single regular file only, leaving as it is.", path);
#include "alloc-util.h"
#include "btrfs-util.h"
#include "fd-util.h"
+#include "format-util.h"
#include "fs-util.h"
#include "hostname-util.h"
#include "import-common.h"
#include "import-util.h"
#include "machine-image.h"
#include "mkdir.h"
-#include "parse-util.h"
#include "ratelimit.h"
#include "rm-rf.h"
#include "string-util.h"
log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
}
- final_path = strjoina(arg_image_root, "/", local);
+ final_path = prefix_roota(arg_image_root, local);
r = tempfn_random(final_path, NULL, &temp_path);
if (r < 0)
assert(!i->temp_path);
assert(i->tar_fd < 0);
- i->final_path = strjoin(i->image_root, "/", i->local);
+ i->final_path = path_join(i->image_root, i->local);
if (!i->final_path)
return log_oom();
}
FOREACH_DIRENT_ALL(de, d, return -errno) {
+ _cleanup_free_ char *u = NULL;
const char *a, *b;
- char *u;
if (de->d_type != DT_UNKNOWN &&
de->d_type != dt)
if (r < 0)
return r;
- if (!http_etag_is_valid(u)) {
- free(u);
+ if (!http_etag_is_valid(u))
continue;
- }
- r = strv_consume(&l, u);
+ r = strv_consume(&l, TAKE_PTR(u));
if (r < 0)
return r;
}
if (!image_root)
image_root = "/var/lib/machines";
- p = strjoina(image_root, "/", local);
+ p = prefix_roota(image_root, local);
if (force_local)
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
#include "alloc-util.h"
#include "fd-util.h"
+#include "format-util.h"
#include "gcrypt-util.h"
#include "hexdecoct.h"
#include "import-util.h"
function machineLoad() {
var request = new XMLHttpRequest();
- request.open("GET", "/machine");
+ request.open("GET", "machine");
request.onreadystatechange = machineOnResult;
request.setRequestHeader("Accept", "application/json");
request.send(null);
range = "";
}
- var url = "/entries";
+ var url = "entries";
if (localStorage["filter"] != "" && localStorage["filter"] != null) {
url += "?_SYSTEMD_UNIT=" + escape(localStorage["filter"]);
function onMessageClick(t) {
var request = new XMLHttpRequest();
- request.open("GET", "/entries?discrete");
+ request.open("GET", "entries?discrete");
request.onreadystatechange = onResultMessageClick;
request.setRequestHeader("Accept", "application/json");
request.setRequestHeader("Range", "entries=" + t + ":0:1");
function onFilterFocus(w) {
var request = new XMLHttpRequest();
- request.open("GET", "/fields/_SYSTEMD_UNIT");
+ request.open("GET", "fields/_SYSTEMD_UNIT");
request.onreadystatechange = onResultFilterFocus;
request.setRequestHeader("Accept", "application/json");
request.send(null);
#include "alloc-util.h"
#include "bus-util.h"
+#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "hostname-util.h"
errno = 0;
k = fread(buf, 1, n, m->tmp);
if (k != n) {
- log_error("Failed to read from file: %s", errno ? strerror(errno) : "Premature EOF");
+ log_error("Failed to read from file: %s", errno != 0 ? strerror_safe(errno) : "Premature EOF");
return MHD_CONTENT_READER_END_WITH_ERROR;
}
errno = 0;
k = fread(buf, 1, n, m->tmp);
if (k != n) {
- log_error("Failed to read from file: %s", errno ? strerror(errno) : "Premature EOF");
+ log_error("Failed to read from file: %s", errno != 0 ? strerror_safe(errno) : "Premature EOF");
return MHD_CONTENT_READER_END_WITH_ERROR;
}
_cleanup_strv_free_ char **words = NULL;
assert(getter);
- r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_QUOTES);
+ r = strv_split_extract(&words, getter, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return log_error_errno(r, "Failed to split getter option: %m");
if (!source)
return NULL;
- source->importer.fd = fd;
+ source->importer = JOURNAL_IMPORTER_MAKE(fd);
source->importer.passive_fd = passive_fd;
source->importer.name = name;
install_data('browse.html',
install_dir : join_paths(pkgdatadir, 'gatewayd'))
- meson.add_install_script('sh', '-c',
- mkdir_p.format('/var/log/journal/remote'))
- meson.add_install_script('sh', '-c',
- '''chown 0:0 $DESTDIR/var/log/journal/remote &&
- chmod 755 $DESTDIR/var/log/journal/remote || :''')
+ if get_option('create-log-dirs')
+ meson.add_install_script('sh', '-c',
+ mkdir_p.format('/var/log/journal/remote'))
+ meson.add_install_script('sh', '-c',
+ '''chown 0:0 $DESTDIR/var/log/journal/remote &&
+ chmod 755 $DESTDIR/var/log/journal/remote || :''')
+ endif
endif
#include "chattr-util.h"
#include "compress.h"
#include "fd-util.h"
+#include "format-util.h"
#include "fs-util.h"
#include "journal-authenticate.h"
#include "journal-def.h"
#include "journal-file.h"
#include "lookup3.h"
#include "memory-util.h"
-#include "parse-util.h"
#include "path-util.h"
#include "random-util.h"
#include "set.h"
#include "alloc-util.h"
#include "dirent-util.h"
#include "fd-util.h"
+#include "format-util.h"
#include "fs-util.h"
#include "journal-def.h"
#include "journal-file.h"
#include "journal-vacuum.h"
-#include "parse-util.h"
#include "sort-util.h"
#include "string-util.h"
#include "time-util.h"
#include "device-private.h"
#include "fd-util.h"
#include "fileio.h"
+#include "format-util.h"
#include "fs-util.h"
#include "fsprg.h"
#include "glob-util.h"
r = free_and_strdup(&arg_verify_key, optarg);
if (r < 0)
return r;
- /* Use memset not string_erase so this doesn't look confusing
+ /* Use memset not explicit_bzero() or similar so this doesn't look confusing
* in ps or htop output. */
memset(optarg, 'x', strlen(optarg));
r = get_boots(j, NULL, &boot_id, arg_boot_offset);
assert(r <= 1);
if (r <= 0) {
- const char *reason = (r == 0) ? "No such boot ID in journal" : strerror(-r);
+ const char *reason = (r == 0) ? "No such boot ID in journal" : strerror_safe(r);
if (sd_id128_is_null(arg_boot_id))
log_error("Data from the specified boot (%+i) is not available: %s",
assert(j);
STRV_FOREACH(i, arg_syslog_identifier) {
- char *u;
+ _cleanup_free_ char *u = NULL;
- u = strjoina("SYSLOG_IDENTIFIER=", *i);
+ u = strjoin("SYSLOG_IDENTIFIER=", *i);
+ if (!u)
+ return -ENOMEM;
r = sd_journal_add_match(j, u, 0);
if (r < 0)
return r;
if (handle_msg) {
v = startswith(p, "msg='");
if (v) {
+ _cleanup_free_ char *c = NULL;
const char *e;
- char *c;
/* Userspace message. It's enclosed in
simple quotation marks, is not
if (!e)
return 0; /* don't continue splitting up if the final quotation mark is missing */
- c = strndupa(v, e - v);
+ c = strndup(v, e - v);
+ if (!c)
+ return -ENOMEM;
+
return map_all_fields(c, map_fields_userspace, "AUDIT_FIELD_", false, iov, n_iov_allocated, n_iov);
}
}
startswith(p, "SYSLOG_IDENTIFIER=")) {
char *t;
- t = strndup(p + 18, l - 18);
+ t = memdup_suffix0(p + 18, l - 18);
if (t) {
free(*identifier);
*identifier = t;
startswith(p, "MESSAGE=")) {
char *t;
- t = strndup(p + 8, l - 8);
+ t = memdup_suffix0(p + 8, l - 8);
if (t) {
free(*message);
*message = t;
/* We failed to format the message. Emit a warning instead. */
char buf[LINE_MAX];
- xsprintf(buf, "MESSAGE=Entry printing failed: %s", strerror(-r));
+ xsprintf(buf, "MESSAGE=Entry printing failed: %s", strerror_safe(r));
n = 3;
iovec[n++] = IOVEC_MAKE_STRING("PRIORITY=4");
server_cache_boot_id(s);
server_cache_machine_id(s);
- s->runtime_storage.path = strjoin("/run/log/journal/", SERVER_MACHINE_ID(s));
- s->system_storage.path = strjoin("/var/log/journal/", SERVER_MACHINE_ID(s));
+ s->runtime_storage.path = path_join("/run/log/journal", SERVER_MACHINE_ID(s));
+ s->system_storage.path = path_join("/var/log/journal", SERVER_MACHINE_ID(s));
if (!s->runtime_storage.path || !s->system_storage.path)
return -ENOMEM;
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER
+#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER && !HAS_FEATURE_MEMORY_SANITIZER
switch(length)
{
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER
+#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER && !HAS_FEATURE_MEMORY_SANITIZER
switch(length)
{
* still catch it and complain. The masking trick does make the hash
* noticeably faster for short strings (like English words).
*/
-#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER
+#if !VALGRIND && !HAS_FEATURE_ADDRESS_SANITIZER && !HAS_FEATURE_MEMORY_SANITIZER
switch(length)
{
install_data('journald.conf',
install_dir : pkgsysconfdir)
-meson.add_install_script(
- 'sh', '-c',
- mkdir_p.format('/var/log/journal'))
-meson.add_install_script(
- 'sh', '-c',
- '''chown 0:0 $DESTDIR/var/log/journal &&
- chmod 755 $DESTDIR/var/log/journal || :''')
-if get_option('adm-group')
+if get_option('create-log-dirs')
meson.add_install_script(
'sh', '-c',
- 'setfacl -nm g:adm:rx,d:g:adm:rx $DESTDIR/var/log/journal || :')
-endif
-if get_option('wheel-group')
+ mkdir_p.format('/var/log/journal'))
meson.add_install_script(
'sh', '-c',
- 'setfacl -nm g:wheel:rx,d:g:wheel:rx $DESTDIR/var/log/journal || :')
+ '''chown 0:0 $DESTDIR/var/log/journal &&
+ chmod 755 $DESTDIR/var/log/journal || :''')
+ if get_option('adm-group')
+ meson.add_install_script(
+ 'sh', '-c',
+ 'setfacl -nm g:adm:rx,d:g:adm:rx $DESTDIR/var/log/journal || :')
+ endif
+ if get_option('wheel-group')
+ meson.add_install_script(
+ 'sh', '-c',
+ 'setfacl -nm g:wheel:rx,d:g:wheel:rx $DESTDIR/var/log/journal || :')
+ endif
endif
if (!file_type_wanted(j->flags, filename))
return 0;
- path = strjoina(prefix, "/", filename);
+ path = prefix_roota(prefix, filename);
return add_any_file(j, -1, path);
}
assert(prefix);
assert(filename);
- path = strjoina(prefix, "/", filename);
+ path = prefix_roota(prefix, filename);
f = ordered_hashmap_get(j->files, path);
if (!f)
return;
/* Adds a journal file directory to watch. If the directory is already tracked this updates the inotify watch
* and reenumerates directory contents */
- if (dirname)
- path = strjoin(prefix, "/", dirname);
- else
- path = strdup(prefix);
+ path = path_join(prefix, dirname);
if (!path) {
r = -ENOMEM;
goto fail;
do { \
int _r_ = (expr); \
if (_unlikely_(_r_ < 0)) \
- log_assert_errno(#expr, -_r_, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ log_assert_errno(#expr, -_r_, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \
} while (false)
static JournalFile *test_open(const char *name) {
/* device is under renaming */
return -EBUSY;
- name = net_get_name(device);
+ name = net_get_name_persistent(device);
}
}
#define DHCP_CLIENT_DONT_DESTROY(client) \
_cleanup_(sd_dhcp_client_unrefp) _unused_ sd_dhcp_client *_dont_destroy_##client = sd_dhcp_client_ref(client)
-#define log_dhcp_client_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
+#define log_dhcp_client_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "DHCP CLIENT (0x%x): " fmt, client->xid, ##__VA_ARGS__)
#define log_dhcp_client(client, fmt, ...) log_dhcp_client_errno(client, 0, fmt, ##__VA_ARGS__)
if (memchr(option, 0, len - 1))
return -EINVAL;
- string = strndup((const char *) option, len);
+ string = memdup_suffix0((const char *) option, len);
if (!string)
return -ENOMEM;
uint32_t lifetime;
} DHCPRequest;
-#define log_dhcp_server(client, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "DHCP SERVER: " fmt, ##__VA_ARGS__)
-#define log_dhcp_server_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCP SERVER: " fmt, ##__VA_ARGS__)
+#define log_dhcp_server(client, fmt, ...) log_internal(LOG_DEBUG, 0, PROJECT_FILE, __LINE__, __func__, "DHCP SERVER: " fmt, ##__VA_ARGS__)
+#define log_dhcp_server_errno(client, error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "DHCP SERVER: " fmt, ##__VA_ARGS__)
int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message,
size_t length);
typedef struct DHCP6IA DHCP6IA;
-#define log_dhcp6_client_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
+#define log_dhcp6_client_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "DHCPv6 CLIENT: " fmt, ##__VA_ARGS__)
#define log_dhcp6_client(p, fmt, ...) log_dhcp6_client_errno(p, 0, fmt, ##__VA_ARGS__)
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
struct ether_addr filter_address;
};
-#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
+#define log_lldp_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "LLDP: " fmt, ##__VA_ARGS__)
#define log_lldp(fmt, ...) log_lldp_errno(0, fmt, ##__VA_ARGS__)
const char* lldp_event_to_string(sd_lldp_event e) _const_;
void *userdata;
};
-#define log_ndisc_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "NDISC: " fmt, ##__VA_ARGS__)
+#define log_ndisc_errno(error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "NDISC: " fmt, ##__VA_ARGS__)
#define log_ndisc(fmt, ...) log_ndisc_errno(0, fmt, ##__VA_ARGS__)
const char* ndisc_event_to_string(sd_ndisc_event e) _const_;
#include "conf-parser.h"
#include "device-util.h"
#include "dhcp-lease-internal.h"
+#include "env-util.h"
#include "ether-addr-util.h"
#include "hexdecoct.h"
#include "log.h"
#include "utf8.h"
#include "util.h"
-const char *net_get_name(sd_device *device) {
+const char *net_get_name_persistent(sd_device *device) {
const char *name, *field;
assert(device);
#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a)
-int net_get_unique_predictable_data(sd_device *device, uint64_t *result) {
+int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *result) {
size_t l, sz = 0;
const char *name;
int r;
assert(device);
- /* net_get_name() will return one of the device names based on stable information about the
- * device. If this is not available, we fall back to using the device name. */
- name = net_get_name(device);
- if (!name)
+ /* net_get_name_persistent() will return one of the device names based on stable information about
+ * the device. If this is not available, we fall back to using the actual device name. */
+ name = net_get_name_persistent(device);
+ if (!name && use_sysname)
(void) sd_device_get_sysname(device, &name);
if (!name)
return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA),
return 0;
}
-static bool net_condition_test_strv(char * const *raw_patterns,
- const char *string) {
- if (strv_isempty(raw_patterns))
+static bool net_condition_test_strv(char * const *patterns, const char *string) {
+ char * const *p;
+ bool match = false, has_positive_rule = false;
+
+ if (strv_isempty(patterns))
return true;
- /* If the patterns begin with "!", edit it out and negate the test. */
- if (raw_patterns[0][0] == '!') {
- char **patterns;
- size_t i, length;
+ STRV_FOREACH(p, patterns) {
+ const char *q = *p;
+ bool invert;
+
+ invert = *q == '!';
+ q += invert;
+
+ if (!invert)
+ has_positive_rule = true;
+
+ if (string && fnmatch(q, string, 0) == 0) {
+ if (invert)
+ return false;
+ else
+ match = true;
+ }
+ }
+
+ return has_positive_rule ? match : true;
+}
+
+static int net_condition_test_property(char * const *match_property, sd_device *device) {
+ char * const *p;
+
+ if (strv_isempty(match_property))
+ return true;
+
+ STRV_FOREACH(p, match_property) {
+ _cleanup_free_ char *key = NULL;
+ const char *val, *dev_val;
+ bool invert, v;
+
+ invert = **p == '!';
+
+ val = strchr(*p + invert, '=');
+ if (!val)
+ return -EINVAL;
- length = strv_length(raw_patterns) + 1; /* Include the NULL. */
- patterns = newa(char*, length);
- patterns[0] = raw_patterns[0] + 1; /* Skip the "!". */
- for (i = 1; i < length; i++)
- patterns[i] = raw_patterns[i];
+ key = strndup(*p + invert, val - *p - invert);
+ if (!key)
+ return -ENOMEM;
+
+ val++;
- return !string || !strv_fnmatch(patterns, string, 0);
+ v = device &&
+ sd_device_get_property_value(device, key, &dev_val) >= 0 &&
+ fnmatch(val, dev_val, 0) == 0;
+
+ if (invert ? v : !v)
+ return false;
}
- return string && strv_fnmatch(raw_patterns, string, 0);
+ return true;
}
bool net_match_config(Set *match_mac,
char * const *match_drivers,
char * const *match_types,
char * const *match_names,
+ char * const *match_property,
+ sd_device *device,
const struct ether_addr *dev_mac,
- const char *dev_path,
- const char *dev_driver,
- const char *dev_type,
const char *dev_name) {
+ const char *dev_path = NULL, *dev_driver = NULL, *dev_type = NULL, *mac_str;
+
+ if (device) {
+ (void) sd_device_get_property_value(device, "ID_PATH", &dev_path);
+ (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
+ (void) sd_device_get_devtype(device, &dev_type);
+
+ if (!dev_name)
+ (void) sd_device_get_sysname(device, &dev_name);
+ if (!dev_mac &&
+ sd_device_get_sysattr_value(device, "address", &mac_str) >= 0)
+ dev_mac = ether_aton(mac_str);
+ }
+
if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac)))
return false;
if (!net_condition_test_strv(match_names, dev_name))
return false;
+ if (!net_condition_test_property(match_property, device))
+ return false;
+
return true;
}
return 0;
}
-int config_parse_ifnames(
+int config_parse_match_strv(
const char *unit,
const char *filename,
unsigned line,
void *data,
void *userdata) {
+ const char *p = rvalue;
char ***sv = data;
+ bool invert;
int r;
assert(filename);
assert(rvalue);
assert(data);
+ if (isempty(rvalue)) {
+ *sv = strv_free(*sv);
+ return 0;
+ }
+
+ invert = *p == '!';
+ p += invert;
+
for (;;) {
- _cleanup_free_ char *word = NULL;
+ _cleanup_free_ char *word = NULL, *k = NULL;
- r = extract_first_word(&rvalue, &word, NULL, 0);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
+ if (r == 0)
+ return 0;
+ if (r == -ENOMEM)
+ return log_oom();
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse interface name list: %s", rvalue);
+ log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue);
return 0;
}
+
+ if (invert) {
+ k = strjoin("!", word);
+ if (!k)
+ return log_oom();
+ } else
+ k = TAKE_PTR(word);
+
+ r = strv_consume(sv, TAKE_PTR(k));
+ if (r < 0)
+ return log_oom();
+ }
+}
+
+int config_parse_match_ifnames(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ const char *p = rvalue;
+ char ***sv = data;
+ bool invert;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ invert = *p == '!';
+ p += invert;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *k = NULL;
+
+ r = extract_first_word(&p, &word, NULL, 0);
if (r == 0)
- break;
+ return 0;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Failed to parse interface name list: %s", rvalue);
+ return 0;
+ }
if (!ifname_valid(word)) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Interface name is not valid or too long, ignoring assignment: %s", rvalue);
- return 0;
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Interface name is not valid or too long, ignoring assignment: %s", word);
+ continue;
}
- r = strv_push(sv, word);
+ if (invert) {
+ k = strjoin("!", word);
+ if (!k)
+ return log_oom();
+ } else
+ k = TAKE_PTR(word);
+
+ r = strv_consume(sv, TAKE_PTR(k));
if (r < 0)
return log_oom();
-
- word = NULL;
}
+}
- return 0;
+int config_parse_match_property(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ const char *p = rvalue;
+ char ***sv = data;
+ bool invert;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ invert = *p == '!';
+ p += invert;
+
+ for (;;) {
+ _cleanup_free_ char *word = NULL, *k = NULL;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNQUOTE);
+ if (r == 0)
+ return 0;
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid syntax, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (!env_assignment_is_valid(word)) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Invalid property or value, ignoring assignment: %s", word);
+ continue;
+ }
+
+ if (invert) {
+ k = strjoin("!", word);
+ if (!k)
+ return log_oom();
+ } else
+ k = TAKE_PTR(word);
+
+ r = strv_consume(sv, TAKE_PTR(k));
+ if (r < 0)
+ return log_oom();
+ }
}
int config_parse_ifalias(const char *unit,
char * const *match_driver,
char * const *match_type,
char * const *match_name,
+ char * const *match_property,
+ sd_device *device,
const struct ether_addr *dev_mac,
- const char *dev_path,
- const char *dev_driver,
- const char *dev_type,
const char *dev_name);
CONFIG_PARSER_PROTOTYPE(config_parse_net_condition);
CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr);
CONFIG_PARSER_PROTOTYPE(config_parse_hwaddrs);
-CONFIG_PARSER_PROTOTYPE(config_parse_ifnames);
+CONFIG_PARSER_PROTOTYPE(config_parse_match_strv);
+CONFIG_PARSER_PROTOTYPE(config_parse_match_ifnames);
+CONFIG_PARSER_PROTOTYPE(config_parse_match_property);
CONFIG_PARSER_PROTOTYPE(config_parse_ifalias);
CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority);
-int net_get_unique_predictable_data(sd_device *device, uint64_t *result);
-const char *net_get_name(sd_device *device);
+int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *result);
+const char *net_get_name_persistent(sd_device *device);
size_t serialize_in_addrs(FILE *f,
const struct in_addr *addresses,
usec_t preferred_until;
};
-#define log_radv_full(level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__)
+#define log_radv_full(level, error, fmt, ...) log_internal(level, error, PROJECT_FILE, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__)
#define log_radv_errno(error, fmt, ...) log_radv_full(LOG_DEBUG, error, fmt, ##__VA_ARGS__)
#define log_radv(fmt, ...) log_radv_errno(0, fmt, ##__VA_ARGS__)
assert(client);
if (error < 0)
- log_dhcp_client(client, "STOPPED: %s", strerror(-error));
+ log_dhcp_client_errno(client, error, "STOPPED: %m");
else if (error == SD_DHCP_CLIENT_EVENT_STOP)
log_dhcp_client(client, "STOPPED");
else
return 0;
}
+static bool lease_equal(const sd_dhcp_lease *a, const sd_dhcp_lease *b) {
+ if (a->address != b->address)
+ return false;
+
+ if (a->subnet_mask != b->subnet_mask)
+ return false;
+
+ if (a->router_size != b->router_size)
+ return false;
+
+ for (size_t i = 0; i < a->router_size; i++)
+ if (a->router[i].s_addr != b->router[i].s_addr)
+ return false;
+
+ return true;
+}
+
static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) {
_cleanup_(sd_dhcp_lease_unrefp) sd_dhcp_lease *lease = NULL;
_cleanup_free_ char *error_message = NULL;
r = SD_DHCP_CLIENT_EVENT_IP_ACQUIRE;
if (client->lease) {
- if (client->lease->address != lease->address ||
- client->lease->subnet_mask != lease->subnet_mask ||
- client->lease->router != lease->router) {
- r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
- } else
+ if (lease_equal(client->lease, lease))
r = SD_DHCP_CLIENT_EVENT_RENEW;
+ else
+ r = SD_DHCP_CLIENT_EVENT_IP_CHANGE;
client->lease = sd_dhcp_lease_unref(client->lease);
}
r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns,
lease->dns_count,
&lease->dns_allocated);
- if (r < 0) {
- log_dhcp6_client(client, "Invalid DNS server option: %s",
- strerror(-r));
-
- return r;
- }
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Invalid DNS server option: %m");
lease->dns_count = r;
r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp,
lease->ntp_count,
&lease->ntp_allocated);
- if (r < 0) {
- log_dhcp6_client(client, "Invalid SNTP server option: %s",
- strerror(-r));
-
- return r;
- }
+ if (r < 0)
+ return log_dhcp6_client_errno(client, r, "Invalid SNTP server option: %m");
lease->ntp_count = r;
void* userdata;
};
-#define log_ipv4acd_errno(acd, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__)
+#define log_ipv4acd_errno(acd, error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "IPV4ACD: " fmt, ##__VA_ARGS__)
#define log_ipv4acd(acd, fmt, ...) log_ipv4acd_errno(acd, 0, fmt, ##__VA_ARGS__)
static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) {
void* userdata;
};
-#define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__)
+#define log_ipv4ll_errno(ll, error, fmt, ...) log_internal(LOG_DEBUG, error, PROJECT_FILE, __LINE__, __func__, "IPV4LL: " fmt, ##__VA_ARGS__)
#define log_ipv4ll(ll, fmt, ...) log_ipv4ll_errno(ll, 0, fmt, ##__VA_ARGS__)
static void ipv4ll_on_acd(sd_ipv4acd *ll, int event, void *userdata);
SD_BUS_ERROR_MAP(BUS_ERROR_OPERATION_IN_PROGRESS, EINPROGRESS),
SD_BUS_ERROR_MAP(BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, EOPNOTSUPP),
SD_BUS_ERROR_MAP(BUS_ERROR_SESSION_BUSY, EBUSY),
+ SD_BUS_ERROR_MAP(BUS_ERROR_NOT_YOUR_DEVICE, EPERM),
SD_BUS_ERROR_MAP(BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED, EALREADY),
SD_BUS_ERROR_MAP(BUS_ERROR_NO_NTP_SUPPORT, EOPNOTSUPP),
#define BUS_ERROR_NO_SUCH_DYNAMIC_USER "org.freedesktop.systemd1.NoSuchDynamicUser"
#define BUS_ERROR_NOT_REFERENCED "org.freedesktop.systemd1.NotReferenced"
#define BUS_ERROR_DISK_FULL "org.freedesktop.systemd1.DiskFull"
+#define BUS_ERROR_NOTHING_TO_CLEAN "org.freedesktop.systemd1.NothingToClean"
+#define BUS_ERROR_UNIT_BUSY "org.freedesktop.systemd1.UnitBusy"
#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"
#define BUS_ERROR_OPERATION_IN_PROGRESS "org.freedesktop.login1.OperationInProgress"
#define BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED "org.freedesktop.login1.SleepVerbNotSupported"
#define BUS_ERROR_SESSION_BUSY "org.freedesktop.login1.SessionBusy"
+#define BUS_ERROR_NOT_YOUR_DEVICE "org.freedesktop.login1.NotYourDevice"
#define BUS_ERROR_AUTOMATIC_TIME_SYNC_ENABLED "org.freedesktop.timedate1.AutomaticTimeSyncEnabled"
#define BUS_ERROR_NO_NTP_SUPPORT "org.freedesktop.timedate1.NoNTPSupport"
int bus_add_match_internal(
sd_bus *bus,
- const char *match) {
+ const char *match,
+ uint64_t *ret_counter) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *e;
+ int r;
assert(bus);
e = append_eavesdrop(bus, match);
- return sd_bus_call_method(
+ r = sd_bus_call_method(
bus,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
"AddMatch",
NULL,
- NULL,
+ &reply,
"s",
e);
+ if (r < 0)
+ return r;
+
+ /* If the caller asked for it, return the read counter of the reply */
+ if (ret_counter)
+ *ret_counter = reply->read_counter;
+
+ return r;
}
int bus_add_match_internal_async(
#include "sd-bus.h"
-int bus_add_match_internal(sd_bus *bus, const char *match);
+int bus_add_match_internal(sd_bus *bus, const char *match, uint64_t *ret_counter);
int bus_add_match_internal_async(sd_bus *bus, sd_bus_slot **ret, const char *match, sd_bus_message_handler_t callback, void *userdata);
int bus_remove_match_internal(sd_bus *bus, const char *match);
if ((unsigned long) capability > lc)
return 0;
- sz = DIV_ROUND_UP(lc, 32LU);
+ /* If the last cap is 63, then there are 64 caps defined, and we need 2 entries á 32bit hence. *
+ * If the last cap is 64, then there are 65 caps defined, and we need 3 entries á 32bit hence. */
+ sz = DIV_ROUND_UP(lc+1, 32LU);
return !!(c->capability[offset * sz + CAP_TO_INDEX((uint32_t) capability)] & CAP_TO_MASK_CORRECTED((uint32_t) capability));
}
assert(c);
assert(p);
- max = DIV_ROUND_UP(cap_last_cap(), 32U);
+ max = DIV_ROUND_UP(cap_last_cap()+1, 32U);
p += strspn(p, WHITESPACE);
sz = strlen(p);
if (c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) {
assert(c->capability);
- n->capability = memdup(c->capability, DIV_ROUND_UP(cap_last_cap(), 32U) * 4 * 4);
+ n->capability = memdup(c->capability, DIV_ROUND_UP(cap_last_cap()+1, 32U) * 4 * 4);
if (!n->capability)
return -ENOMEM;
if (error < 0)
error = -error;
- return strerror(error);
+ return strerror_safe(error);
}
static bool map_ok(const sd_bus_error_map *map) {
unsigned last_iteration;
+ /* Don't dispatch this slot with with messages that arrived in any iteration before or at the this
+ * one. We use this to ensure that matches don't apply "retroactively" and thus can confuse the
+ * caller: matches will only match incoming messages from the moment on the match was installed. */
+ uint64_t after;
+
char *match_string;
struct bus_match_node *match_node;
size_t wqueue_allocated;
uint64_t cookie;
+ uint64_t read_counter; /* A counter for each incoming msg */
char *unique_name;
uint64_t unique_id;
case BUS_MATCH_LEAF:
if (bus) {
- if (node->leaf.callback->last_iteration == bus->iteration_counter)
- return 0;
+ /* Don't run this match as long as the AddMatch() call is not complete yet.
+ *
+ * Don't run this match unless the 'after' counter has been reached.
+ *
+ * Don't run this match more than once per iteration */
+
+ if (node->leaf.callback->install_slot ||
+ m->read_counter <= node->leaf.callback->after ||
+ node->leaf.callback->last_iteration == bus->iteration_counter)
+ return bus_match_run(bus, node->next, m);
node->leaf.callback->last_iteration = bus->iteration_counter;
}
if (!b)
return -EBADMSG;
- sig = strndup(b+1, item_size - (b+1-(char*) q));
+ sig = memdup_suffix0(b+1, item_size - (b+1-(char*) q));
if (!sig)
return -ENOMEM;
size_t header_offsets[_BUS_MESSAGE_HEADER_MAX];
unsigned n_header_offsets;
+
+ uint64_t read_counter;
};
static inline bool BUS_MESSAGE_NEED_BSWAP(sd_bus_message *m) {
bus->n_fds = 0;
if (t) {
+ t->read_counter = ++bus->read_counter;
bus->rqueue[bus->rqueue_size++] = bus_message_ref_queued(t, bus);
sd_bus_message_unref(t);
}
#include "memory-util.h"
#include "missing.h"
#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
#include "string-util.h"
#include "strv.h"
return r;
bus_message_set_sender_local(bus, m);
+ m->read_counter = ++bus->read_counter;
r = bus_seal_synthetic_message(bus, m);
if (r < 0)
if (r < 0)
return r;
+ m->read_counter = ++bus->read_counter;
+
r = bus_seal_synthetic_message(bus, m);
if (r < 0)
return r;
synthetic_reply->realtime = m->realtime;
synthetic_reply->monotonic = m->monotonic;
synthetic_reply->seqnum = m->seqnum;
+ synthetic_reply->read_counter = m->read_counter;
r = bus_seal_synthetic_message(bus, synthetic_reply);
if (r < 0)
if (r < 0)
return r;
+ m->read_counter = ++bus->read_counter;
+
r = bus_seal_synthetic_message(bus, m);
if (r < 0)
return r;
return r;
bus_message_set_sender_local(bus, m);
+ m->read_counter = ++bus->read_counter;
r = bus_seal_synthetic_message(bus, m);
if (r < 0)
bus->current_slot = match_slot->match_callback.install_slot;
bus->current_handler = add_match_callback;
bus->current_userdata = userdata;
-
- match_slot->match_callback.install_slot = sd_bus_slot_unref(match_slot->match_callback.install_slot);
} else {
if (failed) /* Generic failure handling: destroy the connection */
bus_enter_closing(sd_bus_message_get_bus(m));
r = 1;
}
+ /* We don't need the install method reply slot anymore, let's free it */
+ match_slot->match_callback.install_slot = sd_bus_slot_unref(match_slot->match_callback.install_slot);
+
if (failed && match_slot->floating)
bus_slot_disconnect(match_slot, true);
* then make it floating. */
r = sd_bus_slot_set_floating(s->match_callback.install_slot, true);
} else
- r = bus_add_match_internal(bus, s->match_callback.match_string);
+ r = bus_add_match_internal(bus, s->match_callback.match_string, &s->match_callback.after);
if (r < 0)
goto finish;
if (!e)
return -ENOMEM;
- ret = strjoin(prefix, "/", e);
+ ret = path_join(prefix, e);
if (!ret)
return -ENOMEM;
#include "bus-internal.h"
#include "bus-match.h"
#include "bus-util.h"
+#include "errno-util.h"
#include "fd-util.h"
#include "format-util.h"
#include "log.h"
errno = 0;
if (read(pp[0], &x, 1) <= 0) {
- log_error("Failed to read from pipe: %s", errno ? strerror(errno) : "early read");
+ log_error("Failed to read from pipe: %s", errno != 0 ? strerror_safe(errno) : "early read");
goto finish;
}
#include "bus-error.h"
#include "bus-util.h"
#include "errno-list.h"
+#include "errno-util.h"
static void test_error(void) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL, second = SD_BUS_ERROR_NULL;
assert_se(!sd_bus_error_is_set(&error));
assert_se(sd_bus_error_set_errno(&error, EBUSY) == -EBUSY);
assert_se(streq(error.name, "System.Error.EBUSY"));
- assert_se(streq(error.message, strerror(EBUSY)));
+ assert_se(streq(error.message, strerror_safe(EBUSY)));
assert_se(sd_bus_error_has_name(&error, "System.Error.EBUSY"));
assert_se(sd_bus_error_get_errno(&error) == EBUSY);
assert_se(sd_bus_error_is_set(&error));
log_info("message size = %zu, contents =\n%s", sz, h);
#if HAVE_GLIB
+ /* Work-around for asan bug. See c8d980a3e962aba2ea3a4cedf75fa94890a6d746. */
#if !HAS_FEATURE_ADDRESS_SANITIZER
{
GDBusMessage *g;
if (dent->d_type != DT_DIR)
continue;
- child = strjoin(path, "/", dent->d_name);
+ child = path_join(path, dent->d_name);
if (!child)
return -ENOMEM;
int device_rename(sd_device *device, const char *name) {
_cleanup_free_ char *dirname = NULL;
- char *new_syspath;
- const char *interface;
+ const char *new_syspath, *interface;
int r;
assert(device);
if (!dirname)
return -ENOMEM;
- new_syspath = strjoina(dirname, "/", name);
+ new_syspath = prefix_roota(dirname, name);
/* the user must trust that the new name is correct */
r = device_set_syspath(device, new_syspath, false);
};
DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction);
+
+void dump_device_action_table(void) {
+ DUMP_STRING_TABLE(device_action, DeviceAction, _DEVICE_ACTION_MAX);
+}
DeviceAction device_action_from_string(const char *s) _pure_;
const char *device_action_to_string(DeviceAction a) _const_;
+void dump_device_action_table(void);
\
if (_d && _unlikely_(log_get_max_level() >= _level)) \
(void) sd_device_get_sysname(_d, &_sysname); \
- log_object_internal(_level, _error, __FILE__, __LINE__, __func__, \
+ log_object_internal(_level, _error, PROJECT_FILE, __LINE__, __func__, \
_sysname ? "DEVICE=" : NULL, _sysname, \
NULL, NULL, ##__VA_ARGS__); \
})
"sd-device: Canonicalized path '%s' does not starts with sysfs mount point '%s'",
syspath, real_sys);
- new_syspath = strjoin("/sys/", p);
+ new_syspath = path_join("/sys", p);
if (!new_syspath)
return -ENOMEM;
devpath = syspath + STRLEN("/sys");
+ if (devpath[0] == '\0')
+ /* '/sys' alone is not a valid device path */
+ return -ENODEV;
+
r = device_add_property_internal(device, "DEVPATH", devpath);
if (r < 0)
return r;
return r;
FOREACH_DIRENT_ALL(dent, dir, return -errno) {
- char *path;
+ _cleanup_free_ char *path = NULL;
struct stat statbuf;
/* only handle symlinks and regular files */
if (!IN_SET(dent->d_type, DT_LNK, DT_REG))
continue;
- path = strjoina(syspath, "/", dent->d_name);
+ path = path_join(syspath, dent->d_name);
+ if (!path)
+ return -ENOMEM;
if (lstat(path, &statbuf) != 0)
continue;
* with a NULL value in the cache, otherwise the returned string is stored */
_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **_value) {
_cleanup_free_ char *value = NULL;
- const char *syspath, *cached_value = NULL;
- char *path;
+ const char *path, *syspath, *cached_value = NULL;
struct stat statbuf;
int r;
if (r < 0)
return r;
- path = strjoina(syspath, "/", sysattr);
+ path = prefix_roota(syspath, sysattr);
r = lstat(path, &statbuf);
if (r < 0) {
/* remember that we could not access the sysattr */
if (r < 0)
return r;
- path = strjoina(syspath, "/", sysattr);
+ path = prefix_roota(syspath, sysattr);
len = strlen(_value);
#include "signal-util.h"
#include "string-table.h"
#include "string-util.h"
+#include "strxcpyx.h"
#include "time-util.h"
#define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC)
}
static void event_log_delays(sd_event *e) {
- char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1];
- unsigned i;
- int o;
+ char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1], *p;
+ size_t l, i;
- for (i = o = 0; i < ELEMENTSOF(e->delays); i++) {
- o += snprintf(&b[o], sizeof(b) - o, "%u ", e->delays[i]);
+ p = b;
+ l = sizeof(b);
+ for (i = 0; i < ELEMENTSOF(e->delays); i++) {
+ l = strpcpyf(&p, l, "%u ", e->delays[i]);
e->delays[i] = 0;
}
- log_debug("Event loop iterations: %.*s", o, b);
+ log_debug("Event loop iterations: %s", b);
}
_public_ int sd_event_run(sd_event *e, uint64_t timeout) {
#include "log.h"
#include "macro.h"
#include "parse-util.h"
+#include "path-util.h"
#include "process-util.h"
#include "rm-rf.h"
#include "signal-util.h"
_cleanup_free_ char *z;
xsprintf(buf, "%u", i);
- assert_se(z = strjoin(p, "/", buf));
+ assert_se(z = path_join(p, buf));
assert_se(touch(z) >= 0);
}
static int netlink_container_parse(sd_netlink_message *m,
struct netlink_container *container,
- int count,
struct rtattr *rta,
unsigned rt_len) {
_cleanup_free_ struct netlink_attribute *attributes = NULL;
-
- attributes = new0(struct netlink_attribute, count);
- if (!attributes)
- return -ENOMEM;
+ size_t n_allocated = 0;
for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) {
unsigned short type;
type = RTA_TYPE(rta);
- /* if the kernel is newer than the headers we used
- when building, we ignore out-of-range attributes */
- if (type >= count)
- continue;
+ if (!GREEDY_REALLOC0(attributes, n_allocated, type + 1))
+ return -ENOMEM;
if (attributes[type].offset != 0)
log_debug("rtnl: message parse - overwriting repeated attribute");
}
container->attributes = TAKE_PTR(attributes);
- container->n_attributes = count;
+ container->n_attributes = n_allocated;
return 0;
}
r = netlink_message_read_internal(m, type_id, &container, NULL);
if (r < 0)
return r;
- else
- size = (size_t)r;
+
+ size = (size_t)r;
m->n_containers++;
r = netlink_container_parse(m,
&m->containers[m->n_containers],
- type_system_get_count(type_system),
container,
size);
if (r < 0) {
return 0;
}
+int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type_id) {
+ void *container;
+ size_t size;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -EINVAL);
+
+ r = netlink_message_read_internal(m, type_id, &container, NULL);
+ if (r < 0)
+ return r;
+
+ size = (size_t) r;
+
+ m->n_containers++;
+
+ r = netlink_container_parse(m,
+ &m->containers[m->n_containers],
+ container,
+ size);
+ if (r < 0) {
+ m->n_containers--;
+ return r;
+ }
+
+ m->containers[m->n_containers].type_system = m->containers[m->n_containers - 1].type_system;
+
+ return 0;
+}
+
int sd_netlink_message_exit_container(sd_netlink_message *m) {
assert_return(m, -EINVAL);
assert_return(m->sealed, -EINVAL);
r = netlink_container_parse(m,
&m->containers[m->n_containers],
- type_system_get_count(type_system),
(struct rtattr*)((uint8_t*)NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
NLMSG_PAYLOAD(m->hdr, size));
if (r < 0)
/* check that the size matches the message type */
if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
- log_debug("sd-netlink: message larger than expected, dropping");
+ log_debug("sd-netlink: message is shorter than expected, dropping");
continue;
}
[IFLA_MACSEC_VALIDATION] = { .type = NETLINK_TYPE_U8 },
};
+static const NLType rtnl_link_info_data_xfrm_types[] = {
+ [IFLA_XFRM_LINK] = { .type = NETLINK_TYPE_U32 },
+ [IFLA_XFRM_IF_ID] = { .type = NETLINK_TYPE_U32 }
+};
+
/* these strings must match the .kind entries in the kernel */
static const char* const nl_union_link_info_data_table[] = {
[NL_UNION_LINK_INFO_DATA_BOND] = "bond",
[NL_UNION_LINK_INFO_DATA_CAN] = "can",
[NL_UNION_LINK_INFO_DATA_MACSEC] = "macsec",
[NL_UNION_LINK_INFO_DATA_NLMON] = "nlmon",
+ [NL_UNION_LINK_INFO_DATA_XFRM] = "xfrm",
};
DEFINE_STRING_TABLE_LOOKUP(nl_union_link_info_data, NLUnionLinkInfoData);
.types = rtnl_link_info_data_can_types },
[NL_UNION_LINK_INFO_DATA_MACSEC] = { .count = ELEMENTSOF(rtnl_link_info_data_macsec_types),
.types = rtnl_link_info_data_macsec_types },
+ [NL_UNION_LINK_INFO_DATA_XFRM] = { .count = ELEMENTSOF(rtnl_link_info_data_xfrm_types),
+ .types = rtnl_link_info_data_xfrm_types },
};
static const NLTypeSystemUnion rtnl_link_info_data_type_system_union = {
.types = genl_wireguard_cmds,
};
+static const NLType genl_mcast_group_types[] = {
+ [CTRL_ATTR_MCAST_GRP_NAME] = { .type = NETLINK_TYPE_STRING },
+ [CTRL_ATTR_MCAST_GRP_ID] = { .type = NETLINK_TYPE_U32 },
+};
+
+static const NLTypeSystem genl_mcast_group_type_system = {
+ .count = ELEMENTSOF(genl_mcast_group_types),
+ .types = genl_mcast_group_types,
+};
+
static const NLType genl_get_family_types[] = {
- [CTRL_ATTR_FAMILY_NAME] = { .type = NETLINK_TYPE_STRING },
- [CTRL_ATTR_FAMILY_ID] = { .type = NETLINK_TYPE_U16 },
+ [CTRL_ATTR_FAMILY_NAME] = { .type = NETLINK_TYPE_STRING },
+ [CTRL_ATTR_FAMILY_ID] = { .type = NETLINK_TYPE_U16 },
+ [CTRL_ATTR_MCAST_GROUPS] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_mcast_group_type_system },
};
static const NLTypeSystem genl_get_family_type_system = {
NL_UNION_LINK_INFO_DATA_CAN,
NL_UNION_LINK_INFO_DATA_MACSEC,
NL_UNION_LINK_INFO_DATA_NLMON,
+ NL_UNION_LINK_INFO_DATA_XFRM,
_NL_UNION_LINK_INFO_DATA_MAX,
_NL_UNION_LINK_INFO_DATA_INVALID = -1
} NLUnionLinkInfoData;
#include <net/if.h>
#include <netinet/ether.h>
+#include <linux/genetlink.h>
#include "sd-netlink.h"
#include "missing.h"
#include "netlink-util.h"
#include "socket-util.h"
+#include "stdio-util.h"
#include "string-util.h"
#include "util.h"
assert_se(sd_netlink_message_get_errno(m) == -ETIMEDOUT);
}
+static void test_array(void) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *genl = NULL;
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
+
+ assert_se(sd_genl_socket_open(&genl) >= 0);
+ assert_se(sd_genl_message_new(genl, SD_GENL_ID_CTRL, CTRL_CMD_GETFAMILY, &m) >= 0);
+
+ assert_se(sd_netlink_message_open_container(m, CTRL_ATTR_MCAST_GROUPS) >= 0);
+ for (unsigned i = 0; i < 10; i++) {
+ char name[STRLEN("hoge") + DECIMAL_STR_MAX(uint32_t)];
+ uint32_t id = i + 1000;
+
+ xsprintf(name, "hoge%" PRIu32, id);
+ assert_se(sd_netlink_message_open_array(m, i + 1) >= 0);
+ assert_se(sd_netlink_message_append_u32(m, CTRL_ATTR_MCAST_GRP_ID, id) >= 0);
+ assert_se(sd_netlink_message_append_string(m, CTRL_ATTR_MCAST_GRP_NAME, name) >= 0);
+ assert_se(sd_netlink_message_close_container(m) >= 0);
+ }
+ assert_se(sd_netlink_message_close_container(m) >= 0);
+
+ rtnl_message_seal(m);
+ assert_se(sd_netlink_message_rewind(m) >= 0);
+
+ assert_se(sd_netlink_message_enter_container(m, CTRL_ATTR_MCAST_GROUPS) >= 0);
+ for (unsigned i = 0; i < 10; i++) {
+ char expected[STRLEN("hoge") + DECIMAL_STR_MAX(uint32_t)];
+ const char *name;
+ uint32_t id;
+
+ assert_se(sd_netlink_message_enter_array(m, i + 1) >= 0);
+ assert_se(sd_netlink_message_read_u32(m, CTRL_ATTR_MCAST_GRP_ID, &id) >= 0);
+ assert_se(sd_netlink_message_read_string(m, CTRL_ATTR_MCAST_GRP_NAME, &name) >= 0);
+ assert_se(sd_netlink_message_exit_container(m) >= 0);
+
+ assert_se(id == i + 1000);
+ xsprintf(expected, "hoge%" PRIu32, id);
+ assert_se(streq(name, expected));
+ }
+ assert_se(sd_netlink_message_exit_container(m) >= 0);
+
+}
+
int main(void) {
sd_netlink *rtnl;
sd_netlink_message *m;
test_route(rtnl);
test_message(rtnl);
test_container(rtnl);
+ test_array();
if_loopback = (int) if_nametoindex("lo");
assert_se(if_loopback > 0);
#include "strv.h"
bool network_is_online(void) {
- _cleanup_free_ char *state = NULL;
+ _cleanup_free_ char *carrier_state = NULL, *addr_state = NULL;
int r;
- r = sd_network_get_operational_state(&state);
+ r = sd_network_get_carrier_state(&carrier_state);
if (r < 0) /* if we don't know anything, we consider the system online */
return true;
- if (STR_IN_SET(state, "routable", "degraded"))
+ r = sd_network_get_address_state(&addr_state);
+ if (r < 0) /* if we don't know anything, we consider the system online */
+ return true;
+
+ if (STR_IN_SET(carrier_state, "degraded-carrier", "carrier") &&
+ STR_IN_SET(addr_state, "routable", "degraded"))
return true;
return false;
};
DEFINE_STRING_TABLE_LOOKUP(link_operstate, LinkOperationalState);
+
+static const char* const link_carrier_state_table[_LINK_CARRIER_STATE_MAX] = {
+ [LINK_CARRIER_STATE_OFF] = "off",
+ [LINK_CARRIER_STATE_NO_CARRIER] = "no-carrier",
+ [LINK_CARRIER_STATE_DORMANT] = "dormant",
+ [LINK_CARRIER_STATE_DEGRADED_CARRIER] = "degraded-carrier",
+ [LINK_CARRIER_STATE_CARRIER] = "carrier",
+ [LINK_CARRIER_STATE_ENSLAVED] = "enslaved",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(link_carrier_state, LinkCarrierState);
+
+static const char* const link_address_state_table[_LINK_ADDRESS_STATE_MAX] = {
+ [LINK_ADDRESS_STATE_OFF] = "off",
+ [LINK_ADDRESS_STATE_DEGRADED] = "degraded",
+ [LINK_ADDRESS_STATE_ROUTABLE] = "routable",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(link_address_state, LinkAddressState);
_LINK_OPERSTATE_INVALID = -1
} LinkOperationalState;
+typedef enum LinkCarrierState {
+ LINK_CARRIER_STATE_OFF = LINK_OPERSTATE_OFF,
+ LINK_CARRIER_STATE_NO_CARRIER = LINK_OPERSTATE_NO_CARRIER,
+ LINK_CARRIER_STATE_DORMANT = LINK_OPERSTATE_DORMANT,
+ LINK_CARRIER_STATE_DEGRADED_CARRIER = LINK_OPERSTATE_DEGRADED_CARRIER,
+ LINK_CARRIER_STATE_CARRIER = LINK_OPERSTATE_CARRIER,
+ LINK_CARRIER_STATE_ENSLAVED = LINK_OPERSTATE_ENSLAVED,
+ _LINK_CARRIER_STATE_MAX,
+ _LINK_CARRIER_STATE_INVALID = -1
+} LinkCarrierState;
+
+typedef enum LinkAddressState {
+ LINK_ADDRESS_STATE_OFF,
+ LINK_ADDRESS_STATE_DEGRADED,
+ LINK_ADDRESS_STATE_ROUTABLE,
+ _LINK_ADDRESS_STATE_MAX,
+ _LINK_ADDRESS_STATE_INVALID = -1
+} LinkAddressState;
+
const char* link_operstate_to_string(LinkOperationalState s) _const_;
LinkOperationalState link_operstate_from_string(const char *s) _pure_;
+
+const char* link_carrier_state_to_string(LinkCarrierState s) _const_;
+LinkCarrierState link_carrier_state_from_string(const char *s) _pure_;
+
+const char* link_address_state_to_string(LinkAddressState s) _const_;
+LinkAddressState link_address_state_from_string(const char *s) _pure_;
#include "strv.h"
#include "util.h"
-_public_ int sd_network_get_operational_state(char **state) {
+static int network_get_string(const char *field, char **ret) {
_cleanup_free_ char *s = NULL;
int r;
- assert_return(state, -EINVAL);
+ assert_return(ret, -EINVAL);
- r = parse_env_file(NULL, "/run/systemd/netif/state", "OPER_STATE", &s);
+ r = parse_env_file(NULL, "/run/systemd/netif/state", field, &s);
if (r == -ENOENT)
return -ENODATA;
if (r < 0)
if (isempty(s))
return -ENODATA;
- *state = TAKE_PTR(s);
+ *ret = TAKE_PTR(s);
return 0;
}
+_public_ int sd_network_get_operational_state(char **state) {
+ return network_get_string("OPER_STATE", state);
+}
+
+_public_ int sd_network_get_carrier_state(char **state) {
+ return network_get_string("CARRIER_STATE", state);
+}
+
+_public_ int sd_network_get_address_state(char **state) {
+ return network_get_string("ADDRESS_STATE", state);
+}
+
static int network_get_strv(const char *key, char ***ret) {
_cleanup_strv_free_ char **a = NULL;
_cleanup_free_ char *s = NULL;
return network_link_get_string(ifindex, "OPER_STATE", state);
}
+_public_ int sd_network_link_get_carrier_state(int ifindex, char **state) {
+ return network_link_get_string(ifindex, "CARRIER_STATE", state);
+}
+
+_public_ int sd_network_link_get_address_state(int ifindex, char **state) {
+ return network_link_get_string(ifindex, "ADDRESS_STATE", state);
+}
+
_public_ int sd_network_link_get_required_for_online(int ifindex) {
_cleanup_free_ char *s = NULL;
int r;
if (r < 0)
return r;
- if (endswith(h, "/"))
- cc = strappend(h, suffix);
- else
- cc = strjoin(h, "/", suffix);
+ cc = path_join(h, suffix);
if (!cc)
return -ENOMEM;
}
_public_ int sd_path_home(uint64_t type, const char *suffix, char **path) {
- char *buffer = NULL, *cc;
+ _cleanup_free_ char *buffer = NULL;
const char *ret;
+ char *cc;
int r;
assert_return(path, -EINVAL);
if (!buffer)
return -ENOMEM;
- *path = buffer;
+ *path = TAKE_PTR(buffer);
return 0;
}
return -ENOMEM;
}
- *path = buffer;
+ *path = TAKE_PTR(buffer);
return 0;
}
suffix += strspn(suffix, "/");
-
- if (endswith(ret, "/"))
- cc = strappend(ret, suffix);
- else
- cc = strjoin(ret, "/", suffix);
-
- free(buffer);
-
+ cc = path_join(ret, suffix);
if (!cc)
return -ENOMEM;
- *path = cc;
+ *path = TAKE_PTR(cc);
return 0;
}
bool env_search_sufficient,
const char *first, ...) {
+ _cleanup_strv_free_ char **l = NULL;
const char *e;
char *h = NULL;
- char **l = NULL;
int r;
assert(list);
return -ENOMEM;
if (env_search_sufficient) {
- *list = l;
+ *list = TAKE_PTR(l);
return 0;
}
}
e = secure_getenv(env_home);
if (e && path_is_absolute(e)) {
h = strdup(e);
- if (!h) {
- strv_free(l);
+ if (!h)
return -ENOMEM;
- }
}
}
if (!h && home_suffix) {
e = secure_getenv("HOME");
if (e && path_is_absolute(e)) {
- if (endswith(e, "/"))
- h = strappend(e, home_suffix);
- else
- h = strjoin(e, "/", home_suffix);
-
- if (!h) {
- strv_free(l);
+ h = path_join(e, home_suffix);
+ if (!h)
return -ENOMEM;
- }
}
}
if (h) {
r = strv_consume_prepend(&l, h);
- if (r < 0) {
- strv_free(l);
+ if (r < 0)
return -ENOMEM;
- }
}
- *list = l;
+ *list = TAKE_PTR(l);
return 0;
}
j = n;
STRV_FOREACH(i, l) {
-
- if (endswith(*i, "/"))
- *j = strappend(*i, suffix);
- else
- *j = strjoin(*i, "/", suffix);
-
+ *j = path_join(*i, suffix);
if (!*j)
return -ENOMEM;
._h_errno = _h_errno,
};
+ msan_unpoison(&resp, sizeof(resp));
+
if (ret == 0 && ai) {
void *p = &buffer;
struct addrinfo *k;
._h_errno = _h_errno,
};
+ msan_unpoison(&resp, sizeof(resp));
+
iov[0] = IOVEC_MAKE(&resp, sizeof(NameInfoResponse));
iov[1] = IOVEC_MAKE((void*) host, hl);
iov[2] = IOVEC_MAKE((void*) serv, sl);
.ai_protocol = ai_req->ai_protocol,
};
+ msan_unpoison(&hints, sizeof(hints));
+
node = ai_req->node_len ? (const char*) ai_req + sizeof(AddrInfoRequest) : NULL;
service = ai_req->service_len ? (const char*) ai_req + sizeof(AddrInfoRequest) + ai_req->node_len : NULL;
.ai_protocol = hints ? hints->ai_protocol : 0,
};
+ msan_unpoison(&req, sizeof(req));
+
iov[mh.msg_iovlen++] = IOVEC_MAKE(&req, sizeof(AddrInfoRequest));
if (node)
iov[mh.msg_iovlen++] = IOVEC_MAKE((void*) node, req.node_len);
.getserv = !!(get & SD_RESOLVE_GET_SERVICE),
};
+ msan_unpoison(&req, sizeof(req));
+
iov[0] = IOVEC_MAKE(&req, sizeof(NameInfoRequest));
iov[1] = IOVEC_MAKE((void*) sa, salen);
#include "libudev.h"
#include "sd-device.h"
-#include "libudev-list-internal.h"
-
-/**
- * udev_device:
- *
- * Opaque object representing one kernel sys device.
- */
-struct udev_device {
- struct udev *udev;
-
- /* real device object */
- sd_device *device;
-
- /* legacy */
- unsigned n_ref;
-
- struct udev_device *parent;
- bool parent_set;
-
- struct udev_list properties;
- uint64_t properties_generation;
- struct udev_list tags;
- uint64_t tags_generation;
- struct udev_list devlinks;
- uint64_t devlinks_generation;
- bool properties_read:1;
- bool tags_read:1;
- bool devlinks_read:1;
- struct udev_list sysattrs;
- bool sysattrs_read;
-};
+struct udev_device;
struct udev_device *udev_device_new(struct udev *udev, sd_device *device);
+sd_device *udev_device_get_sd_device(struct udev_device *udev_device);
#include "device-private.h"
#include "device-util.h"
#include "libudev-device-internal.h"
+#include "libudev-list-internal.h"
#include "parse-util.h"
#include "time-util.h"
* a unique name inside that subsystem.
*/
+/**
+ * udev_device:
+ *
+ * Opaque object representing one kernel sys device.
+ */
+struct udev_device {
+ struct udev *udev;
+
+ /* real device object */
+ sd_device *device;
+
+ /* legacy */
+ unsigned n_ref;
+
+ struct udev_device *parent;
+ bool parent_set;
+
+ struct udev_list *properties;
+ uint64_t properties_generation;
+ struct udev_list *tags;
+ uint64_t tags_generation;
+ struct udev_list *devlinks;
+ uint64_t devlinks_generation;
+ bool properties_read:1;
+ bool tags_read:1;
+ bool devlinks_read:1;
+ struct udev_list *sysattrs;
+ bool sysattrs_read;
+};
+
/**
* udev_device_get_seqnum:
* @udev_device: udev device
}
struct udev_device *udev_device_new(struct udev *udev, sd_device *device) {
+ _cleanup_(udev_list_freep) struct udev_list *properties = NULL, *tags = NULL, *sysattrs = NULL, *devlinks = NULL;
struct udev_device *udev_device;
assert(device);
+ properties = udev_list_new(true);
+ if (!properties)
+ return_with_errno(NULL, ENOMEM);
+ tags = udev_list_new(true);
+ if (!tags)
+ return_with_errno(NULL, ENOMEM);
+ sysattrs = udev_list_new(true);
+ if (!sysattrs)
+ return_with_errno(NULL, ENOMEM);
+ devlinks = udev_list_new(true);
+ if (!devlinks)
+ return_with_errno(NULL, ENOMEM);
+
udev_device = new(struct udev_device, 1);
if (!udev_device)
return_with_errno(NULL, ENOMEM);
.n_ref = 1,
.udev = udev,
.device = sd_device_ref(device),
+ .properties = TAKE_PTR(properties),
+ .tags = TAKE_PTR(tags),
+ .sysattrs = TAKE_PTR(sysattrs),
+ .devlinks = TAKE_PTR(devlinks),
};
- udev_list_init(&udev_device->properties, true);
- udev_list_init(&udev_device->tags, true);
- udev_list_init(&udev_device->sysattrs, true);
- udev_list_init(&udev_device->devlinks, true);
-
return udev_device;
}
sd_device_unref(udev_device->device);
udev_device_unref(udev_device->parent);
- udev_list_cleanup(&udev_device->properties);
- udev_list_cleanup(&udev_device->sysattrs);
- udev_list_cleanup(&udev_device->tags);
- udev_list_cleanup(&udev_device->devlinks);
+ udev_list_free(udev_device->properties);
+ udev_list_free(udev_device->sysattrs);
+ udev_list_free(udev_device->tags);
+ udev_list_free(udev_device->devlinks);
return mfree(udev_device);
}
!udev_device->devlinks_read) {
const char *devlink;
- udev_list_cleanup(&udev_device->devlinks);
+ udev_list_cleanup(udev_device->devlinks);
FOREACH_DEVICE_DEVLINK(udev_device->device, devlink)
- if (!udev_list_entry_add(&udev_device->devlinks, devlink, NULL))
+ if (!udev_list_entry_add(udev_device->devlinks, devlink, NULL))
return_with_errno(NULL, ENOMEM);
udev_device->devlinks_read = true;
udev_device->devlinks_generation = device_get_devlinks_generation(udev_device->device);
}
- return udev_list_get_entry(&udev_device->devlinks);
+ return udev_list_get_entry(udev_device->devlinks);
}
/**
!udev_device->properties_read) {
const char *key, *value;
- udev_list_cleanup(&udev_device->properties);
+ udev_list_cleanup(udev_device->properties);
FOREACH_DEVICE_PROPERTY(udev_device->device, key, value)
- if (!udev_list_entry_add(&udev_device->properties, key, value))
+ if (!udev_list_entry_add(udev_device->properties, key, value))
return_with_errno(NULL, ENOMEM);
udev_device->properties_read = true;
udev_device->properties_generation = device_get_properties_generation(udev_device->device);
}
- return udev_list_get_entry(&udev_device->properties);
+ return udev_list_get_entry(udev_device->properties);
}
/**
if (!udev_device->sysattrs_read) {
const char *sysattr;
- udev_list_cleanup(&udev_device->sysattrs);
+ udev_list_cleanup(udev_device->sysattrs);
FOREACH_DEVICE_SYSATTR(udev_device->device, sysattr)
- if (!udev_list_entry_add(&udev_device->sysattrs, sysattr, NULL))
+ if (!udev_list_entry_add(udev_device->sysattrs, sysattr, NULL))
return_with_errno(NULL, ENOMEM);
udev_device->sysattrs_read = true;
}
- return udev_list_get_entry(&udev_device->sysattrs);
+ return udev_list_get_entry(udev_device->sysattrs);
}
/**
!udev_device->tags_read) {
const char *tag;
- udev_list_cleanup(&udev_device->tags);
+ udev_list_cleanup(udev_device->tags);
FOREACH_DEVICE_TAG(udev_device->device, tag)
- if (!udev_list_entry_add(&udev_device->tags, tag, NULL))
+ if (!udev_list_entry_add(udev_device->tags, tag, NULL))
return_with_errno(NULL, ENOMEM);
udev_device->tags_read = true;
udev_device->tags_generation = device_get_tags_generation(udev_device->device);
}
- return udev_list_get_entry(&udev_device->tags);
+ return udev_list_get_entry(udev_device->tags);
}
/**
return sd_device_has_tag(udev_device->device, tag) > 0;
}
+
+sd_device *udev_device_get_sd_device(struct udev_device *udev_device) {
+ assert(udev_device);
+
+ return udev_device->device;
+}
#include "device-enumerator-private.h"
#include "device-util.h"
#include "libudev-device-internal.h"
+#include "libudev-list-internal.h"
/**
* SECTION:libudev-enumerate
struct udev_enumerate {
struct udev *udev;
unsigned n_ref;
- struct udev_list devices_list;
+ struct udev_list *devices_list;
bool devices_uptodate:1;
sd_device_enumerator *enumerator;
**/
_public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) {
_cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
+ _cleanup_(udev_list_freep) struct udev_list *list = NULL;
struct udev_enumerate *udev_enumerate;
int r;
if (r < 0)
return_with_errno(NULL, r);
+ list = udev_list_new(false);
+ if (!list)
+ return_with_errno(NULL, ENOMEM);
+
udev_enumerate = new(struct udev_enumerate, 1);
if (!udev_enumerate)
return_with_errno(NULL, ENOMEM);
.udev = udev,
.n_ref = 1,
.enumerator = TAKE_PTR(e),
+ .devices_list = TAKE_PTR(list),
};
- udev_list_init(&udev_enumerate->devices_list, false);
-
return udev_enumerate;
}
static struct udev_enumerate *udev_enumerate_free(struct udev_enumerate *udev_enumerate) {
assert(udev_enumerate);
- udev_list_cleanup(&udev_enumerate->devices_list);
+ udev_list_free(udev_enumerate->devices_list);
sd_device_enumerator_unref(udev_enumerate->enumerator);
return mfree(udev_enumerate);
}
if (!udev_enumerate->devices_uptodate) {
sd_device *device;
- udev_list_cleanup(&udev_enumerate->devices_list);
+ udev_list_cleanup(udev_enumerate->devices_list);
FOREACH_DEVICE_AND_SUBSYSTEM(udev_enumerate->enumerator, device) {
const char *syspath;
if (r < 0)
return_with_errno(NULL, r);
- if (!udev_list_entry_add(&udev_enumerate->devices_list, syspath, NULL))
+ if (!udev_list_entry_add(udev_enumerate->devices_list, syspath, NULL))
return_with_errno(NULL, ENOMEM);
}
udev_enumerate->devices_uptodate = true;
}
- e = udev_list_get_entry(&udev_enumerate->devices_list);
+ e = udev_list_get_entry(udev_enumerate->devices_list);
if (!e)
return_with_errno(NULL, ENODATA);
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) {
+ int r;
+
assert_return(udev_enumerate, -EINVAL);
if (!subsystem)
return 0;
- return sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, true);
+ r = sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, true);
+ if (r < 0)
+ return r;
+
+ udev_enumerate->devices_uptodate = false;
+ return 0;
}
/**
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_enumerate, const char *subsystem) {
+ int r;
+
assert_return(udev_enumerate, -EINVAL);
if (!subsystem)
return 0;
- return sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, false);
+ r = sd_device_enumerator_add_match_subsystem(udev_enumerate->enumerator, subsystem, false);
+ if (r < 0)
+ return r;
+
+ udev_enumerate->devices_uptodate = false;
+ return 0;
}
/**
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) {
+ int r;
+
assert_return(udev_enumerate, -EINVAL);
if (!sysattr)
return 0;
- return sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, true);
+ r = sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, true);
+ if (r < 0)
+ return r;
+
+ udev_enumerate->devices_uptodate = false;
+ return 0;
}
/**
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enumerate, const char *sysattr, const char *value) {
+ int r;
+
assert_return(udev_enumerate, -EINVAL);
if (!sysattr)
return 0;
- return sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, false);
+ r = sd_device_enumerator_add_match_sysattr(udev_enumerate->enumerator, sysattr, value, false);
+ if (r < 0)
+ return r;
+
+ udev_enumerate->devices_uptodate = false;
+ return 0;
}
/**
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enumerate, const char *property, const char *value) {
+ int r;
+
assert_return(udev_enumerate, -EINVAL);
if (!property)
return 0;
- return sd_device_enumerator_add_match_property(udev_enumerate->enumerator, property, value);
+ r = sd_device_enumerator_add_match_property(udev_enumerate->enumerator, property, value);
+ if (r < 0)
+ return r;
+
+ udev_enumerate->devices_uptodate = false;
+ return 0;
}
/**
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, const char *tag) {
+ int r;
+
assert_return(udev_enumerate, -EINVAL);
if (!tag)
return 0;
- return sd_device_enumerator_add_match_tag(udev_enumerate->enumerator, tag);
+ r = sd_device_enumerator_add_match_tag(udev_enumerate->enumerator, tag);
+ if (r < 0)
+ return r;
+
+ udev_enumerate->devices_uptodate = false;
+ return 0;
}
/**
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) {
+ int r;
+
assert_return(udev_enumerate, -EINVAL);
if (!parent)
return 0;
- return sd_device_enumerator_add_match_parent(udev_enumerate->enumerator, parent->device);
+ r = sd_device_enumerator_add_match_parent(udev_enumerate->enumerator, udev_device_get_sd_device(parent));
+ if (r < 0)
+ return r;
+
+ udev_enumerate->devices_uptodate = false;
+ return 0;
}
/**
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev_enumerate) {
+ int r;
+
assert_return(udev_enumerate, -EINVAL);
- return device_enumerator_add_match_is_initialized(udev_enumerate->enumerator);
+ r = device_enumerator_add_match_is_initialized(udev_enumerate->enumerator);
+ if (r < 0)
+ return r;
+
+ udev_enumerate->devices_uptodate = false;
+ return 0;
}
/**
* Returns: 0 on success, otherwise a negative error value.
*/
_public_ int udev_enumerate_add_match_sysname(struct udev_enumerate *udev_enumerate, const char *sysname) {
+ int r;
+
assert_return(udev_enumerate, -EINVAL);
if (!sysname)
return 0;
- return sd_device_enumerator_add_match_sysname(udev_enumerate->enumerator, sysname);
+ r = sd_device_enumerator_add_match_sysname(udev_enumerate->enumerator, sysname);
+ if (r < 0)
+ return r;
+
+ udev_enumerate->devices_uptodate = false;
+ return 0;
}
/**
if (r < 0)
return r;
+ udev_enumerate->devices_uptodate = false;
return 0;
}
struct udev_hwdb {
unsigned n_ref;
sd_hwdb *hwdb;
- struct udev_list properties_list;
+ struct udev_list *properties_list;
};
/**
* Returns: a hwdb context.
**/
_public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
+ _cleanup_(udev_list_freep) struct udev_list *list = NULL;
_cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb_internal = NULL;
struct udev_hwdb *hwdb;
int r;
if (r < 0)
return_with_errno(NULL, r);
+ list = udev_list_new(true);
+ if (!list)
+ return_with_errno(NULL, ENOMEM);
+
hwdb = new(struct udev_hwdb, 1);
if (!hwdb)
return_with_errno(NULL, ENOMEM);
*hwdb = (struct udev_hwdb) {
.n_ref = 1,
.hwdb = TAKE_PTR(hwdb_internal),
+ .properties_list = TAKE_PTR(list),
};
- udev_list_init(&hwdb->properties_list, true);
-
return hwdb;
}
assert(hwdb);
sd_hwdb_unref(hwdb->hwdb);
- udev_list_cleanup(&hwdb->properties_list);
+ udev_list_free(hwdb->properties_list);
return mfree(hwdb);
}
assert_return_errno(hwdb, NULL, EINVAL);
assert_return_errno(modalias, NULL, EINVAL);
- udev_list_cleanup(&hwdb->properties_list);
+ udev_list_cleanup(hwdb->properties_list);
SD_HWDB_FOREACH_PROPERTY(hwdb->hwdb, modalias, key, value)
- if (!udev_list_entry_add(&hwdb->properties_list, key, value))
+ if (!udev_list_entry_add(hwdb->properties_list, key, value))
return_with_errno(NULL, ENOMEM);
- e = udev_list_get_entry(&hwdb->properties_list);
+ e = udev_list_get_entry(hwdb->properties_list);
if (!e)
return_with_errno(NULL, ENODATA);
#include "libudev.h"
-struct udev_list_node {
- struct udev_list_node *next, *prev;
-};
+#include "macro.h"
-struct udev_list {
- struct udev_list_node node;
- struct udev_list_entry **entries;
- unsigned entries_cur;
- unsigned entries_max;
- bool unique;
-};
+struct udev_list;
-void udev_list_init(struct udev_list *list, bool unique);
+struct udev_list *udev_list_new(bool unique);
void udev_list_cleanup(struct udev_list *list);
+struct udev_list *udev_list_free(struct udev_list *list);
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_list *, udev_list_free);
+
struct udev_list_entry *udev_list_get_entry(struct udev_list *list);
struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value);
/* SPDX-License-Identifier: LGPL-2.1+ */
-#include <errno.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "alloc-util.h"
+#include "hashmap.h"
#include "libudev-list-internal.h"
-#include "memory-util.h"
+#include "list.h"
+#include "sort-util.h"
/**
* SECTION:libudev-list
* contains a name, and optionally a value.
*/
struct udev_list_entry {
- struct udev_list_node node;
struct udev_list *list;
char *name;
char *value;
- int num;
-};
-/* the list's head points to itself if empty */
-static void udev_list_node_init(struct udev_list_node *list) {
- list->next = list;
- list->prev = list;
-}
+ LIST_FIELDS(struct udev_list_entry, entries);
+};
-static int udev_list_node_is_empty(struct udev_list_node *list) {
- return list->next == list;
-}
+struct udev_list {
+ Hashmap *unique_entries;
+ LIST_HEAD(struct udev_list_entry, entries);
+ bool unique:1;
+ bool uptodate:1;
+};
-static void udev_list_node_insert_between(struct udev_list_node *new,
- struct udev_list_node *prev,
- struct udev_list_node *next) {
- next->prev = new;
- new->next = next;
- new->prev = prev;
- prev->next = new;
-}
+static struct udev_list_entry *udev_list_entry_free(struct udev_list_entry *entry) {
+ if (!entry)
+ return NULL;
-static void udev_list_node_remove(struct udev_list_node *entry) {
- struct udev_list_node *prev = entry->prev;
- struct udev_list_node *next = entry->next;
+ if (entry->list) {
+ if (entry->list->unique)
+ hashmap_remove(entry->list->unique_entries, entry->name);
+ else
+ LIST_REMOVE(entries, entry->list->entries, entry);
+ }
- next->prev = prev;
- prev->next = next;
+ free(entry->name);
+ free(entry->value);
- entry->prev = NULL;
- entry->next = NULL;
+ return mfree(entry);
}
-/* return list entry which embeds this node */
-static struct udev_list_entry *list_node_to_entry(struct udev_list_node *node) {
- return container_of(node, struct udev_list_entry, node);
-}
+DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_list_entry *, udev_list_entry_free);
-void udev_list_init(struct udev_list *list, bool unique) {
- memzero(list, sizeof(struct udev_list));
- list->unique = unique;
- udev_list_node_init(&list->node);
-}
+struct udev_list *udev_list_new(bool unique) {
+ struct udev_list *list;
-/* insert entry into a list as the last element */
-static void udev_list_entry_append(struct udev_list_entry *new, struct udev_list *list) {
- /* inserting before the list head make the node the last node in the list */
- udev_list_node_insert_between(&new->node, list->node.prev, &list->node);
- new->list = list;
-}
+ list = new(struct udev_list, 1);
+ if (!list)
+ return NULL;
-/* insert entry into a list, before a given existing entry */
-static void udev_list_entry_insert_before(struct udev_list_entry *new, struct udev_list_entry *entry) {
- udev_list_node_insert_between(&new->node, entry->node.prev, &entry->node);
- new->list = entry->list;
+ *list = (struct udev_list) {
+ .unique = unique,
+ };
+
+ return list;
}
-/* binary search in sorted array */
-static int list_search(struct udev_list *list, const char *name) {
- unsigned first, last;
-
- first = 0;
- last = list->entries_cur;
- while (first < last) {
- unsigned i;
- int cmp;
-
- i = (first + last)/2;
- cmp = strcmp(name, list->entries[i]->name);
- if (cmp < 0)
- last = i;
- else if (cmp > 0)
- first = i+1;
- else
- return i;
- }
+struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *_name, const char *_value) {
+ _cleanup_(udev_list_entry_freep) struct udev_list_entry *entry = NULL;
+ _cleanup_free_ char *name = NULL, *value = NULL;
+ int r;
- /* not found, return negative insertion-index+1 */
- return -(first+1);
-}
+ assert(list);
-struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char *name, const char *value) {
- struct udev_list_entry *entry;
- int i = 0;
+ name = strdup(_name);
+ if (!name)
+ return NULL;
- if (list->unique) {
- /* lookup existing name or insertion-index */
- i = list_search(list, name);
- if (i >= 0) {
- entry = list->entries[i];
-
- free(entry->value);
- if (!value) {
- entry->value = NULL;
- return entry;
- }
- entry->value = strdup(value);
- if (!entry->value)
- return NULL;
- return entry;
- }
+ if (_value) {
+ value = strdup(_value);
+ if (!value)
+ return NULL;
}
- /* add new name */
- entry = new0(struct udev_list_entry, 1);
+ entry = new(struct udev_list_entry, 1);
if (!entry)
return NULL;
- entry->name = strdup(name);
- if (!entry->name)
- return mfree(entry);
-
- if (value) {
- entry->value = strdup(value);
- if (!entry->value) {
- free(entry->name);
- return mfree(entry);
- }
- }
+ *entry = (struct udev_list_entry) {
+ .list = list,
+ .name = TAKE_PTR(name),
+ .value = TAKE_PTR(value),
+ };
if (list->unique) {
- /* allocate or enlarge sorted array if needed */
- if (list->entries_cur >= list->entries_max) {
- struct udev_list_entry **entries;
- unsigned add;
-
- add = list->entries_max;
- if (add < 1)
- add = 64;
- entries = reallocarray(list->entries, list->entries_max + add, sizeof(struct udev_list_entry *));
- if (!entries) {
- free(entry->name);
- free(entry->value);
- return mfree(entry);
- }
- list->entries = entries;
- list->entries_max += add;
- }
+ r = hashmap_ensure_allocated(&list->unique_entries, &string_hash_ops);
+ if (r < 0)
+ return NULL;
- /* the negative i returned the insertion index */
- i = (-i)-1;
+ udev_list_entry_free(hashmap_get(list->unique_entries, entry->name));
- /* insert into sorted list */
- if ((unsigned)i < list->entries_cur)
- udev_list_entry_insert_before(entry, list->entries[i]);
- else
- udev_list_entry_append(entry, list);
+ r = hashmap_put(list->unique_entries, entry->name, entry);
+ if (r < 0)
+ return NULL;
- /* insert into sorted array */
- memmove(&list->entries[i+1], &list->entries[i],
- (list->entries_cur - i) * sizeof(struct udev_list_entry *));
- list->entries[i] = entry;
- list->entries_cur++;
+ list->uptodate = false;
} else
- udev_list_entry_append(entry, list);
+ LIST_APPEND(entries, list->entries, entry);
- return entry;
+ return TAKE_PTR(entry);
}
-static void udev_list_entry_delete(struct udev_list_entry *entry) {
- if (entry->list->entries) {
- int i;
- struct udev_list *list = entry->list;
-
- /* remove entry from sorted array */
- i = list_search(list, entry->name);
- if (i >= 0) {
- memmove(&list->entries[i], &list->entries[i+1],
- ((list->entries_cur-1) - i) * sizeof(struct udev_list_entry *));
- list->entries_cur--;
- }
- }
+void udev_list_cleanup(struct udev_list *list) {
+ struct udev_list_entry *i, *n;
- udev_list_node_remove(&entry->node);
- free(entry->name);
- free(entry->value);
- free(entry);
+ if (!list)
+ return;
+
+ if (list->unique) {
+ hashmap_clear_with_destructor(list->unique_entries, udev_list_entry_free);
+ list->uptodate = false;
+ } else
+ LIST_FOREACH_SAFE(entries, i, n, list->entries)
+ udev_list_entry_free(i);
}
-#define udev_list_entry_foreach_safe(entry, tmp, first) \
- for (entry = first, tmp = udev_list_entry_get_next(entry); \
- entry; \
- entry = tmp, tmp = udev_list_entry_get_next(tmp))
+struct udev_list *udev_list_free(struct udev_list *list) {
+ if (!list)
+ return NULL;
-void udev_list_cleanup(struct udev_list *list) {
- struct udev_list_entry *entry_loop;
- struct udev_list_entry *entry_tmp;
-
- list->entries = mfree(list->entries);
- list->entries_cur = 0;
- list->entries_max = 0;
- udev_list_entry_foreach_safe(entry_loop, entry_tmp, udev_list_get_entry(list))
- udev_list_entry_delete(entry_loop);
+ udev_list_cleanup(list);
+ hashmap_free(list->unique_entries);
+
+ return mfree(list);
+}
+
+static int udev_list_entry_compare_func(struct udev_list_entry * const *a, struct udev_list_entry * const *b) {
+ return strcmp((*a)->name, (*b)->name);
}
struct udev_list_entry *udev_list_get_entry(struct udev_list *list) {
- if (udev_list_node_is_empty(&list->node))
+ if (!list)
return NULL;
- return list_node_to_entry(list->node.next);
+
+ if (list->unique && !list->uptodate) {
+ size_t n;
+
+ LIST_HEAD_INIT(list->entries);
+
+ n = hashmap_size(list->unique_entries);
+ if (n == 0)
+ ;
+ else if (n == 1)
+ LIST_PREPEND(entries, list->entries, hashmap_first(list->unique_entries));
+ else {
+ _cleanup_free_ struct udev_list_entry **buf = NULL;
+ struct udev_list_entry *entry, **p;
+ Iterator i;
+ size_t j;
+
+ buf = new(struct udev_list_entry *, n);
+ if (!buf)
+ return NULL;
+
+ p = buf;
+ HASHMAP_FOREACH(entry, list->unique_entries, i)
+ *p++ = entry;
+
+ typesafe_qsort(buf, n, udev_list_entry_compare_func);
+
+ for (j = n; j > 0; j--)
+ LIST_PREPEND(entries, list->entries, buf[j-1]);
+ }
+
+ list->uptodate = true;
+ }
+
+ return list->entries;
}
/**
* Returns: udev_list_entry, #NULL if no more entries are available.
*/
_public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry *list_entry) {
- struct udev_list_node *next;
-
if (!list_entry)
return NULL;
- next = list_entry->node.next;
- /* empty list or no more entries */
- if (next == &list_entry->list->node)
+ if (list_entry->list->unique && !list_entry->list->uptodate)
return NULL;
- return list_node_to_entry(next);
+ return list_entry->entries_next;
}
/**
* Returns: udev_list_entry, #NULL if no matching entry is found.
*/
_public_ struct udev_list_entry *udev_list_entry_get_by_name(struct udev_list_entry *list_entry, const char *name) {
- int i;
-
if (!list_entry)
return NULL;
-
- if (!list_entry->list->unique)
- return NULL;
-
- i = list_search(list_entry->list, name);
- if (i < 0)
+ if (!list_entry->list->unique || !list_entry->list->uptodate)
return NULL;
- return list_entry->list->entries[i];
+ return hashmap_get(list_entry->list->unique_entries, name);
}
/**
if (in_section && first_word(l, "Option")) {
_cleanup_strv_free_ char **a = NULL;
- r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES);
+ r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return r;
} else if (!in_section && first_word(l, "Section")) {
_cleanup_strv_free_ char **a = NULL;
- r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_QUOTES);
+ r = strv_split_extract(&a, l, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return -ENOMEM;
if (IN_SET(l[0], 0, '#'))
continue;
- r = strv_split_extract(&b, l, WHITESPACE, EXTRACT_QUOTES);
+ r = strv_split_extract(&b, l, WHITESPACE, EXTRACT_UNQUOTE);
if (r < 0)
return r;
if (startswith_comma(c->x11_layout, a[1]))
matching = 5;
else {
- char *x;
+ _cleanup_free_ char *x = NULL;
/* If that didn't work, strip off the
* other layouts from the entry, too */
- x = strndupa(a[1], strcspn(a[1], ","));
+ x = strndup(a[1], strcspn(a[1], ","));
if (startswith_comma(c->x11_layout, x))
matching = 1;
}
SUBSYSTEM=="input", KERNEL=="input*", TAG+="seat"
SUBSYSTEM=="graphics", KERNEL=="fb[0-9]*", TAG+="seat"
+# Assign keyboard and LCD backlights to the seat
+SUBSYSTEM=="leds", TAG+="seat"
+SUBSYSTEM=="backlight", TAG+="seat"
+
# HyperV currently doesn't do DRM, hence we need to synthesize for HyperV's fb device instead
SUBSYSTEM=="graphics", KERNEL=="fb[0-9]", DRIVERS=="hyperv_fb", TAG+="master-of-seat"
(void) pager_open(arg_pager_flags);
if (argc <= 1) {
- const char *session, *p = "/org/freedesktop/login1/session/self";
-
+ /* If no argument is specified inspect the manager itself */
if (properties)
- /* If no argument is specified inspect the manager itself */
return show_properties(bus, "/org/freedesktop/login1", &new_line);
- /* And in the pretty case, show data of the calling session */
- session = getenv("XDG_SESSION_ID");
- if (session) {
- r = get_session_path(bus, session, &error, &path);
- if (r < 0)
- return log_error_errno(r, "Failed to get session path: %s", bus_error_message(&error, r));
-
- p = path;
- }
-
- return print_session_status_info(bus, p, &new_line);
+ return print_session_status_info(bus, "/org/freedesktop/login1/session/auto", &new_line);
}
for (i = 1; i < argc; i++) {
(void) pager_open(arg_pager_flags);
if (argc <= 1) {
- /* If not argument is specified inspect the manager
- * itself */
+ /* If no argument is specified inspect the manager itself */
if (properties)
return show_properties(bus, "/org/freedesktop/login1", &new_line);
(void) pager_open(arg_pager_flags);
if (argc <= 1) {
- /* If not argument is specified inspect the manager
- * itself */
+ /* If no argument is specified inspect the manager itself */
if (properties)
return show_properties(bus, "/org/freedesktop/login1", &new_line);
- return print_seat_status_info(bus, "/org/freedesktop/login1/seat/self", &new_line);
+ return print_seat_status_info(bus, "/org/freedesktop/login1/seat/auto", &new_line);
}
for (i = 1; i < argc; i++) {
polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
if (argc < 2) {
- /* No argument? Let's either use $XDG_SESSION_ID (if specified), or an empty
- * session name, in which case logind will try to guess our session. */
-
short_argv[0] = argv[0];
- short_argv[1] = getenv("XDG_SESSION_ID") ?: (char*) "";
+ short_argv[1] = (char*) "";
short_argv[2] = NULL;
argv = short_argv;
&error, NULL,
"s", argv[i]);
if (r < 0)
- return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, -r));
+ return log_error_errno(r, "Failed to issue method call: %s", bus_error_message(&error, r));
}
return 0;
#include "conf-parser.h"
#include "format-util.h"
#include "logind-action.h"
+#include "logind-dbus.h"
+#include "logind-session-dbus.h"
#include "process-util.h"
#include "sleep-config.h"
#include "special.h"
int r;
assert(m);
-
- /* If the key handling is turned off, don't do anything */
- if (handle == HANDLE_IGNORE) {
- log_debug("Refusing operation, as it is turned off.");
- return 0;
- }
+ /* We should be called only with valid actions different than HANDLE_IGNORE. */
+ assert(handle > HANDLE_IGNORE && handle < _HANDLE_ACTION_MAX);
if (inhibit_key == INHIBIT_HANDLE_LID_SWITCH) {
/* If the last system suspend or startup is too close,
/* If the key handling is inhibited, don't do anything */
if (inhibit_key > 0) {
if (manager_is_inhibited(m, inhibit_key, INHIBIT_BLOCK, NULL, true, false, 0, NULL)) {
- log_debug("Refusing operation, %s is inhibited.", inhibit_what_to_string(inhibit_key));
+ log_debug("Refusing %s operation, %s is inhibited.",
+ handle_action_to_string(handle),
+ inhibit_what_to_string(inhibit_key));
return 0;
}
}
if (!supported && IN_SET(handle, HANDLE_HIBERNATE, HANDLE_HYBRID_SLEEP, HANDLE_SUSPEND_THEN_HIBERNATE)) {
supported = can_sleep("suspend") > 0;
if (supported) {
- log_notice("Operation '%s' requested but not supported, using regular suspend instead.", handle_action_to_string(handle));
+ log_notice("Requested %s operation is not supported, using regular suspend instead.",
+ handle_action_to_string(handle));
handle = HANDLE_SUSPEND;
}
}
- if (!supported) {
- log_warning("Requested operation not supported, ignoring.");
- return -EOPNOTSUPP;
- }
+ if (!supported)
+ return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Requested %s operation not supported, ignoring.", handle_action_to_string(handle));
- if (m->action_what > 0) {
- log_debug("Action already in progress, ignoring.");
- return -EALREADY;
- }
+ if (m->action_what > 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EALREADY),
+ "Action already in progress (%s), ignoring requested %s operation.",
+ inhibit_what_to_string(m->action_what),
+ handle_action_to_string(handle));
assert_se(target = manager_target_for_action(handle));
u = uid_to_name(offending->uid);
/* If this is just a recheck of the lid switch then don't warn about anything */
- if (!is_edge) {
- log_debug("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.",
- inhibit_what_to_string(inhibit_operation),
- offending->uid, strna(u),
- offending->pid, strna(comm));
- return 0;
- }
-
- log_error("Refusing operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.",
- inhibit_what_to_string(inhibit_operation),
- offending->uid, strna(u),
- offending->pid, strna(comm));
-
- return -EPERM;
+ log_full(is_edge ? LOG_ERR : LOG_DEBUG,
+ "Refusing %s operation, %s is inhibited by UID "UID_FMT"/%s, PID "PID_FMT"/%s.",
+ handle_action_to_string(handle),
+ inhibit_what_to_string(inhibit_operation),
+ offending->uid, strna(u),
+ offending->pid, strna(comm));
+
+ return is_edge ? -EPERM : 0;
}
log_info("%s", message_table[handle]);
r = bus_manager_shutdown_or_sleep_now_or_later(m, target, inhibit_operation, &error);
if (r < 0)
- return log_error_errno(r, "Failed to execute operation: %s", bus_error_message(&error, r));
+ return log_error_errno(r, "Failed to execute %s operation: %s",
+ handle_action_to_string(handle),
+ bus_error_message(&error, r));
return 1;
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-util.h"
+#include "device-util.h"
+#include "hash-funcs.h"
+#include "logind-brightness.h"
+#include "logind.h"
+#include "process-util.h"
+#include "stdio-util.h"
+
+/* Brightness and LED devices tend to be very slow to write to (often being I2C and such). Writes to the
+ * sysfs attributes are synchronous, and hence will freeze our process on access. We can't really have that,
+ * hence we add some complexity: whenever we need to write to the brightness attribute, we do so in a forked
+ * off process, which terminates when it is done. Watching that process allows us to watch completion of the
+ * write operation.
+ *
+ * To make this even more complex: clients are likely to send us many write requests in a short time-frame
+ * (because they implement reactive brightness sliders on screen). Let's coalesce writes to make this
+ * efficient: whenever we get requests to change brightness while we are still writing to the brightness
+ * attribute, let's remember the request and restart a new one when the initial operation finished. When we
+ * get another request while one is ongoing and one is pending we'll replace the pending one with the new
+ * one.
+ *
+ * The bus messages are answered when the first write operation finishes that started either due to the
+ * request or due to a later request that overrode the requested one.
+ *
+ * Yes, this is complex, but I don't see an easier way if we want to be both efficient and still support
+ * completion notification. */
+
+typedef struct BrightnessWriter {
+ Manager *manager;
+
+ sd_device *device;
+ char *path;
+
+ pid_t child;
+
+ uint32_t brightness;
+ bool again;
+
+ Set *current_messages;
+ Set *pending_messages;
+
+ sd_event_source* child_event_source;
+} BrightnessWriter;
+
+static void brightness_writer_free(BrightnessWriter *w) {
+ if (!w)
+ return;
+
+ if (w->manager && w->path)
+ (void) hashmap_remove_value(w->manager->brightness_writers, w->path, w);
+
+ sd_device_unref(w->device);
+ free(w->path);
+
+ set_free(w->current_messages);
+ set_free(w->pending_messages);
+
+ w->child_event_source = sd_event_source_unref(w->child_event_source);
+
+ free(w);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(BrightnessWriter*, brightness_writer_free);
+
+DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+ brightness_writer_hash_ops,
+ char,
+ string_hash_func,
+ string_compare_func,
+ BrightnessWriter,
+ brightness_writer_free);
+
+static void brightness_writer_reply(BrightnessWriter *w, int error) {
+ int r;
+
+ assert(w);
+
+ for (;;) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ m = set_steal_first(w->current_messages);
+ if (!m)
+ break;
+
+ if (error == 0)
+ r = sd_bus_reply_method_return(m, NULL);
+ else
+ r = sd_bus_reply_method_errnof(m, error, "Failed to write to brightness device: %m");
+ if (r < 0)
+ log_warning_errno(r, "Failed to send method reply, ignoring: %m");
+ }
+}
+
+static int brightness_writer_fork(BrightnessWriter *w);
+
+static int on_brightness_writer_exit(sd_event_source *s, const siginfo_t *si, void *userdata) {
+ BrightnessWriter *w = userdata;
+ int r;
+
+ assert(s);
+ assert(si);
+ assert(w);
+
+ assert(si->si_pid == w->child);
+ w->child = 0;
+ w->child_event_source = sd_event_source_unref(w->child_event_source);
+
+ brightness_writer_reply(w,
+ si->si_code == CLD_EXITED &&
+ si->si_status == EXIT_SUCCESS ? 0 : -EPROTO);
+
+ if (w->again) {
+ /* Another request to change the brightness has been queued. Act on it, but make the pending
+ * messages the current ones. */
+ w->again = false;
+ set_free(w->current_messages);
+ w->current_messages = TAKE_PTR(w->pending_messages);
+
+ r = brightness_writer_fork(w);
+ if (r >= 0)
+ return 0;
+
+ brightness_writer_reply(w, r);
+ }
+
+ brightness_writer_free(w);
+ return 0;
+}
+
+static int brightness_writer_fork(BrightnessWriter *w) {
+ int r;
+
+ assert(w);
+ assert(w->manager);
+ assert(w->child == 0);
+ assert(!w->child_event_source);
+
+ r = safe_fork("(sd-bright)", FORK_DEATHSIG|FORK_NULL_STDIO|FORK_CLOSE_ALL_FDS|FORK_LOG, &w->child);
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ char brs[DECIMAL_STR_MAX(uint32_t)+1];
+
+ /* Child */
+ xsprintf(brs, "%" PRIu32, w->brightness);
+
+ r = sd_device_set_sysattr_value(w->device, "brightness", brs);
+ if (r < 0) {
+ log_device_error_errno(w->device, r, "Failed to write brightness to device: %m");
+ _exit(EXIT_FAILURE);
+ }
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ r = sd_event_add_child(w->manager->event, &w->child_event_source, w->child, WEXITED, on_brightness_writer_exit, w);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch brightness writer child " PID_FMT ": %m", w->child);
+
+ return 0;
+}
+
+static int set_add_message(Set **set, sd_bus_message *message) {
+ int r;
+
+ assert(set);
+
+ if (!message)
+ return 0;
+
+ r = sd_bus_message_get_expect_reply(message);
+ if (r <= 0)
+ return r;
+
+ r = set_ensure_allocated(set, &bus_message_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = set_put(*set, message);
+ if (r < 0)
+ return r;
+
+ sd_bus_message_ref(message);
+ return 1;
+}
+
+int manager_write_brightness(
+ Manager *m,
+ sd_device *device,
+ uint32_t brightness,
+ sd_bus_message *message) {
+
+ _cleanup_(brightness_writer_freep) BrightnessWriter *w = NULL;
+ BrightnessWriter *existing;
+ const char *path;
+ int r;
+
+ assert(m);
+ assert(device);
+
+ r = sd_device_get_syspath(device, &path);
+ if (r < 0)
+ return log_device_error_errno(device, r, "Failed to get sysfs path for brightness device: %m");
+
+ existing = hashmap_get(m->brightness_writers, path);
+ if (existing) {
+ /* There's already a writer for this device. Let's update it with the new brightness, and add
+ * our message to the set of message to reply when done. */
+
+ r = set_add_message(&existing->pending_messages, message);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add message to set: %m");
+
+ /* We overide any previously requested brightness here: we coalesce writes, and the newest
+ * requested brightness is the one we'll put into effect. */
+ existing->brightness = brightness;
+ existing->again = true; /* request another iteration of the writer when the current one is
+ * complete */
+ return 0;
+ }
+
+ r = hashmap_ensure_allocated(&m->brightness_writers, &brightness_writer_hash_ops);
+ if (r < 0)
+ return log_oom();
+
+ w = new(BrightnessWriter, 1);
+ if (!w)
+ return log_oom();
+
+ *w = (BrightnessWriter) {
+ .device = sd_device_ref(device),
+ .path = strdup(path),
+ .brightness = brightness,
+ };
+
+ if (!w->path)
+ return log_oom();
+
+ r = hashmap_put(m->brightness_writers, w->path, w);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add brightness writer to hashmap: %m");
+ w->manager = m;
+
+ r = set_add_message(&w->current_messages, message);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add message to set: %m");
+
+ r = brightness_writer_fork(w);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(w);
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+#include "sd-device.h"
+
+#include "logind.h"
+
+int manager_write_brightness(Manager *m, sd_device *device, uint32_t brightness, sd_bus_message *message);
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
+#include "logind-dbus.h"
+#include "logind-seat-dbus.h"
+#include "logind-session-dbus.h"
+#include "logind-user-dbus.h"
#include "logind.h"
#include "missing_capability.h"
#include "mkdir.h"
#include "utmp-wtmp.h"
#include "virt.h"
-static int get_sender_session(Manager *m, sd_bus_message *message, sd_bus_error *error, Session **ret) {
+static int get_sender_session(
+ Manager *m,
+ sd_bus_message *message,
+ bool consult_display,
+ sd_bus_error *error,
+ Session **ret) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ Session *session = NULL;
const char *name;
- Session *session;
int r;
- /* Get client login session. This is not what you are looking for these days,
- * as apps may instead belong to a user service unit. This includes terminal
- * emulators and hence command-line apps. */
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
+ /* Acquire the sender's session. This first checks if the sending process is inside a session itself,
+ * and returns that. If not and 'consult_display' is true, this returns the display session of the
+ * owning user of the caller. */
+
+ r = sd_bus_query_sender_creds(message,
+ SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT|
+ (consult_display ? SD_BUS_CREDS_OWNER_UID : 0), &creds);
if (r < 0)
return r;
r = sd_bus_creds_get_session(creds, &name);
- if (r == -ENXIO)
- goto err_no_session;
- if (r < 0)
- return r;
+ if (r < 0) {
+ if (r != -ENXIO)
+ return r;
+
+ if (consult_display) {
+ uid_t uid;
+
+ r = sd_bus_creds_get_owner_uid(creds, &uid);
+ if (r < 0) {
+ if (r != -ENXIO)
+ return r;
+ } else {
+ User *user;
+
+ user = hashmap_get(m->users, UID_TO_PTR(uid));
+ if (user)
+ session = user->display;
+ }
+ }
+ } else
+ session = hashmap_get(m->sessions, name);
- session = hashmap_get(m->sessions, name);
if (!session)
- goto err_no_session;
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID,
+ consult_display ?
+ "Caller does not belong to any known session and doesn't own any suitable session." :
+ "Caller does not belong to any known session.");
*ret = session;
return 0;
-
-err_no_session:
- return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID,
- "Caller does not belong to any known session");
}
-int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret) {
+int manager_get_session_from_creds(
+ Manager *m,
+ sd_bus_message *message,
+ const char *name,
+ sd_bus_error *error,
+ Session **ret) {
+
Session *session;
assert(m);
assert(message);
assert(ret);
- if (isempty(name))
- return get_sender_session(m, message, error, ret);
+ if (SEAT_IS_SELF(name)) /* the caller's own session */
+ return get_sender_session(m, message, false, error, ret);
+ if (SEAT_IS_AUTO(name)) /* The caller's own session if they have one, otherwise their user's display session */
+ return get_sender_session(m, message, true, error, ret);
session = hashmap_get(m->sessions, name);
if (!session)
}
static int get_sender_user(Manager *m, sd_bus_message *message, sd_bus_error *error, User **ret) {
-
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
uid_t uid;
User *user;
return r;
r = sd_bus_creds_get_owner_uid(creds, &uid);
- if (r == -ENXIO)
- goto err_no_user;
- if (r < 0)
- return r;
+ if (r < 0) {
+ if (r != -ENXIO)
+ return r;
+
+ user = NULL;
+ } else
+ user = hashmap_get(m->users, UID_TO_PTR(uid));
- user = hashmap_get(m->users, UID_TO_PTR(uid));
if (!user)
- goto err_no_user;
+ return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID,
+ "Caller does not belong to any logged in or lingering user");
*ret = user;
return 0;
-
-err_no_user:
- return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID,
- "Caller does not belong to any logged in user or lingering user");
}
int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret) {
return 0;
}
-int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret) {
+int manager_get_seat_from_creds(
+ Manager *m,
+ sd_bus_message *message,
+ const char *name,
+ sd_bus_error *error,
+ Seat **ret) {
+
Seat *seat;
int r;
assert(message);
assert(ret);
- if (isempty(name)) {
+ if (SEAT_IS_SELF(name) || SEAT_IS_AUTO(name)) {
Session *session;
- r = manager_get_session_from_creds(m, message, NULL, error, &session);
+ /* Use these special seat names as session names */
+ r = manager_get_session_from_creds(m, message, name, error, &session);
if (r < 0)
return r;
seat = session->seat;
if (!seat)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session has no seat.");
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "Session '%s' has no seat.", session->id);
} else {
seat = hashmap_get(m->seats, name);
if (!seat)
if (asprintf(&id, "%"PRIu32, audit_id) < 0)
return -ENOMEM;
- /* Wut? There's already a session by this name and we
- * didn't find it above? Weird, then let's not trust
- * the audit data and let's better register a new
- * ID */
- if (hashmap_get(m->sessions, id)) {
+ /* Wut? There's already a session by this name and we didn't find it above? Weird, then let's
+ * not trust the audit data and let's better register a new ID */
+ if (hashmap_contains(m->sessions, id)) {
log_warning("Existing logind session ID %s used by new audit session, ignoring.", id);
audit_id = AUDIT_SESSION_INVALID;
id = mfree(id);
if (asprintf(&id, "c%lu", ++m->session_counter) < 0)
return -ENOMEM;
- } while (hashmap_get(m->sessions, id));
+ } while (hashmap_contains(m->sessions, id));
}
+ /* The generated names should not clash with 'auto' or 'self' */
+ assert(!SESSION_IS_SELF(id));
+ assert(!SESSION_IS_AUTO(id));
+
/* If we are not watching utmp already, try again */
manager_reconnect_utmp(m);
assert(message);
assert(m);
- /* Same as ActivateSession() but refuses to work if
- * the seat doesn't match */
+ /* Same as ActivateSession() but refuses to work if the seat doesn't match */
r = sd_bus_message_read(message, "ss", &session_name, &seat_name);
if (r < 0)
if (r < 0)
return r;
+ if (!path_is_normalized(sysfs))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not normalized", sysfs);
if (!path_startswith(sysfs, "/sys"))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path %s is not in /sys", sysfs);
- if (!seat_name_is_valid(seat))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat %s is not valid", seat);
+ if (SEAT_IS_SELF(seat) || SEAT_IS_AUTO(seat)) {
+ Seat *found;
+
+ r = manager_get_seat_from_creds(m, message, seat, error, &found);
+ if (r < 0)
+ return r;
+
+ seat = found->id;
+
+ } else if (!seat_name_is_valid(seat)) /* Note that a seat does not have to exist yet for this operation to succeed */
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat name %s is not valid", seat);
r = bus_verify_polkit_async(
message,
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "logind.h"
+#include "logind-session.h"
+#include "logind-user.h"
+
+int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret);
+int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret);
+int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret);
+
+int manager_dispatch_delayed(Manager *manager, bool timeout);
+
+int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, sd_bus_error *error);
+
+int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error);
+
+int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
+
+int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, char **wants, char **after, const char *requires_mounts_for, sd_bus_message *more_properties, sd_bus_error *error, char **job);
+int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
+int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
+int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error);
+int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error);
+int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *error);
+int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *error);
#include "alloc-util.h"
#include "logind-device.h"
+#include "logind-seat-dbus.h"
#include "util.h"
Device* device_new(Manager *m, const char *sysfs, bool master) {
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
+#include "logind-dbus.h"
#include "logind-inhibit.h"
#include "mkdir.h"
#include "parse-util.h"
#include "bus-common-errors.h"
#include "bus-label.h"
#include "bus-util.h"
+#include "logind-dbus.h"
+#include "logind-seat-dbus.h"
#include "logind-seat.h"
+#include "logind-session-dbus.h"
#include "logind.h"
#include "missing_capability.h"
#include "strv.h"
};
int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+ _cleanup_free_ char *e = NULL;
+ sd_bus_message *message;
Manager *m = userdata;
+ const char *p;
Seat *seat;
int r;
assert(found);
assert(m);
- if (streq(path, "/org/freedesktop/login1/seat/self")) {
- sd_bus_message *message;
-
- message = sd_bus_get_current_message(bus);
- if (!message)
- return 0;
-
- r = manager_get_seat_from_creds(m, message, NULL, error, &seat);
- if (r < 0)
- return r;
- } else {
- _cleanup_free_ char *e = NULL;
- const char *p;
+ p = startswith(path, "/org/freedesktop/login1/seat/");
+ if (!p)
+ return 0;
- p = startswith(path, "/org/freedesktop/login1/seat/");
- if (!p)
- return 0;
+ e = bus_label_unescape(p);
+ if (!e)
+ return -ENOMEM;
- e = bus_label_unescape(p);
- if (!e)
- return -ENOMEM;
+ message = sd_bus_get_current_message(bus);
+ if (!message)
+ return 0;
- seat = hashmap_get(m->seats, e);
- if (!seat)
- return 0;
+ r = manager_get_seat_from_creds(m, message, e, error, &seat);
+ if (r == -ENXIO) {
+ sd_bus_error_free(error);
+ return 0;
}
+ if (r < 0)
+ return r;
*found = seat;
return 1;
message = sd_bus_get_current_message(bus);
if (message) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- const char *name;
- Session *session;
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
if (r >= 0) {
+ bool may_auto = false;
+ const char *name;
+
r = sd_bus_creds_get_session(creds, &name);
if (r >= 0) {
+ Session *session;
+
session = hashmap_get(m->sessions, name);
if (session && session->seat) {
r = strv_extend(&l, "/org/freedesktop/login1/seat/self");
if (r < 0)
return r;
+
+ may_auto = true;
}
}
+
+ if (!may_auto) {
+ uid_t uid;
+
+ r = sd_bus_creds_get_owner_uid(creds, &uid);
+ if (r >= 0) {
+ User *user;
+
+ user = hashmap_get(m->users, UID_TO_PTR(uid));
+ may_auto = user && user->display && user->display->seat;
+ }
+ }
+
+ if (may_auto) {
+ r = strv_extend(&l, "/org/freedesktop/login1/seat/auto");
+ if (r < 0)
+ return r;
+ }
}
}
*nodes = TAKE_PTR(l);
-
return 1;
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "logind-seat.h"
+
+extern const sd_bus_vtable seat_vtable[];
+
+int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+char *seat_bus_path(Seat *s);
+
+int seat_send_signal(Seat *s, bool new_seat);
+int seat_send_changed(Seat *s, const char *properties, ...) _sentinel_;
+
+int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
#include "sd-messages.h"
#include "alloc-util.h"
+#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "logind-acl.h"
+#include "logind-seat-dbus.h"
#include "logind-seat.h"
+#include "logind-session-dbus.h"
#include "mkdir.h"
#include "parse-util.h"
#include "stdio-util.h"
k = read(s->manager->console_active_fd, t, sizeof(t)-1);
if (k <= 0) {
- log_error("Failed to read current console: %s", k < 0 ? strerror(errno) : "EOF");
+ log_error("Failed to read current console: %s", k < 0 ? strerror_safe(errno) : "EOF");
return k < 0 ? -errno : -EIO;
}
bool seat_name_is_valid(const char *name);
-extern const sd_bus_vtable seat_vtable[];
+static inline bool SEAT_IS_SELF(const char *name) {
+ return isempty(name) || streq(name, "self");
+}
-int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-int seat_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-char *seat_bus_path(Seat *s);
-
-int seat_send_signal(Seat *s, bool new_seat);
-int seat_send_changed(Seat *s, const char *properties, ...) _sentinel_;
-
-int bus_seat_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+static inline bool SEAT_IS_AUTO(const char *name) {
+ return streq_ptr(name, "auto");
+}
#include "bus-label.h"
#include "bus-util.h"
#include "fd-util.h"
+#include "logind-brightness.h"
+#include "logind-dbus.h"
+#include "logind-seat-dbus.h"
+#include "logind-session-dbus.h"
#include "logind-session-device.h"
#include "logind-session.h"
+#include "logind-user-dbus.h"
#include "logind.h"
#include "missing_capability.h"
+#include "path-util.h"
#include "signal-util.h"
#include "stat-util.h"
#include "strv.h"
+#include "user-util.h"
#include "util.h"
static int property_get_user(
return sd_bus_reply_method_return(message, NULL);
}
+static int method_set_brightness(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ _cleanup_(sd_device_unrefp) sd_device *d = NULL;
+ const char *subsystem, *name, *seat;
+ Session *s = userdata;
+ uint32_t brightness;
+ uid_t uid;
+ int r;
+
+ assert(message);
+ assert(s);
+
+ r = sd_bus_message_read(message, "ssu", &subsystem, &name, &brightness);
+ if (r < 0)
+ return r;
+
+ if (!STR_IN_SET(subsystem, "backlight", "leds"))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Subsystem type %s not supported, must be one of 'backlight' or 'leds'.", subsystem);
+ if (!filename_is_valid(name))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Not a valid device name %s, refusing.", name);
+
+ if (!s->seat)
+ return sd_bus_error_setf(error, BUS_ERROR_NOT_YOUR_DEVICE, "Your session has no seat, refusing.");
+ if (s->seat->active != s)
+ return sd_bus_error_setf(error, BUS_ERROR_NOT_YOUR_DEVICE, "Session is not in foreground, refusing.");
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_euid(creds, &uid);
+ if (r < 0)
+ return r;
+
+ if (uid != 0 && uid != s->user->uid)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may change brightness.");
+
+ r = sd_device_new_from_subsystem_sysname(&d, subsystem, name);
+ if (r < 0)
+ return sd_bus_error_set_errnof(error, r, "Failed to open device %s:%s: %m", subsystem, name);
+
+ if (sd_device_get_property_value(d, "ID_SEAT", &seat) >= 0 && !streq_ptr(seat, s->seat->id))
+ return sd_bus_error_setf(error, BUS_ERROR_NOT_YOUR_DEVICE, "Device %s:%s does not belong to your seat %s, refusing.", subsystem, name, s->seat->id);
+
+ r = manager_write_brightness(s->manager, d, brightness, message);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
const sd_bus_vtable session_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SetBrightness", "ssu", NULL, method_set_brightness, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_SIGNAL("PauseDevice", "uus", 0),
SD_BUS_SIGNAL("ResumeDevice", "uuh", 0),
};
int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error) {
+ _cleanup_free_ char *e = NULL;
+ sd_bus_message *message;
Manager *m = userdata;
Session *session;
+ const char *p;
int r;
assert(bus);
assert(found);
assert(m);
- if (streq(path, "/org/freedesktop/login1/session/self")) {
- sd_bus_message *message;
-
- message = sd_bus_get_current_message(bus);
- if (!message)
- return 0;
-
- r = manager_get_session_from_creds(m, message, NULL, error, &session);
- if (r < 0)
- return r;
- } else {
- _cleanup_free_ char *e = NULL;
- const char *p;
+ p = startswith(path, "/org/freedesktop/login1/session/");
+ if (!p)
+ return 0;
- p = startswith(path, "/org/freedesktop/login1/session/");
- if (!p)
- return 0;
+ e = bus_label_unescape(p);
+ if (!e)
+ return -ENOMEM;
- e = bus_label_unescape(p);
- if (!e)
- return -ENOMEM;
+ message = sd_bus_get_current_message(bus);
+ if (!message)
+ return 0;
- session = hashmap_get(m->sessions, e);
- if (!session)
- return 0;
+ r = manager_get_session_from_creds(m, message, e, error, &session);
+ if (r == -ENXIO) {
+ sd_bus_error_free(error);
+ return 0;
}
+ if (r < 0)
+ return r;
*found = session;
return 1;
message = sd_bus_get_current_message(bus);
if (message) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- const char *name;
- r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_AUGMENT, &creds);
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
if (r >= 0) {
+ bool may_auto = false;
+ const char *name;
+
r = sd_bus_creds_get_session(creds, &name);
if (r >= 0) {
session = hashmap_get(m->sessions, name);
r = strv_extend(&l, "/org/freedesktop/login1/session/self");
if (r < 0)
return r;
+
+ may_auto = true;
+ }
+ }
+
+ if (!may_auto) {
+ uid_t uid;
+
+ r = sd_bus_creds_get_owner_uid(creds, &uid);
+ if (r >= 0) {
+ User *user;
+
+ user = hashmap_get(m->users, UID_TO_PTR(uid));
+ may_auto = user && user->display;
}
}
+
+ if (may_auto) {
+ r = strv_extend(&l, "/org/freedesktop/login1/session/auto");
+ if (r < 0)
+ return r;
+ }
}
}
*nodes = TAKE_PTR(l);
-
return 1;
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "logind-session.h"
+
+extern const sd_bus_vtable session_vtable[];
+int session_node_enumerator(sd_bus *bus, const char *path,void *userdata, char ***nodes, sd_bus_error *error);
+int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+char *session_bus_path(Session *s);
+
+int session_send_signal(Session *s, bool new_session);
+int session_send_changed(Session *s, const char *properties, ...) _sentinel_;
+int session_send_lock(Session *s, bool lock);
+int session_send_lock_all(Manager *m, bool lock);
+
+int session_send_create_reply(Session *s, sd_bus_error *error);
+
+int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
#include <sys/types.h>
#include "sd-device.h"
+#include "sd-daemon.h"
#include "alloc-util.h"
#include "bus-util.h"
#include "fd-util.h"
+#include "logind-session-dbus.h"
#include "logind-session-device.h"
#include "missing.h"
#include "parse-util.h"
-#include "sd-daemon.h"
#include "util.h"
enum SessionDeviceNotifications {
#include "fileio.h"
#include "format-util.h"
#include "io-util.h"
+#include "logind-dbus.h"
+#include "logind-seat-dbus.h"
+#include "logind-session-dbus.h"
#include "logind-session.h"
+#include "logind-user-dbus.h"
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
#include "list.h"
#include "login-util.h"
#include "logind-user.h"
+#include "string-util.h"
typedef enum SessionState {
SESSION_OPENING, /* Session scope is being created */
SessionState session_get_state(Session *u);
-extern const sd_bus_vtable session_vtable[];
-int session_node_enumerator(sd_bus *bus, const char *path,void *userdata, char ***nodes, sd_bus_error *error);
-int session_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-char *session_bus_path(Session *s);
-
-int session_send_signal(Session *s, bool new_session);
-int session_send_changed(Session *s, const char *properties, ...) _sentinel_;
-int session_send_lock(Session *s, bool lock);
-int session_send_lock_all(Manager *m, bool lock);
-
-int session_send_create_reply(Session *s, sd_bus_error *error);
-
const char* session_state_to_string(SessionState t) _const_;
SessionState session_state_from_string(const char *s) _pure_;
int session_set_controller(Session *s, const char *sender, bool force, bool prepare);
void session_drop_controller(Session *s);
-int bus_session_method_activate(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
+static inline bool SESSION_IS_SELF(const char *name) {
+ return isempty(name) || streq(name, "self");
+}
+
+static inline bool SESSION_IS_AUTO(const char *name) {
+ return streq_ptr(name, "auto");
+}
#include "alloc-util.h"
#include "bus-util.h"
#include "format-util.h"
+#include "logind-dbus.h"
+#include "logind-session-dbus.h"
+#include "logind-user-dbus.h"
#include "logind-user.h"
#include "logind.h"
#include "missing_capability.h"
return 0;
r = manager_get_user_from_creds(m, message, UID_INVALID, error, &user);
+ if (r == -ENXIO) {
+ sd_bus_error_free(error);
+ return 0;
+ }
if (r < 0)
return r;
} else {
message = sd_bus_get_current_message(bus);
if (message) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
- uid_t uid;
r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_OWNER_UID|SD_BUS_CREDS_AUGMENT, &creds);
if (r >= 0) {
+ uid_t uid;
+
r = sd_bus_creds_get_owner_uid(creds, &uid);
if (r >= 0) {
user = hashmap_get(m->users, UID_TO_PTR(uid));
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "logind-user.h"
+
+extern const sd_bus_vtable user_vtable[];
+int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+char *user_bus_path(User *s);
+
+int user_send_signal(User *u, bool new_user);
+int user_send_changed(User *u, const char *properties, ...) _sentinel_;
+
+int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
+int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
#include "hashmap.h"
#include "label.h"
#include "limits-util.h"
+#include "logind-dbus.h"
#include "logind-user.h"
+#include "logind-user-dbus.h"
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
/* Return true if the session is a candidate for the user’s ‘primary session’ or ‘display’. */
assert(s);
- return s->class == SESSION_USER && s->started && !s->stopping;
+ return IN_SET(s->class, SESSION_USER, SESSION_GREETER) && s->started && !s->stopping;
}
static int elect_display_compare(Session *s1, Session *s2) {
/* Indexed by SessionType. Lower numbers mean more preferred. */
- const int type_ranks[_SESSION_TYPE_MAX] = {
+ static const int type_ranks[_SESSION_TYPE_MAX] = {
[SESSION_UNSPECIFIED] = 0,
[SESSION_TTY] = -2,
[SESSION_X11] = -3,
void user_elect_display(User *u);
void user_update_last_session_timer(User *u);
-extern const sd_bus_vtable user_vtable[];
-int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-int user_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-char *user_bus_path(User *s);
-
-int user_send_signal(User *u, bool new_user);
-int user_send_changed(User *u, const char *properties, ...) _sentinel_;
-
const char* user_state_to_string(UserState s) _const_;
UserState user_state_from_string(const char *s) _pure_;
-int bus_user_method_terminate(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int bus_user_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error);
-
CONFIG_PARSER_PROTOTYPE(config_parse_compat_user_tasks_max);
#include "fd-util.h"
#include "format-util.h"
#include "fs-util.h"
+#include "logind-dbus.h"
+#include "logind-seat-dbus.h"
+#include "logind-session-dbus.h"
+#include "logind-user-dbus.h"
#include "logind.h"
#include "main-func.h"
#include "parse-util.h"
*m = (Manager) {
.console_active_fd = -1,
.reserve_vt_fd = -1,
+ .idle_action_not_before_usec = now(CLOCK_MONOTONIC),
};
- m->idle_action_not_before_usec = now(CLOCK_MONOTONIC);
-
m->devices = hashmap_new(&string_hash_ops);
m->seats = hashmap_new(&string_hash_ops);
m->sessions = hashmap_new(&string_hash_ops);
hashmap_free(m->users);
hashmap_free(m->inhibitors);
hashmap_free(m->buttons);
+ hashmap_free(m->brightness_writers);
hashmap_free(m->user_units);
hashmap_free(m->session_units);
if (n >= since.monotonic + m->idle_action_usec &&
(m->idle_action_not_before_usec <= 0 || n >= m->idle_action_not_before_usec + m->idle_action_usec)) {
- log_info("System idle. Taking action.");
+ log_info("System idle. Doing %s operation.", handle_action_to_string(m->idle_action));
manager_handle_action(m, 0, m->idle_action, false, false);
m->idle_action_not_before_usec = n;
(void) mkdir_label("/run/systemd/users", 0755);
(void) mkdir_label("/run/systemd/sessions", 0755);
- assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGHUP, SIGTERM, SIGINT, -1) >= 0);
+ assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGHUP, SIGTERM, SIGINT, SIGCHLD, -1) >= 0);
r = manager_new(&m);
if (r < 0)
Hashmap *users;
Hashmap *inhibitors;
Hashmap *buttons;
+ Hashmap *brightness_writers;
LIST_HEAD(Seat, seat_gc_queue);
LIST_HEAD(Session, session_gc_queue);
extern const sd_bus_vtable manager_vtable[];
-int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int match_unit_removed(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int match_properties_changed(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int match_reloading(sd_bus_message *message, void *userdata, sd_bus_error *error);
-int match_name_owner_changed(sd_bus_message *message, void *userdata, sd_bus_error *error);
-
-int bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const char *unit_name, InhibitWhat w, sd_bus_error *error);
-
-int manager_send_changed(Manager *manager, const char *property, ...) _sentinel_;
-
-int manager_start_scope(Manager *manager, const char *scope, pid_t pid, const char *slice, const char *description, char **wants, char **after, const char *requires_mounts_for, sd_bus_message *more_properties, sd_bus_error *error, char **job);
-int manager_start_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
-int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job);
-int manager_abandon_scope(Manager *manager, const char *scope, sd_bus_error *error);
-int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error);
-int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *error);
-int manager_job_is_active(Manager *manager, const char *path, sd_bus_error *error);
-
/* gperf lookup function */
const struct ConfigPerfItem* logind_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
CONFIG_PARSER_PROTOTYPE(config_parse_n_autovts);
CONFIG_PARSER_PROTOTYPE(config_parse_tmpfs_size);
-int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Session **ret);
-int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret);
-int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char *name, sd_bus_error *error, Seat **ret);
-
int manager_setup_wall_message_timer(Manager *m);
bool logind_wall_tty_filter(const char *tty, void *userdata);
-
-int manager_dispatch_delayed(Manager *manager, bool timeout);
command : [gperf, '@INPUT@', '--output-file', '@OUTPUT@'])
liblogind_core_sources = files('''
+ logind-acl.h
+ logind-action.c
+ logind-action.h
+ logind-brightness.c
+ logind-brightness.h
+ logind-button.c
+ logind-button.h
logind-core.c
+ logind-dbus.c
+ logind-dbus.h
logind-device.c
logind-device.h
- logind-button.c
- logind-button.h
- logind-action.c
- logind-action.h
+ logind-inhibit.c
+ logind-inhibit.h
+ logind-seat-dbus.c
+ logind-seat-dbus.h
logind-seat.c
logind-seat.h
- logind-session.c
- logind-session.h
+ logind-session-dbus.c
+ logind-session-dbus.h
logind-session-device.c
logind-session-device.h
+ logind-session.c
+ logind-session.h
+ logind-user-dbus.c
+ logind-user-dbus.h
logind-user.c
logind-user.h
- logind-inhibit.c
- logind-inhibit.h
- logind-dbus.c
- logind-session-dbus.c
- logind-seat-dbus.c
- logind-user-dbus.c
logind-utmp.c
- logind-acl.h
'''.split())
liblogind_core_sources += [logind_gperf_c]
user-runtime-dir.c
'''.split())
+pam_systemd_sym = 'src/login/pam_systemd.sym'
+pam_systemd_c = files('pam_systemd.c')
+
if conf.get('ENABLE_LOGIND') == 1
logind_conf = configure_file(
input : 'logind.conf.in',
install_data(logind_conf,
install_dir : pkgsysconfdir)
- pam_systemd_sym = 'src/login/pam_systemd.sym'
- pam_systemd_c = files('pam_systemd.c')
-
install_data('org.freedesktop.login1.conf',
install_dir : dbuspolicydir)
install_data('org.freedesktop.login1.service',
send_interface="org.freedesktop.login1.Session"
send_member="PauseDeviceComplete"/>
+ <allow send_destination="org.freedesktop.login1"
+ send_interface="org.freedesktop.login1.Session"
+ send_member="SetBrightness"/>
+
<allow send_destination="org.freedesktop.login1"
send_interface="org.freedesktop.login1.User"
send_member="Terminate"/>
#include "bus-internal.h"
#include "bus-util.h"
#include "cgroup-util.h"
+#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
if (streq(limit, "infinity")) {
r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", (uint64_t)-1);
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
return r;
}
} else {
if (r >= 0) {
r = sd_bus_message_append(m, "(sv)", "MemoryMaxScale", "u", (uint32_t) (((uint64_t) r * UINT32_MAX) / 1000U));
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
return r;
}
} else {
if (r >= 0) {
r = sd_bus_message_append(m, "(sv)", "MemoryMax", "t", val);
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
return r;
}
} else
- pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.limit: %s, ignoring.", limit);
+ pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.memory_max: %s, ignoring.", limit);
}
}
if (r >= 0) {
r = sd_bus_message_append(m, "(sv)", "TasksMax", "t", val);
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
return r;
}
} else
- pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.limit: %s, ignoring.", limit);
+ pam_syslog(handle, LOG_WARNING, "Failed to parse systemd.tasks_max: %s, ignoring.", limit);
return 0;
}
if (r >= 0) {
r = sd_bus_message_append(m, "(sv)", field, "t", val);
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
return r;
}
} else if (streq(field, "CPUWeight"))
* up properly for us. */
if (lstat(path, &st) < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror(errno));
+ pam_syslog(handle, LOG_ERR, "Failed to stat() runtime directory '%s': %s", path, strerror_safe(errno));
goto fail;
}
r = sd_bus_open_system(&bus);
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
return PAM_SESSION_ERR;
}
"org.freedesktop.login1.Manager",
"CreateSession");
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to create CreateSession method call: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to create CreateSession method call: %s", strerror_safe(r));
return PAM_SESSION_ERR;
}
remote_user,
remote_host);
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to append to bus message: %s", strerror_safe(r));
return PAM_SESSION_ERR;
}
r = sd_bus_message_open_container(m, 'a', "(sv)");
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to open message container: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to open message container: %s", strerror_safe(r));
return PAM_SYSTEM_ERR;
}
r = sd_bus_message_close_container(m);
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to close message container: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to close message container: %s", strerror_safe(r));
return PAM_SYSTEM_ERR;
}
&vtnr,
&existing);
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to parse message: %s", strerror_safe(r));
return PAM_SESSION_ERR;
}
r = sd_bus_open_system(&bus);
if (r < 0) {
- pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror(-r));
+ pam_syslog(handle, LOG_ERR, "Failed to connect to system bus: %s", strerror_safe(r));
return PAM_SESSION_ERR;
}
-/***
- SPDX-License-Identifier: LGPL-2.1+
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-***/
+/* SPDX-License-Identifier: LGPL-2.1+ */
{
global:
if (r == 0)
return 1; /* Will call us back */
- master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
if (master < 0)
return master;
- r = ptsname_namespace(master, &pty_name);
- if (r < 0)
- return r;
-
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
if (r == 0)
return 1; /* Will call us back */
- master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
if (master < 0)
return master;
- r = ptsname_namespace(master, &pty_name);
- if (r < 0)
- return r;
-
p = path_startswith(pty_name, "/dev/pts/");
- if (!p)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "PTS name %s is invalid", pty_name);
+ assert(p);
r = container_bus_new(m, error, &allocated_bus);
if (r < 0)
if (r == 0)
return 1; /* Will call us back */
- master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ master = machine_openpt(m, O_RDWR|O_NOCTTY|O_CLOEXEC, &pty_name);
if (master < 0)
return master;
- r = ptsname_namespace(master, &pty_name);
- if (r < 0)
- return r;
-
p = path_startswith(pty_name, "/dev/pts/");
assert(p);
return manager_kill_unit(m->manager, m->unit, signo, NULL);
}
-int machine_openpt(Machine *m, int flags) {
+int machine_openpt(Machine *m, int flags, char **ret_slave) {
assert(m);
switch (m->class) {
- case MACHINE_HOST: {
- int fd;
-
- fd = posix_openpt(flags);
- if (fd < 0)
- return -errno;
-
- if (unlockpt(fd) < 0)
- return -errno;
+ case MACHINE_HOST:
- return fd;
- }
+ return openpt_allocate(flags, ret_slave);
case MACHINE_CONTAINER:
if (m->leader <= 0)
return -EINVAL;
- return openpt_in_namespace(m->leader, flags);
+ return openpt_allocate_in_namespace(m->leader, flags, ret_slave);
default:
return -EOPNOTSUPP;
const char *kill_who_to_string(KillWho k) _const_;
KillWho kill_who_from_string(const char *s) _pure_;
-int machine_openpt(Machine *m, int flags);
+int machine_openpt(Machine *m, int flags, char **ret_slave);
int machine_open_terminal(Machine *m, const char *path, int mode);
int machine_get_uid_shift(Machine *m, uid_t *ret);
if (!startswith(de->d_name, "loop"))
continue;
- sys = strjoin("/sys/devices/virtual/block/", de->d_name, "/loop/backing_file");
+ sys = path_join("/sys/devices/virtual/block", de->d_name, "loop/backing_file");
if (!sys)
return -ENOMEM;
if (files_same(fname, backing_file, 0) <= 0)
continue;
- l = strjoin("/dev/", de->d_name);
+ l = path_join("/dev", de->d_name);
if (!l)
return -ENOMEM;
if (!filename_is_valid(escaped))
return 0;
- arg_mount_where = strjoin("/run/media/system/", escaped);
+ arg_mount_where = path_join("/run/media/system", escaped);
} else
arg_mount_where = strdup(v);
return -EINVAL;
}
- arg_mount_where = strjoin("/run/media/system/", escaped);
+ arg_mount_where = path_join("/run/media/system", escaped);
if (!arg_mount_where)
return log_oom();
if (arg_action == ACTION_UMOUNT)
return action_umount(bus, argc, argv);
- if (!path_is_normalized(arg_mount_what)) {
+ if ((!arg_mount_type || !fstype_is_network(arg_mount_type))
+ && !path_is_normalized(arg_mount_what)) {
log_error("Path contains non-normalized components: %s", arg_mount_what);
return -EINVAL;
}
netdev/l2tp-tunnel.h
netdev/macsec.c
netdev/macsec.h
+ netdev/xfrm.c
+ netdev/xfrm.h
networkd-address-label.c
networkd-address-label.h
networkd-address-pool.c
networkd-can.h
networkd-conf.c
networkd-conf.h
+ networkd-dhcp-common.c
+ networkd-dhcp-common.h
+ networkd-dhcp-server.c
+ networkd-dhcp-server.h
networkd-dhcp4.c
+ networkd-dhcp4.h
networkd-dhcp6.c
+ networkd-dhcp6.h
networkd-fdb.c
networkd-fdb.h
networkd-ipv4ll.c
+ networkd-ipv4ll.h
networkd-ipv6-proxy-ndp.c
networkd-ipv6-proxy-ndp.h
networkd-link-bus.c
+ networkd-link-bus.h
networkd-link.c
networkd-link.h
networkd-lldp-rx.c
networkd-radv.c
networkd-radv.h
networkd-network-bus.c
+ networkd-network-bus.h
networkd-network.c
networkd-network.h
networkd-route.c
[]],
[['src/network/test-network-tables.c',
- 'src/network/test-network-tables.c',
test_tables_h],
[libnetworkd_core,
libudev_static,
_cleanup_(macsec_transmit_association_free_or_set_invalidp) TransmitAssociation *a = NULL;
_cleanup_(macsec_receive_association_free_or_set_invalidp) ReceiveAssociation *b = NULL;
- _cleanup_free_ void *p;
+ _cleanup_(erase_and_freep) void *p = NULL;
MACsec *s = userdata;
SecurityAssociation *dest;
size_t l;
r = unhexmem_full(rvalue, strlen(rvalue), true, &p, &l);
if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse key. Ignoring assignment: %m");
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse key. Ignoring assignment: %m");
return 0;
}
+
if (l != 16) {
/* See DEFAULT_SAK_LEN in drivers/net/macsec.c */
- explicit_bzero_safe(p, l);
- log_syntax(unit, LOG_ERR, filename, line, 0,
- "Invalid key length (%zu). Ignoring assignment", l);
+ log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid key length (%zu). Ignoring assignment", l);
return 0;
}
+ explicit_bzero_safe(dest->key, dest->key_len);
free_and_replace(dest->key, p);
dest->key_len = l;
}
static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) {
- _cleanup_free_ uint8_t *key = NULL;
+ _cleanup_(erase_and_freep) uint8_t *key = NULL;
size_t key_len;
int r;
return log_netdev_error_errno(netdev, r,
"Failed to read key from '%s', ignoring: %m",
sa->key_file);
- if (key_len != 16) {
- explicit_bzero_safe(key, key_len);
+
+ if (key_len != 16)
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
- "Invalid key length (%zu bytes), ignoring: %m",
- key_len);
- }
+ "Invalid key length (%zu bytes), ignoring: %m", key_len);
explicit_bzero_safe(sa->key, sa->key_len);
free_and_replace(sa->key, key);
#include "netdev/wireguard.h"
#include "netdev/fou-tunnel.h"
#include "netdev/l2tp-tunnel.h"
+#include "netdev/xfrm.h"
#include "vlan-util.h"
%}
struct ConfigPerfItem;
Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp)
Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit)
Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent)
+Tunnel.AssignToLoopback, config_parse_bool, 0, offsetof(Tunnel, assign_to_loopback)
Tunnel.AllowLocalRemote, config_parse_tristate, 0, offsetof(Tunnel, allow_localremote)
Tunnel.FooOverUDP, config_parse_bool, 0, offsetof(Tunnel, fou_tunnel)
Tunnel.FOUDestinationPort, config_parse_ip_port, 0, offsetof(Tunnel, fou_destination_port)
WireGuardPeer.PresharedKey, config_parse_wireguard_preshared_key, 0, 0
WireGuardPeer.PresharedKeyFile, config_parse_wireguard_preshared_key_file, 0, 0
WireGuardPeer.PersistentKeepalive, config_parse_wireguard_keepalive, 0, 0
+Xfrm.InterfaceId, config_parse_uint32, 0, offsetof(Xfrm, if_id)
+Xfrm.Independent, config_parse_bool, 0, offsetof(Xfrm, independent)
#include "netdev/vxcan.h"
#include "netdev/vxlan.h"
#include "netdev/wireguard.h"
+#include "netdev/xfrm.h"
#include "netlink-util.h"
#include "network-internal.h"
#include "networkd-link.h"
[NETDEV_KIND_L2TP] = &l2tptnl_vtable,
[NETDEV_KIND_MACSEC] = &macsec_vtable,
[NETDEV_KIND_NLMON] = &nlmon_vtable,
+ [NETDEV_KIND_XFRM] = &xfrm_vtable,
};
static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = {
[NETDEV_KIND_L2TP] = "l2tp",
[NETDEV_KIND_MACSEC] = "macsec",
[NETDEV_KIND_NLMON] = "nlmon",
+ [NETDEV_KIND_XFRM] = "xfrm",
};
DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
case NETDEV_KIND_ERSPAN:
independent = ERSPAN(netdev)->independent;
break;
+ case NETDEV_KIND_XFRM:
+ independent = XFRM(netdev)->independent;
+ break;
default:
break;
}
NETDEV_KIND_L2TP,
NETDEV_KIND_MACSEC,
NETDEV_KIND_NLMON,
+ NETDEV_KIND_XFRM,
_NETDEV_KIND_MAX,
_NETDEV_KIND_TUNNEL, /* Used by config_parse_stacked_netdev() */
_NETDEV_KIND_INVALID = -1
#define log_netdev_full(netdev, level, error, ...) \
({ \
const NetDev *_n = (netdev); \
- _n ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _n->ifname, NULL, NULL, ##__VA_ARGS__) : \
- log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
+ _n ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "INTERFACE=", _n->ifname, NULL, NULL, ##__VA_ARGS__) : \
+ log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
})
#define log_netdev_debug(netdev, ...) log_netdev_full(netdev, LOG_DEBUG, 0, ##__VA_ARGS__)
assert(m);
assert(t);
- assert(t->family == AF_INET);
- if (link) {
- r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
+ if (link || t->assign_to_loopback) {
+ r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
}
}
assert(t);
- assert(t->family == AF_INET);
- if (link) {
- r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link->ifindex);
+ if (link || t->assign_to_loopback) {
+ r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m");
}
t = IP6GRETAP(netdev);
assert(t);
- assert(t->family == AF_INET6);
assert(m);
- if (link) {
- r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link->ifindex);
+ if (link || t->assign_to_loopback) {
+ r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m");
}
t = VTI6(netdev);
assert(t);
- assert((netdev->kind == NETDEV_KIND_VTI && t->family == AF_INET) ||
- (netdev->kind == NETDEV_KIND_VTI6 && t->family == AF_INET6));
- if (link) {
- r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link->ifindex);
+ if (link || t->assign_to_loopback) {
+ r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_LINK attribute: %m");
}
assert(netdev);
assert(m);
assert(t);
- assert(t->family == AF_INET6);
- if (link) {
- r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex);
+ if (link || t->assign_to_loopback) {
+ r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
if (r < 0)
return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m");
}
assert(t);
- if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_SIT, NETDEV_KIND_GRE, NETDEV_KIND_GRETAP) &&
- t->family != AF_INET)
+ if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_SIT, NETDEV_KIND_GRE) &&
+ !IN_SET(t->family, AF_UNSPEC, AF_INET))
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"vti/ipip/sit/gre tunnel without a local/remote IPv4 address configured in %s. Ignoring", filename);
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"gretap/erspan tunnel without a remote IPv4 address configured in %s. Ignoring", filename);
- if (IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL, NETDEV_KIND_IP6GRE, NETDEV_KIND_IP6GRETAP) &&
- t->family != AF_INET6)
+ if ((IN_SET(netdev->kind, NETDEV_KIND_VTI6, NETDEV_KIND_IP6TNL) && t->family != AF_INET6) ||
+ (netdev->kind == NETDEV_KIND_IP6GRE && !IN_SET(t->family, AF_UNSPEC, AF_INET6)))
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
"vti6/ip6tnl/ip6gre tunnel without a local/remote IPv6 address configured in %s. Ignoring", filename);
if (netdev->kind == NETDEV_KIND_ERSPAN && (t->erspan_index >= (1 << 20) || t->erspan_index == 0))
return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "Invalid erspan index %d. Ignoring", t->erspan_index);
+ /* netlink_message_append_in_addr_union() is used for vti/vti6. So, t->family cannot be AF_UNSPEC. */
+ if (netdev->kind == NETDEV_KIND_VTI)
+ t->family = AF_INET;
+
return 0;
}
bool copy_dscp;
bool independent;
bool fou_tunnel;
+ bool assign_to_loopback;
uint16_t encap_src_port;
uint16_t fou_destination_port;
unsigned line,
const char *lvalue) {
- _cleanup_free_ void *key = NULL;
+ _cleanup_(erase_and_freep) void *key = NULL;
size_t len;
int r;
if (r < 0)
return log_syntax(unit, LOG_ERR, filename, line, r,
"Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue);
- if (len != WG_KEY_LEN) {
- explicit_bzero_safe(key, len);
+ if (len != WG_KEY_LEN)
return log_syntax(unit, LOG_ERR, filename, line, 0,
"Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.",
lvalue, len);
- }
memcpy(ret, key, WG_KEY_LEN);
return 0;
}
static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) {
- _cleanup_free_ char *key = NULL;
+ _cleanup_(erase_and_freep) char *key = NULL;
size_t key_len;
int r;
if (r < 0)
return r;
- if (key_len != WG_KEY_LEN) {
- r = -EINVAL;
- goto finalize;
- }
+ if (key_len != WG_KEY_LEN)
+ return -EINVAL;
memcpy(dest, key, WG_KEY_LEN);
- r = 0;
-
-finalize:
- explicit_bzero_safe(key, key_len);
- return r;
+ return 0;
}
static int wireguard_peer_verify(WireguardPeer *peer) {
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "missing_network.h"
+#include "netdev/xfrm.h"
+
+static int xfrm_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *message) {
+ Xfrm *x;
+ int r;
+
+ assert(netdev);
+ assert(message);
+
+ x = XFRM(netdev);
+
+ assert(link || x->independent);
+
+ r = sd_netlink_message_append_u32(message, IFLA_XFRM_LINK, link ? link->ifindex : LOOPBACK_IFINDEX);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_XFRM_LINK: %m");
+
+ r = sd_netlink_message_append_u32(message, IFLA_XFRM_IF_ID, x->if_id);
+ if (r < 0)
+ return log_netdev_error_errno(netdev, r, "Could not append IFLA_XFRM_IF_ID: %m");
+
+ return 0;
+}
+
+const NetDevVTable xfrm_vtable = {
+ .object_size = sizeof(Xfrm),
+ .sections = "Match\0NetDev\0Xfrm\0",
+ .fill_message_create = xfrm_fill_message_create,
+ .create_type = NETDEV_CREATE_STACKED
+};
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "netdev/netdev.h"
+
+typedef struct Xfrm {
+ NetDev meta;
+
+ uint32_t if_id;
+ bool independent;
+} Xfrm;
+
+DEFINE_NETDEV_CAST(XFRM, Xfrm);
+extern const NetDevVTable xfrm_vtable;
#include "bus-util.h"
#include "device-util.h"
#include "ether-addr-util.h"
+#include "ethtool-util.h"
#include "fd-util.h"
#include "format-table.h"
#include "format-util.h"
struct rtnl_link_stats stats;
};
- double tx_bitrate;
- double rx_bitrate;
+ uint64_t tx_bitrate;
+ uint64_t rx_bitrate;
+
+ /* ethtool info */
+ int autonegotiation;
+ size_t speed;
+ Duplex duplex;
+ NetDevPort port;
bool has_mac_address:1;
bool has_tx_queues:1;
bool has_stats64:1;
bool has_stats:1;
bool has_bitrates:1;
+ bool has_ethtool_link_info:1;
} LinkInfo;
static int link_info_compare(const LinkInfo *a, const LinkInfo *b) {
r, "Failed to query link bit rates: %s", bus_error_message(&error, r));
}
- r = sd_bus_message_enter_container(reply, 'v', "(dd)");
+ r = sd_bus_message_enter_container(reply, 'v', "(tt)");
if (r < 0)
return bus_log_parse_error(r);
- r = sd_bus_message_read(reply, "(dd)", &link->tx_bitrate, &link->rx_bitrate);
+ r = sd_bus_message_read(reply, "(tt)", &link->tx_bitrate, &link->rx_bitrate);
if (r < 0)
return bus_log_parse_error(r);
if (r < 0)
return bus_log_parse_error(r);
- link->has_bitrates = link->tx_bitrate >= 0 && link->rx_bitrate >= 0;
+ link->has_bitrates = true;
return 0;
}
static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, LinkInfo **ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
_cleanup_free_ LinkInfo *links = NULL;
+ _cleanup_close_ int fd = -1;
size_t allocated = 0, c = 0, j;
sd_netlink_message *i;
int r;
return log_error_errno(r, "Failed to enumerate links: %m");
for (i = reply; i; i = sd_netlink_message_next(i)) {
- if (!GREEDY_REALLOC(links, allocated, c+1))
+ if (!GREEDY_REALLOC0(links, allocated, c+1))
return -ENOMEM;
r = decode_link(i, links + c, patterns);
if (r < 0)
return r;
- if (r > 0)
- c++;
+ if (r == 0)
+ continue;
+
+ r = ethtool_get_link_info(&fd, links[c].name,
+ &links[c].autonegotiation, &links[c].speed,
+ &links[c].duplex, &links[c].port);
+ if (r >= 0)
+ links[c].has_ethtool_link_info = true;
+
+ c++;
}
typesafe_qsort(links, c, link_info_compare);
return 0;
}
-static const struct {
- double val;
- const char *str;
-} prefix_table[] = {
- { .val = 1e15, .str = "P" },
- { .val = 1e12, .str = "T" },
- { .val = 1e9, .str = "G" },
- { .val = 1e6, .str = "M" },
- { .val = 1e3, .str = "k" },
-};
-
-static void get_prefix(double val, double *ret_div, const char **ret_prefix) {
- assert(ret_div);
- assert(ret_prefix);
-
- for (size_t i = 0; i < ELEMENTSOF(prefix_table); i++)
- if (val > prefix_table[i].val) {
- *ret_div = prefix_table[i].val;
- *ret_prefix = prefix_table[i].str;
- return;
- }
-
- *ret_div = 1;
- *ret_prefix = NULL;
-}
-
static int link_status_one(
sd_netlink *rtnl,
sd_hwdb *hwdb,
if (r < 0)
return r;
r = table_add_cell_stringf(table, NULL, "%s%s%s (%s%s%s)",
- on_color_operational, strna(operational_state), off_color_operational,
- on_color_setup, strna(setup_state), off_color_setup);
+ on_color_operational, strna(operational_state), off_color_operational,
+ on_color_setup, strna(setup_state), off_color_setup);
if (r < 0)
return r;
if (r < 0)
return r;
r = table_add_cell_stringf(table, NULL, "%s%s%s%s",
- ether_addr_to_string(&info->mac_address, ea),
- description ? " (" : "",
- description,
- description ? ")" : "");
+ ether_addr_to_string(&info->mac_address, ea),
+ description ? " (" : "",
+ strempty(description),
+ description ? ")" : "");
if (r < 0)
return r;
}
}
if (info->has_bitrates) {
- const char *tx_prefix, *rx_prefix;
- double tx_div, rx_div;
-
- get_prefix(info->tx_bitrate, &tx_div, &tx_prefix);
- get_prefix(info->rx_bitrate, &rx_div, &rx_prefix);
+ char tx[FORMAT_BYTES_MAX], rx[FORMAT_BYTES_MAX];
r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
if (r < 0)
if (r < 0)
return r;
- r = table_add_cell_stringf(table, NULL, "%.4g %sbps/%.4g %sbps",
- info->tx_bitrate / tx_div, strempty(tx_prefix),
- info->rx_bitrate / rx_div, strempty(rx_prefix));
+ r = table_add_cell_stringf(table, NULL, "%sbps/%sbps",
+ format_bytes_full(tx, sizeof tx, info->tx_bitrate, 0),
+ format_bytes_full(rx, sizeof rx, info->rx_bitrate, 0));
if (r < 0)
return r;
}
return r;
}
+ if (info->has_ethtool_link_info) {
+ const char *duplex = duplex_to_string(info->duplex);
+ const char *port = port_to_string(info->port);
+
+ if (IN_SET(info->autonegotiation, AUTONEG_DISABLE, AUTONEG_ENABLE)) {
+ r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+ if (r < 0)
+ return r;
+ r = table_add_cell(table, NULL, TABLE_STRING, "Auto negotiation:");
+ if (r < 0)
+ return r;
+ r = table_add_cell(table, NULL, TABLE_BOOLEAN, &info->autonegotiation);
+ if (r < 0)
+ return r;
+ }
+
+ if (info->speed > 0) {
+ r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+ if (r < 0)
+ return r;
+ r = table_add_cell(table, NULL, TABLE_STRING, "Speed:");
+ if (r < 0)
+ return r;
+ r = table_add_cell(table, NULL, TABLE_BPS, &info->speed);
+ if (r < 0)
+ return r;
+ }
+
+ if (duplex) {
+ r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+ if (r < 0)
+ return r;
+ r = table_add_cell(table, NULL, TABLE_STRING, "Duplex:");
+ if (r < 0)
+ return r;
+ r = table_add_cell(table, NULL, TABLE_STRING, duplex);
+ if (r < 0)
+ return r;
+ }
+
+ if (port) {
+ r = table_add_cell(table, NULL, TABLE_EMPTY, NULL);
+ if (r < 0)
+ return r;
+ r = table_add_cell(table, NULL, TABLE_STRING, "Port:");
+ if (r < 0)
+ return r;
+ r = table_add_cell(table, NULL, TABLE_STRING, port);
+ if (r < 0)
+ return r;
+ }
+ }
+
r = dump_addresses(rtnl, table, info->ifindex);
if (r < 0)
return r;
assert(link->manager->rtnl);
assert(callback);
+ if (address->family == AF_INET6 && link_sysctl_ipv6_enabled(link) == 0) {
+ log_link_warning(link, "An IPv6 address is requested, but IPv6 is disabled by sysctl, ignoring.");
+ return 0;
+ }
+
/* 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 &&
set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX)
return log_link_error_errno(link, r, "Could not add address: %m");
}
- return 0;
+ return 1;
}
int config_parse_broadcast(
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 | htonl(0xfffffffflu >> n->prefixlen);
+ n->broadcast.s_addr = n->in_addr.in.s_addr | htobe32(0xfffffffflu >> n->prefixlen);
n = NULL;
static bool is_bit_set(unsigned bit, uint32_t scope) {
assert(bit < sizeof(scope)*8);
- return scope & (1 << bit);
+ return scope & (UINT32_C(1) << bit);
}
static void set_bit(unsigned nr, uint32_t *addr) {
if (nr < BRIDGE_VLAN_BITMAP_MAX)
- addr[nr / 32] |= (((uint32_t) 1) << (nr % 32));
+ addr[nr / 32] |= (UINT32_C(1) << (nr % 32));
}
static int find_next_bit(int i, uint32_t x) {
static int append_vlan_info_data(Link *const link, sd_netlink_message *req, uint16_t pvid, const uint32_t *br_vid_bitmap, const uint32_t *br_untagged_bitmap) {
struct bridge_vlan_info br_vlan;
- int i, j, k, r, done, cnt;
+ int i, j, k, r, cnt;
uint16_t begin, end;
- bool untagged = false;
+ bool done, untagged = false;
assert(link);
assert(req);
assert(br_vid_bitmap);
assert(br_untagged_bitmap);
- i = cnt = -1;
+ cnt = 0;
begin = end = UINT16_MAX;
for (k = 0; k < BRIDGE_VLAN_BITMAP_LEN; k++) {
base_bit = k * 32;
i = -1;
- done = 0;
+ done = false;
do {
j = find_next_bit(i, vid_map);
if (j > 0) {
goto next;
}
} else
- done = 1;
+ done = true;
if (begin != UINT16_MAX) {
cnt++;
i = j;
} while (!done);
}
- if (!cnt)
- return -EINVAL;
+ assert(cnt > 0);
return cnt;
}
int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
- int r;
- uint16_t flags;
sd_netlink *rtnl;
+ uint16_t flags;
+ int r;
assert(link);
assert(link->manager);
return 0;
}
-static int parse_vid_range(const char *rvalue, uint16_t *vid, uint16_t *vid_end) {
- int r;
- char *p;
- char *_rvalue = NULL;
- uint16_t _vid = UINT16_MAX;
- uint16_t _vid_end = UINT16_MAX;
-
- assert(rvalue);
- assert(vid);
- assert(vid_end);
-
- _rvalue = strdupa(rvalue);
- p = strchr(_rvalue, '-');
- if (p) {
- *p = '\0';
- p++;
- r = parse_vlanid(_rvalue, &_vid);
- if (r < 0)
- return r;
-
- if (_vid == 0)
- return -ERANGE;
-
- r = parse_vlanid(p, &_vid_end);
- if (r < 0)
- return r;
-
- if (_vid_end == 0)
- return -ERANGE;
- } else {
- r = parse_vlanid(_rvalue, &_vid);
- if (r < 0)
- return r;
-
- if (_vid == 0)
- return -ERANGE;
- }
-
- *vid = _vid;
- *vid_end = _vid_end;
- return r;
-}
-
int config_parse_brvlan_pvid(const char *unit, const char *filename,
unsigned line, const char *section,
unsigned section_line, const char *lvalue,
int ltype, const char *rvalue, void *data,
void *userdata) {
Network *network = userdata;
- int r;
uint16_t pvid;
+ int r;
+
r = parse_vlanid(rvalue, &pvid);
if (r < 0)
return r;
int ltype, const char *rvalue, void *data,
void *userdata) {
Network *network = userdata;
- int r;
uint16_t vid, vid_end;
+ int r;
assert(filename);
assert(section);
return 0;
}
- if (UINT16_MAX == vid_end)
- set_bit(vid++, network->br_vid_bitmap);
- else {
- if (vid >= vid_end) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid VLAN range, ignoring %s", rvalue);
- return 0;
- }
- for (; vid <= vid_end; vid++)
- set_bit(vid, network->br_vid_bitmap);
- }
+ for (; vid <= vid_end; vid++)
+ set_bit(vid, network->br_vid_bitmap);
+
network->use_br_vlan = true;
return 0;
}
return 0;
}
- if (UINT16_MAX == vid_end) {
+ for (; vid <= vid_end; vid++) {
set_bit(vid, network->br_vid_bitmap);
set_bit(vid, network->br_untagged_bitmap);
- } else {
- if (vid >= vid_end) {
- log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid VLAN range, ignoring %s", rvalue);
- return 0;
- }
- for (; vid <= vid_end; vid++) {
- set_bit(vid, network->br_vid_bitmap);
- set_bit(vid, network->br_untagged_bitmap);
- }
}
+
network->use_br_vlan = true;
return 0;
}
#include "conf-parser.h"
+#define BRIDGE_VLAN_BITMAP_MAX 4096
+#define BRIDGE_VLAN_BITMAP_LEN (BRIDGE_VLAN_BITMAP_MAX / 32)
+
typedef struct Link Link;
int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32_t *br_untagged_bitmap);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "networkd-dhcp-common.h"
+#include "networkd-network.h"
+#include "parse-util.h"
+#include "string-table.h"
+
+int config_parse_dhcp(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ AddressFamilyBoolean *dhcp = data, s;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ /* Note that this is mostly like
+ * config_parse_address_family_boolean(), except that it
+ * understands some old names for the enum values */
+
+ s = address_family_boolean_from_string(rvalue);
+ if (s < 0) {
+
+ /* Previously, we had a slightly different enum here,
+ * support its values for compatibility. */
+
+ if (streq(rvalue, "none"))
+ s = ADDRESS_FAMILY_NO;
+ else if (streq(rvalue, "v4"))
+ s = ADDRESS_FAMILY_IPV4;
+ else if (streq(rvalue, "v6"))
+ s = ADDRESS_FAMILY_IPV6;
+ else if (streq(rvalue, "both"))
+ s = ADDRESS_FAMILY_YES;
+ else {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Failed to parse DHCP option, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "DHCP=%s is deprecated, please use DHCP=%s instead.",
+ rvalue, address_family_boolean_to_string(s));
+ }
+
+ *dhcp = s;
+ return 0;
+}
+
+int config_parse_dhcp_use_dns(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse UseDNS=%s, ignoring assignment: %m", rvalue);
+ return 0;
+ }
+
+ network->dhcp_use_dns = r;
+ network->dhcp6_use_dns = r;
+
+ return 0;
+}
+
+int config_parse_dhcp_use_ntp(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse UseNTP=%s, ignoring assignment: %m", rvalue);
+ return 0;
+ }
+
+ network->dhcp_use_ntp = r;
+ network->dhcp6_use_ntp = r;
+
+ return 0;
+}
+
+int config_parse_section_route_table(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = data;
+ uint32_t rt;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = safe_atou32(rvalue, &rt);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
+ return 0;
+ }
+
+ if (streq_ptr(section, "DHCP")) {
+ network->dhcp_route_table = rt;
+ network->dhcp_route_table_set = true;
+ } else { /* section is IPv6AcceptRA */
+ network->ipv6_accept_ra_route_table = rt;
+ network->ipv6_accept_ra_route_table_set = true;
+ }
+
+ return 0;
+}
+
+int config_parse_iaid(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Network *network = data;
+ uint32_t iaid;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(network);
+
+ r = safe_atou32(rvalue, &iaid);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Unable to read IAID, ignoring assignment: %s", rvalue);
+ return 0;
+ }
+
+ network->iaid = iaid;
+ network->iaid_set = true;
+
+ return 0;
+}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains,
+ "Failed to parse DHCP use domains setting");
+
+static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
+ [DHCP_USE_DOMAINS_NO] = "no",
+ [DHCP_USE_DOMAINS_ROUTE] = "route",
+ [DHCP_USE_DOMAINS_YES] = "yes",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "conf-parser.h"
+#include "dhcp-identifier.h"
+#include "time-util.h"
+
+#define DHCP_ROUTE_METRIC 1024
+
+typedef enum DHCPUseDomains {
+ DHCP_USE_DOMAINS_NO,
+ DHCP_USE_DOMAINS_YES,
+ DHCP_USE_DOMAINS_ROUTE,
+ _DHCP_USE_DOMAINS_MAX,
+ _DHCP_USE_DOMAINS_INVALID = -1,
+} DHCPUseDomains;
+
+typedef struct DUID {
+ /* Value of Type in [DHCP] section */
+ DUIDType type;
+
+ uint8_t raw_data_len;
+ uint8_t raw_data[MAX_DUID_LEN];
+ usec_t llt_time;
+} DUID;
+
+const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_;
+DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_;
+
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_dns);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_ntp);
+CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
+CONFIG_PARSER_PROTOTYPE(config_parse_section_route_table);
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "sd-dhcp-server.h"
+
+#include "networkd-dhcp-server.h"
+#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "networkd-network.h"
+#include "strv.h"
+
+static Address* link_find_dhcp_server_address(Link *link) {
+ Address *address;
+
+ assert(link);
+ assert(link->network);
+
+ /* The first statically configured address if there is any */
+ LIST_FOREACH(addresses, address, link->network->static_addresses) {
+
+ if (address->family != AF_INET)
+ continue;
+
+ if (in_addr_is_null(address->family, &address->in_addr))
+ continue;
+
+ return address;
+ }
+
+ /* If that didn't work, find a suitable address we got from the pool */
+ LIST_FOREACH(addresses, address, link->pool_addresses) {
+ if (address->family != AF_INET)
+ continue;
+
+ return address;
+ }
+
+ return NULL;
+}
+
+static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
+ _cleanup_free_ struct in_addr *addresses = NULL;
+ size_t n_addresses = 0, n_allocated = 0;
+ unsigned i;
+
+ log_debug("Copying DNS server information from %s", link->ifname);
+
+ if (!link->network)
+ return 0;
+
+ for (i = 0; i < link->network->n_dns; i++) {
+ struct in_addr ia;
+
+ /* Only look for IPv4 addresses */
+ if (link->network->dns[i].family != AF_INET)
+ continue;
+
+ ia = link->network->dns[i].address.in;
+
+ /* Never propagate obviously borked data */
+ if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
+ continue;
+
+ if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
+ return log_oom();
+
+ addresses[n_addresses++] = ia;
+ }
+
+ if (link->network->dhcp_use_dns && link->dhcp_lease) {
+ const struct in_addr *da = NULL;
+ int j, n;
+
+ n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
+ if (n > 0) {
+
+ if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
+ return log_oom();
+
+ for (j = 0; j < n; j++)
+ if (in4_addr_is_non_local(&da[j]))
+ addresses[n_addresses++] = da[j];
+ }
+ }
+
+ if (n_addresses <= 0)
+ return 0;
+
+ return sd_dhcp_server_set_dns(s, addresses, n_addresses);
+}
+
+static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
+ _cleanup_free_ struct in_addr *addresses = NULL;
+ size_t n_addresses = 0, n_allocated = 0;
+ char **a;
+
+ if (!link->network)
+ return 0;
+
+ log_debug("Copying NTP server information from %s", link->ifname);
+
+ STRV_FOREACH(a, link->network->ntp) {
+ union in_addr_union ia;
+
+ /* Only look for IPv4 addresses */
+ if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
+ continue;
+
+ /* Never propagate obviously borked data */
+ if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
+ continue;
+
+ if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
+ return log_oom();
+
+ addresses[n_addresses++] = ia.in;
+ }
+
+ if (link->network->dhcp_use_ntp && link->dhcp_lease) {
+ const struct in_addr *da = NULL;
+ int j, n;
+
+ n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da);
+ if (n > 0) {
+
+ if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
+ return log_oom();
+
+ for (j = 0; j < n; j++)
+ if (in4_addr_is_non_local(&da[j]))
+ addresses[n_addresses++] = da[j];
+ }
+ }
+
+ if (n_addresses <= 0)
+ return 0;
+
+ return sd_dhcp_server_set_ntp(s, addresses, n_addresses);
+}
+
+int dhcp4_server_configure(Link *link) {
+ Address *address;
+ Link *uplink = NULL;
+ bool acquired_uplink = false;
+ int r;
+
+ address = link_find_dhcp_server_address(link);
+ if (!address)
+ return log_link_warning_errno(link, SYNTHETIC_ERRNO(EBUSY),
+ "Failed to find suitable address for DHCPv4 server instance.");
+
+ /* use the server address' subnet as the pool */
+ r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
+ link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size);
+ if (r < 0)
+ return r;
+
+ /* TODO:
+ r = sd_dhcp_server_set_router(link->dhcp_server, &main_address->in_addr.in);
+ if (r < 0)
+ return r;
+ */
+
+ if (link->network->dhcp_server_max_lease_time_usec > 0) {
+ r = sd_dhcp_server_set_max_lease_time(link->dhcp_server,
+ DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC));
+ if (r < 0)
+ return r;
+ }
+
+ if (link->network->dhcp_server_default_lease_time_usec > 0) {
+ r = sd_dhcp_server_set_default_lease_time(link->dhcp_server,
+ DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC));
+ if (r < 0)
+ return r;
+ }
+
+ if (link->network->dhcp_server_emit_dns) {
+ if (link->network->n_dhcp_server_dns > 0)
+ r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns);
+ else {
+ uplink = manager_find_uplink(link->manager, link);
+ acquired_uplink = true;
+
+ if (!uplink) {
+ log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink.");
+ r = 0;
+ } else
+ r = link_push_uplink_dns_to_dhcp_server(uplink, link->dhcp_server);
+ }
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m");
+ }
+
+ if (link->network->dhcp_server_emit_ntp) {
+ if (link->network->n_dhcp_server_ntp > 0)
+ r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp);
+ else {
+ if (!acquired_uplink)
+ uplink = manager_find_uplink(link->manager, link);
+
+ if (!uplink) {
+ log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink.");
+ r = 0;
+ } else
+ r = link_push_uplink_ntp_to_dhcp_server(uplink, link->dhcp_server);
+
+ }
+ if (r < 0)
+ log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m");
+ }
+
+ r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m");
+
+ if (link->network->dhcp_server_emit_timezone) {
+ _cleanup_free_ char *buffer = NULL;
+ const char *tz;
+
+ if (link->network->dhcp_server_timezone)
+ tz = link->network->dhcp_server_timezone;
+ else {
+ r = get_timezone(&buffer);
+ if (r < 0)
+ return log_warning_errno(r, "Failed to determine timezone: %m");
+
+ tz = buffer;
+ }
+
+ r = sd_dhcp_server_set_timezone(link->dhcp_server, tz);
+ if (r < 0)
+ return r;
+ }
+ if (!sd_dhcp_server_is_running(link->dhcp_server)) {
+ r = sd_dhcp_server_start(link->dhcp_server);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not start DHCPv4 server instance: %m");
+ }
+
+ return 0;
+}
+
+int config_parse_dhcp_server_dns(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *n = data;
+ const char *p = rvalue;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ for (;;) {
+ _cleanup_free_ char *w = NULL;
+ union in_addr_union a;
+ struct in_addr *m;
+
+ r = extract_first_word(&p, &w, NULL, 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to extract word, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ break;
+
+ r = in_addr_from_string(AF_INET, w, &a);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse DNS server address '%s', ignoring assignment: %m", w);
+ continue;
+ }
+
+ m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr));
+ if (!m)
+ return log_oom();
+
+ m[n->n_dhcp_server_dns++] = a.in;
+ n->dhcp_server_dns = m;
+ }
+
+ return 0;
+}
+
+int config_parse_dhcp_server_ntp(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *n = data;
+ const char *p = rvalue;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ for (;;) {
+ _cleanup_free_ char *w = NULL;
+ union in_addr_union a;
+ struct in_addr *m;
+
+ r = extract_first_word(&p, &w, NULL, 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to extract word, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ return 0;
+
+ r = in_addr_from_string(AF_INET, w, &a);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse NTP server address '%s', ignoring: %m", w);
+ continue;
+ }
+
+ m = reallocarray(n->dhcp_server_ntp, n->n_dhcp_server_ntp + 1, sizeof(struct in_addr));
+ if (!m)
+ return log_oom();
+
+ m[n->n_dhcp_server_ntp++] = a.in;
+ n->dhcp_server_ntp = m;
+ }
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "conf-parser.h"
+
+typedef struct Link Link;
+
+int dhcp4_server_configure(Link *link);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_dns);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_ntp);
#include "hostname-util.h"
#include "parse-util.h"
#include "network-internal.h"
+#include "networkd-dhcp4.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-network.h"
+#include "string-table.h"
#include "string-util.h"
#include "sysctl-util.h"
+static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, sd_dhcp_lease *new_lease, struct in_addr *address);
+static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, struct in_addr *address);
+static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, struct in_addr *address);
+
+void dhcp4_release_old_lease(Link *link) {
+ union in_addr_union address = IN_ADDR_NULL, address_old = IN_ADDR_NULL;
+
+ assert(link);
+
+ if (!link->dhcp_lease_old)
+ return;
+
+ assert(link->dhcp_lease);
+
+ (void) sd_dhcp_lease_get_address(link->dhcp_lease_old, &address_old.in);
+ (void) sd_dhcp_lease_get_address(link->dhcp_lease, &address.in);
+
+ (void) dhcp_remove_routes(link, link->dhcp_lease_old, link->dhcp_lease, &address_old.in);
+
+ if (!in_addr_equal(AF_INET, &address_old, &address)) {
+ (void) dhcp_remove_router(link, link->dhcp_lease_old, &address_old.in);
+ (void) dhcp_remove_address(link, link->dhcp_lease_old, &address_old.in);
+ }
+
+ link->dhcp_lease_old = sd_dhcp_lease_unref(link->dhcp_lease_old);
+ link_dirty(link);
+}
+
static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
int r;
link->dhcp4_messages--;
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set DHCPv4 route: %m");
link_enter_failed(link);
+ return 1;
}
if (link->dhcp4_messages == 0) {
link->dhcp4_configured = true;
+ /* New address and routes are configured now. Let's release old lease. */
+ dhcp4_release_old_lease(link);
link_check_ready(link);
}
route->priority = link->network->dhcp_route_metric;
route->table = table;
route->scope = route_scope_from_address(route, &address);
+ if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
+ route->prefsrc.in = address;
r = route_configure(route, link, dhcp4_route_handler);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not set host route: %m");
+ return log_link_error_errno(link, r, "Could not set host route: %m");
link->dhcp4_messages++;
}
r = route_configure(route_gw, link, dhcp4_route_handler);
if (r < 0)
- return log_link_warning_errno(link, r, "Could not set host route: %m");
+ return log_link_error_errno(link, r, "Could not set host route: %m");
link->dhcp4_messages++;
route->table = table;
r = route_configure(route, link, dhcp4_route_handler);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not set routes: %m");
- link_enter_failed(link);
- return r;
- }
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not set routes: %m");
link->dhcp4_messages++;
}
return 0;
}
-static int dhcp_remove_routes(Link *link, struct in_addr *address) {
- _cleanup_free_ sd_dhcp_route **routes = NULL;
+static bool route_present_in_routes(const Route *route, sd_dhcp_route **routes, unsigned n_routes) {
+ assert(n_routes == 0 || routes);
+
+ for (unsigned j = 0; j < n_routes; j++) {
+ union in_addr_union a;
+ unsigned char l;
+
+ assert_se(sd_dhcp_route_get_gateway(routes[j], &a.in) >= 0);
+ if (!in_addr_equal(AF_INET, &a, &route->gw))
+ continue;
+ assert_se(sd_dhcp_route_get_destination(routes[j], &a.in) >= 0);
+ if (!in_addr_equal(AF_INET, &a, &route->dst))
+ continue;
+ assert_se(sd_dhcp_route_get_destination_prefix_length(routes[j], &l) >= 0);
+ if (l != route->dst_prefixlen)
+ continue;
+ return true;
+ }
+
+ return false;
+}
+
+static int dhcp_remove_routes(Link *link, sd_dhcp_lease *lease, sd_dhcp_lease *new_lease, struct in_addr *address) {
+ _cleanup_free_ sd_dhcp_route **routes = NULL, **new_routes = NULL;
uint32_t table;
- int n, i, r;
+ int m = 0, n, i, r;
assert(link);
assert(address);
if (!link->network->dhcp_use_routes)
return 0;
- n = sd_dhcp_lease_get_routes(link->dhcp_lease, &routes);
- if (IN_SET(n, 0, -ENODATA)) {
- log_link_debug(link, "DHCP: No routes received from DHCP server: %m");
+ n = sd_dhcp_lease_get_routes(lease, &routes);
+ if (IN_SET(n, 0, -ENODATA))
return 0;
- } else if (n < 0)
- return log_link_error_errno(link, n, "DHCP error: could not get routes: %m");
+ else if (n < 0)
+ return log_link_error_errno(link, n, "DHCP error: Failed to get routes: %m");
+
+ if (new_lease) {
+ m = sd_dhcp_lease_get_routes(new_lease, &new_routes);
+ if (m == -ENODATA)
+ m = 0;
+ else if (m < 0)
+ return log_link_error_errno(link, m, "DHCP error: Failed to get routes: %m");
+ }
table = link_get_dhcp_route_table(link);
route->priority = link->network->dhcp_route_metric;
route->table = table;
route->scope = route_scope_from_address(route, address);
+ if (IN_SET(route->scope, RT_SCOPE_LINK, RT_SCOPE_UNIVERSE))
+ route->prefsrc.in = *address;
+
+ if (route_present_in_routes(route, new_routes, m))
+ continue;
(void) route_remove(route, link, NULL);
}
return n;
}
-static int dhcp_remove_router(Link *link, struct in_addr *address) {
+static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, struct in_addr *address) {
_cleanup_(route_freep) Route *route_gw = NULL, *route = NULL;
const struct in_addr *router;
uint32_t table;
if (!link->network->dhcp_use_routes)
return 0;
- r = sd_dhcp_lease_get_router(link->dhcp_lease, &router);
+ r = sd_dhcp_lease_get_router(lease, &router);
if (IN_SET(r, 0, -ENODATA)) {
log_link_debug(link, "DHCP: No gateway received from DHCP server.");
return 0;
return 0;
}
-static int dhcp_remove_address(Link *link, struct in_addr *address) {
+static int dhcp_remove_address(Link *link, sd_dhcp_lease *lease, struct in_addr *address) {
_cleanup_(address_freep) Address *a = NULL;
struct in_addr netmask;
int r;
a->family = AF_INET;
a->in_addr.in = *address;
- if (sd_dhcp_lease_get_netmask(link->dhcp_lease, &netmask) >= 0)
+ if (sd_dhcp_lease_get_netmask(lease, &netmask) >= 0)
a->prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
(void) address_remove(a, link, NULL);
link->dhcp4_configured = false;
(void) sd_dhcp_lease_get_address(link->dhcp_lease, &address);
- (void) dhcp_remove_routes(link, &address);
- (void) dhcp_remove_router(link, &address);
- (void) dhcp_remove_address(link, &address);
+ (void) dhcp_remove_routes(link, link->dhcp_lease, NULL, &address);
+ (void) dhcp_remove_router(link, link->dhcp_lease, &address);
+ (void) dhcp_remove_address(link, link->dhcp_lease, &address);
(void) dhcp_reset_mtu(link);
(void) dhcp_reset_hostname(link);
assert(link);
+ if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
+ return 1;
+
r = sd_netlink_message_get_errno(m);
if (r < 0 && r != -EEXIST) {
log_link_error_errno(link, r, "Could not set DHCPv4 address: %m");
link_enter_failed(link);
- } else if (r >= 0)
- manager_rtnl_process_address(rtnl, m, link->manager);
+ return 1;
+ }
+
+ manager_rtnl_process_address(rtnl, m, link->manager);
- link_set_dhcp_routes(link);
+ r = link_set_dhcp_routes(link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return 1;
+ }
/* Add back static routes since kernel removes while DHCPv4 address is removed from when lease expires */
link_request_set_routes(link);
if (link->dhcp4_messages == 0) {
link->dhcp4_configured = true;
+ /* The new address is configured, and no route is requested.
+ * Let's drop the old lease. */
+ dhcp4_release_old_lease(link);
link_check_ready(link);
}
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: no netmask: %m");
- if (!link->network->dhcp_critical) {
+ if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
if (r < 0)
return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
}
r = dhcp4_update_address(link, &address, &netmask, lifetime);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not update IP address: %m");
- link_enter_failed(link);
- return r;
- }
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update IP address: %m");
return 0;
}
prefixlen = in4_addr_netmask_to_prefixlen(&netmask);
+ if (!FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
+ r = sd_dhcp_lease_get_lifetime(lease, &lifetime);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
+ }
+
r = sd_dhcp_lease_get_router(lease, &router);
if (r < 0 && r != -ENODATA)
return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m");
}
}
- if (!link->network->dhcp_critical) {
- r = sd_dhcp_lease_get_lifetime(link->dhcp_lease, &lifetime);
- if (r < 0)
- return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m");
- }
-
r = dhcp4_update_address(link, &address, &netmask, lifetime);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Could not update IP address: %m");
+
+ return 0;
+}
+
+static int dhcp_lease_ip_change(sd_dhcp_client *client, Link *link) {
+ int r;
+
+ link->dhcp_lease_old = TAKE_PTR(link->dhcp_lease);
+
+ /* On ip address change, to keep the connectability, we would like to assign new address and
+ * routes, and then release old lease. There are two possible success paths:
+ *
+ * 1. new address and routes are configured.
+ * -> handled by dhcp_release_old_lease() in dhcp4_route_handler().
+ * 2. new address is configured and no route is requested.
+ * -> handled by dhcp_release_old_lease() in dhcp4_address_handler().
+ *
+ * On error in assigning new address and routes, then the link always enters to the failed
+ * state. And link_enter_failed() leads to the DHCP client to be stopped. So,
+ * dhcp_release_old_lease() will be also called by link_stop_clients().
+ */
+
+ r = dhcp_lease_acquired(client, link);
if (r < 0) {
- log_link_warning_errno(link, r, "Could not update IP address: %m");
- link_enter_failed(link);
+ /* If it fails, then the new address is not configured yet.
+ * So, let's simply drop the old lease. */
+ sd_dhcp_lease_unref(link->dhcp_lease);
+ link->dhcp_lease = TAKE_PTR(link->dhcp_lease_old);
+ (void) dhcp_lease_lost(link);
return r;
}
return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m");
}
- if (link->network->dhcp_critical) {
+ if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it.");
return 0;
}
break;
case SD_DHCP_CLIENT_EVENT_EXPIRED:
- case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
-
- if (link->network->dhcp_critical) {
+ if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it.");
return 0;
}
}
}
- if (event == SD_DHCP_CLIENT_EVENT_IP_CHANGE) {
- r = dhcp_lease_acquired(client, link);
- if (r < 0) {
- link_enter_failed(link);
- return r;
- }
+ break;
+ case SD_DHCP_CLIENT_EVENT_IP_CHANGE:
+ if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP)) {
+ log_link_notice(link, "DHCPv4 connection considered critical, ignoring request to reconfigure it.");
+ return 0;
+ }
+
+ r = dhcp_lease_ip_change(client, link);
+ if (r < 0) {
+ link_enter_failed(link);
+ return r;
}
break;
return dhcp4_set_client_identifier(link);
}
+
+int config_parse_dhcp_max_attempts(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = data;
+ uint64_t a;
+ int r;
+
+ assert(network);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ network->dhcp_max_attempts = 0;
+ return 0;
+ }
+
+ if (streq(rvalue, "infinity")) {
+ network->dhcp_max_attempts = (uint64_t) -1;
+ return 0;
+ }
+
+ r = safe_atou64(rvalue, &a);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse DHCP maximum attempts, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (a == 0) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "%s= must be positive integer or 'infinity', ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ network->dhcp_max_attempts = a;
+
+ return 0;
+}
+
+int config_parse_dhcp_black_listed_ip_address(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = data;
+ const char *p;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (isempty(rvalue)) {
+ network->dhcp_black_listed_ip = set_free(network->dhcp_black_listed_ip);
+ return 0;
+ }
+
+ for (p = rvalue;;) {
+ _cleanup_free_ char *n = NULL;
+ union in_addr_union ip;
+
+ r = extract_first_word(&p, &n, NULL, 0);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse DHCP black listed ip address, ignoring assignment: %s",
+ rvalue);
+ return 0;
+ }
+ if (r == 0)
+ return 0;
+
+ r = in_addr_from_string(AF_INET, n, &ip);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "DHCP black listed ip address is invalid, ignoring assignment: %s", n);
+ continue;
+ }
+
+ r = set_ensure_allocated(&network->dhcp_black_listed_ip, NULL);
+ if (r < 0)
+ return log_oom();
+
+ r = set_put(network->dhcp_black_listed_ip, UINT32_TO_PTR(ip.in.s_addr));
+ if (r < 0)
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to store DHCP black listed ip address '%s', ignoring assignment: %m", n);
+ }
+
+ return 0;
+}
+
+int config_parse_dhcp_user_class(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ char ***l = data;
+ int r;
+
+ assert(l);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ *l = strv_free(*l);
+ return 0;
+ }
+
+ for (;;) {
+ _cleanup_free_ char *w = NULL;
+
+ r = extract_first_word(&rvalue, &w, NULL, 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to split user classes option, ignoring: %s", rvalue);
+ break;
+ }
+ if (r == 0)
+ break;
+
+ if (strlen(w) > 255) {
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "%s length is not in the range 1-255, ignoring.", w);
+ continue;
+ }
+
+ r = strv_push(l, w);
+ if (r < 0)
+ return log_oom();
+
+ w = NULL;
+ }
+
+ return 0;
+}
+
+static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
+ [DHCP_CLIENT_ID_MAC] = "mac",
+ [DHCP_CLIENT_ID_DUID] = "duid",
+ [DHCP_CLIENT_ID_DUID_ONLY] = "duid-only",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DHCPClientIdentifier);
+DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DHCPClientIdentifier,
+ "Failed to parse client identifier type");
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "conf-parser.h"
+
+typedef struct Link Link;
+
+typedef enum DHCPClientIdentifier {
+ DHCP_CLIENT_ID_MAC,
+ DHCP_CLIENT_ID_DUID,
+ /* The following option may not be good for RFC regarding DHCP (3315 and 4361).
+ * But some setups require this. E.g., Sky Broadband, the second largest provider in the UK
+ * requires the client id to be set to a custom string, reported at
+ * https://github.com/systemd/systemd/issues/7828 */
+ DHCP_CLIENT_ID_DUID_ONLY,
+ _DHCP_CLIENT_ID_MAX,
+ _DHCP_CLIENT_ID_INVALID = -1,
+} DHCPClientIdentifier;
+
+void dhcp4_release_old_lease(Link *link);
+int dhcp4_configure(Link *link);
+int dhcp4_set_client_identifier(Link *link);
+int dhcp4_set_promote_secondaries(Link *link);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_black_listed_ip_address);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
+CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class);
#include "hostname-util.h"
#include "missing_network.h"
#include "network-internal.h"
+#include "networkd-dhcp6.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "siphash24.h"
#include "radv-internal.h"
static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link);
+static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr);
+static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link);
+static int dhcp6_prefix_remove_all(Manager *m, Link *link);
static bool dhcp6_get_prefix_delegation(Link *link) {
if (!link->network)
if (r < 0 && r != -EEXIST)
return r;
- r = manager_dhcp6_prefix_add(link->manager, prefix, link);
+ r = dhcp6_prefix_add(link->manager, prefix, link);
if (r < 0)
return r;
r = sd_netlink_message_get_errno(m);
if (r < 0)
- log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnetl: %m");
+ log_link_debug_errno(link, r, "Received error on unreachable route removal for DHCPv6 delegated subnet: %m");
return 1;
}
if (!dhcp6_get_prefix_delegation(link))
continue;
- assigned_link = manager_dhcp6_prefix_get(manager, &prefix.in6);
+ assigned_link = dhcp6_prefix_get(manager, &prefix.in6);
if (assigned_link && assigned_link != link)
continue;
log_link_warning(link, "DHCPv6 lease lost");
(void) dhcp6_lease_pd_prefix_lost(client, link);
- (void) manager_dhcp6_prefix_remove_all(link->manager, link);
+ (void) dhcp6_prefix_remove_all(link->manager, link);
link->dhcp6_configured = false;
break;
return 0;
}
+
+static Link *dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
+ assert_return(m, NULL);
+ assert_return(addr, NULL);
+
+ return hashmap_get(m->dhcp6_prefixes, addr);
+}
+
+static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(link);
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0 && r != -EEXIST)
+ log_link_debug_errno(link, r, "Received error adding DHCPv6 Prefix Delegation route: %m");
+
+ return 0;
+}
+
+static int dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
+ _cleanup_free_ struct in6_addr *a = NULL;
+ _cleanup_free_ char *buf = NULL;
+ Link *assigned_link;
+ Route *route;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(addr, -EINVAL);
+
+ r = route_add(link, AF_INET6, (union in_addr_union *) addr, 64,
+ 0, 0, 0, &route);
+ if (r < 0)
+ return r;
+
+ r = route_configure(route, link, dhcp6_route_add_handler);
+ if (r < 0)
+ return r;
+
+ (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
+ log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
+
+ assigned_link = hashmap_get(m->dhcp6_prefixes, addr);
+ if (assigned_link) {
+ assert(assigned_link == link);
+ return 0;
+ }
+
+ a = newdup(struct in6_addr, addr, 1);
+ if (!a)
+ return -ENOMEM;
+
+ r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &in6_addr_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = hashmap_put(m->dhcp6_prefixes, a, link);
+ if (r < 0)
+ return r;
+
+ TAKE_PTR(a);
+ link_ref(link);
+ return 0;
+}
+
+static int dhcp6_prefix_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
+ int r;
+
+ assert(link);
+
+ r = sd_netlink_message_get_errno(m);
+ if (r < 0)
+ log_link_debug_errno(link, r, "Received error on DHCPv6 Prefix Delegation route removal: %m");
+
+ return 1;
+}
+
+int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
+ _cleanup_free_ struct in6_addr *a = NULL;
+ _cleanup_(link_unrefp) Link *l = NULL;
+ _cleanup_free_ char *buf = NULL;
+ Route *route;
+ int r;
+
+ assert_return(m, -EINVAL);
+ assert_return(addr, -EINVAL);
+
+ l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a);
+ if (!l)
+ return -EINVAL;
+
+ (void) sd_radv_remove_prefix(l->radv, addr, 64);
+ r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64, 0, 0, 0, &route);
+ if (r < 0)
+ return r;
+
+ r = route_remove(route, l, dhcp6_prefix_remove_handler);
+ if (r < 0)
+ return r;
+
+ (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
+ log_link_debug(l, "Removing prefix route %s/64", strnull(buf));
+
+ return 0;
+}
+
+static int dhcp6_prefix_remove_all(Manager *m, Link *link) {
+ struct in6_addr *addr;
+ Iterator i;
+ Link *l;
+
+ assert_return(m, -EINVAL);
+ assert_return(link, -EINVAL);
+
+ HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i)
+ if (l == link)
+ (void) dhcp6_prefix_remove(m, addr);
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-dhcp6-client.h"
+
+#include "conf-parser.h"
+
+typedef struct Link Link;
+typedef struct Manager Manager;
+
+int dhcp6_request_prefix_delegation(Link *link);
+int dhcp6_configure(Link *link);
+int dhcp6_request_address(Link *link, int ir);
+int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link);
+int dhcp6_prefix_remove(Manager *m, struct in6_addr *addr);
assert(link->manager);
assert(fdb_entry);
+ if (fdb_entry->family == AF_INET6 && link_sysctl_ipv6_enabled(link) == 0) {
+ log_link_warning(link, "An IPv6 fdb entry is requested, but IPv6 is disabled by sysctl, ignoring.");
+ return 0;
+ }
+
/* create new RTM message */
r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, PF_BRIDGE);
if (r < 0)
link_ref(link);
- return 0;
+ return 1;
}
/* remove and FDB entry. */
#include "network-internal.h"
#include "networkd-address.h"
-#include "networkd-manager.h"
+#include "networkd-ipv4ll.h"
#include "networkd-link.h"
+#include "networkd-manager.h"
+#include "parse-util.h"
static int ipv4ll_address_lost(Link *link) {
_cleanup_(address_freep) Address *address = NULL;
}
if (link->sd_device &&
- net_get_unique_predictable_data(link->sd_device, &seed) >= 0) {
+ net_get_unique_predictable_data(link->sd_device, true, &seed) >= 0) {
r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
if (r < 0)
return r;
return 0;
}
+
+int config_parse_ipv4ll(
+ const char* unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ AddressFamilyBoolean *link_local = data;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ /* Note that this is mostly like
+ * config_parse_address_family_boolean(), except that it
+ * applies only to IPv4 */
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to parse %s=%s, ignoring assignment. "
+ "Note that the setting %s= is deprecated, please use LinkLocalAddressing= instead.",
+ lvalue, rvalue, lvalue);
+ return 0;
+ }
+
+ SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, r);
+
+ log_syntax(unit, LOG_WARNING, filename, line, 0,
+ "%s=%s is deprecated, please use LinkLocalAddressing=%s instead.",
+ lvalue, rvalue, address_family_boolean_to_string(*link_local));
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "conf-parser.h"
+
+#define IPV4LL_ROUTE_METRIC 2048
+
+typedef struct Link Link;
+
+int ipv4ll_configure(Link *link);
+
+CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll);
#include "alloc-util.h"
#include "bus-common-errors.h"
#include "bus-util.h"
+#include "networkd-link-bus.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "parse-util.h"
#include "strv.h"
-static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
+BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
+BUS_DEFINE_PROPERTY_GET_ENUM(property_get_carrier_state, link_carrier_state, LinkCarrierState);
+BUS_DEFINE_PROPERTY_GET_ENUM(property_get_address_state, link_address_state, LinkAddressState);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_administrative_state, link_state, LinkState);
static int property_get_bit_rates(
Link *link = userdata;
Manager *manager;
- double tx, rx, interval_sec;
+ double interval_sec;
+ uint64_t tx, rx;
assert(bus);
assert(reply);
return sd_bus_error_set(error, BUS_ERROR_SPEED_METER_INACTIVE, "Failed to measure bit-rates.");
assert(manager->speed_meter_usec_new > manager->speed_meter_usec_old);
- interval_sec = (double) (manager->speed_meter_usec_new - manager->speed_meter_usec_old) / USEC_PER_SEC;
+ interval_sec = (manager->speed_meter_usec_new - manager->speed_meter_usec_old) / USEC_PER_SEC;
if (link->stats_new.tx_bytes > link->stats_old.tx_bytes)
- tx = (link->stats_new.tx_bytes - link->stats_old.tx_bytes) / interval_sec;
+ tx = (uint64_t) ((link->stats_new.tx_bytes - link->stats_old.tx_bytes) / interval_sec);
else
- tx = (UINT64_MAX - (link->stats_old.tx_bytes - link->stats_new.tx_bytes)) / interval_sec;
+ tx = (uint64_t) ((UINT64_MAX - (link->stats_old.tx_bytes - link->stats_new.tx_bytes)) / interval_sec);
if (link->stats_new.rx_bytes > link->stats_old.rx_bytes)
- rx = (link->stats_new.rx_bytes - link->stats_old.rx_bytes) / interval_sec;
+ rx = (uint64_t) ((link->stats_new.rx_bytes - link->stats_old.rx_bytes) / interval_sec);
else
- rx = (UINT64_MAX - (link->stats_old.rx_bytes - link->stats_new.rx_bytes)) / interval_sec;
+ rx = (uint64_t) ((UINT64_MAX - (link->stats_old.rx_bytes - link->stats_new.rx_bytes)) / interval_sec);
- return sd_bus_message_append(reply, "(dd)", tx, rx);
+ return sd_bus_message_append(reply, "(tt)", tx, rx);
}
const sd_bus_vtable link_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Link, operstate), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("CarrierState", "s", property_get_carrier_state, offsetof(Link, carrier_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("AddressState", "s", property_get_address_state, offsetof(Link, address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("AdministrativeState", "s", property_get_administrative_state, offsetof(Link, state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
- SD_BUS_PROPERTY("BitRates", "(dd)", property_get_bit_rates, 0, 0),
+ SD_BUS_PROPERTY("BitRates", "(tt)", property_get_bit_rates, 0, 0),
SD_BUS_VTABLE_END
};
-static char *link_bus_path(Link *link) {
+char *link_bus_path(Link *link) {
_cleanup_free_ char *ifindex = NULL;
char *p;
int r;
return 1;
}
-int link_send_changed(Link *link, const char *property, ...) {
+int link_send_changed_strv(Link *link, char **properties) {
_cleanup_free_ char *p = NULL;
- char **l;
assert(link);
assert(link->manager);
+ assert(properties);
if (!link->manager->bus)
- return 0; /* replace with assert when we have kdbus */
-
- l = strv_from_stdarg_alloca(property);
+ return 0;
p = link_bus_path(link);
if (!p)
link->manager->bus,
p,
"org.freedesktop.network1.Link",
- l);
+ properties);
+}
+
+int link_send_changed(Link *link, const char *property, ...) {
+ char **properties;
+
+ properties = strv_from_stdarg_alloca(property);
+
+ return link_send_changed_strv(link, properties);
}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+#include "macro.h"
+
+typedef struct Link Link;
+
+extern const sd_bus_vtable link_vtable[];
+
+char *link_bus_path(Link *link);
+int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
+int link_send_changed_strv(Link *link, char **properties);
+int link_send_changed(Link *link, const char *property, ...) _sentinel_;
+
+int property_get_operational_state(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+int property_get_carrier_state(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
+int property_get_address_state(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error);
#include "netlink-util.h"
#include "network-internal.h"
#include "networkd-can.h"
+#include "networkd-dhcp-server.h"
+#include "networkd-dhcp4.h"
+#include "networkd-dhcp6.h"
+#include "networkd-ipv4ll.h"
#include "networkd-ipv6-proxy-ndp.h"
+#include "networkd-link-bus.h"
+#include "networkd-link.h"
#include "networkd-lldp-tx.h"
#include "networkd-manager.h"
#include "networkd-ndisc.h"
return &link->manager->duid;
}
+int link_sysctl_ipv6_enabled(Link *link) {
+ _cleanup_free_ char *value = NULL;
+ int r;
+
+ assert(link);
+ assert(link->ifname);
+
+ if (link->sysctl_ipv6_enabled >= 0)
+ return link->sysctl_ipv6_enabled;
+
+ const char *ifname = link->ifname; /* work around bogus gcc warning */
+ r = sysctl_read_ip_property(AF_INET6, ifname, "disable_ipv6", &value);
+ if (r < 0)
+ return log_link_warning_errno(link, r,
+ "Failed to read net.ipv6.conf.%s.disable_ipv6 sysctl property: %m",
+ ifname);
+
+ link->sysctl_ipv6_enabled = value[0] == '0';
+ return link->sysctl_ipv6_enabled;
+}
+
static bool link_dhcp6_enabled(Link *link) {
assert(link);
if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
return link->network->dhcp & ADDRESS_FAMILY_IPV6;
if (!link->network)
return false;
- if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "ip6gre", "ip6tnl", "sit", "vti", "vti6", "can", "vcan", "vxcan", "nlmon"))
+ if (STRPTR_IN_SET(link->kind,
+ "vrf", "wireguard", "ipip", "gre", "ip6gre","ip6tnl", "sit", "vti",
+ "vti6", "can", "vcan", "vxcan", "nlmon", "xfrm"))
return false;
/* L3 or L3S mode do not support ARP. */
if (link->network->bond)
return false;
- if (link->network->bond)
- return false;
-
return link->network->link_local & mask;
}
if (link->network->bond)
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
return link->network->link_local & ADDRESS_FAMILY_IPV6;
if (link->network->bond)
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
if (STRPTR_IN_SET(link->kind, "can", "vcan", "vxcan"))
if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID)
return false;
- if (manager_sysctl_ipv6_enabled(link->manager) == 0)
+ if (link_sysctl_ipv6_enabled(link) == 0)
return false;
return link->network->ip_forward & ADDRESS_FAMILY_IPV6;
void link_update_operstate(Link *link, bool also_update_master) {
LinkOperationalState operstate;
+ LinkCarrierState carrier_state;
+ LinkAddressState address_state;
+ _cleanup_strv_free_ char **p = NULL;
+ uint8_t scope = RT_SCOPE_NOWHERE;
+ bool changed = false;
+ Address *address;
Iterator i;
assert(link);
if (link->kernel_operstate == IF_OPER_DORMANT)
- operstate = LINK_OPERSTATE_DORMANT;
+ carrier_state = LINK_CARRIER_STATE_DORMANT;
else if (link_has_carrier(link)) {
- Address *address;
- uint8_t scope = RT_SCOPE_NOWHERE;
+ if (link_is_enslaved(link))
+ carrier_state = LINK_CARRIER_STATE_ENSLAVED;
+ else
+ carrier_state = LINK_CARRIER_STATE_CARRIER;
+ } else if (link->flags & IFF_UP)
+ carrier_state = LINK_CARRIER_STATE_NO_CARRIER;
+ else
+ carrier_state = LINK_CARRIER_STATE_OFF;
- /* if we have carrier, check what addresses we have */
- SET_FOREACH(address, link->addresses, i) {
- if (!address_is_ready(address))
- continue;
+ if (carrier_state >= LINK_CARRIER_STATE_CARRIER) {
+ Link *slave;
- if (address->scope < scope)
- scope = address->scope;
+ SET_FOREACH(slave, link->slaves, i) {
+ link_update_operstate(slave, false);
+
+ if (slave->carrier_state < LINK_CARRIER_STATE_CARRIER)
+ carrier_state = LINK_CARRIER_STATE_DEGRADED_CARRIER;
}
+ }
- /* for operstate we also take foreign addresses into account */
- SET_FOREACH(address, link->addresses_foreign, i) {
- if (!address_is_ready(address))
- continue;
+ SET_FOREACH(address, link->addresses, i) {
+ if (!address_is_ready(address))
+ continue;
- if (address->scope < scope)
- scope = address->scope;
- }
+ if (address->scope < scope)
+ scope = address->scope;
+ }
- if (scope < RT_SCOPE_SITE)
- /* universally accessible addresses found */
- operstate = LINK_OPERSTATE_ROUTABLE;
- else if (scope < RT_SCOPE_HOST)
- /* only link or site local addresses found */
- operstate = LINK_OPERSTATE_DEGRADED;
- else
- /* no useful addresses found */
- operstate = LINK_OPERSTATE_CARRIER;
- } else if (link->flags & IFF_UP)
- operstate = LINK_OPERSTATE_NO_CARRIER;
+ /* for operstate we also take foreign addresses into account */
+ SET_FOREACH(address, link->addresses_foreign, i) {
+ if (!address_is_ready(address))
+ continue;
+
+ if (address->scope < scope)
+ scope = address->scope;
+ }
+
+ if (scope < RT_SCOPE_SITE)
+ /* universally accessible addresses found */
+ address_state = LINK_ADDRESS_STATE_ROUTABLE;
+ else if (scope < RT_SCOPE_HOST)
+ /* only link or site local addresses found */
+ address_state = LINK_ADDRESS_STATE_DEGRADED;
else
- operstate = LINK_OPERSTATE_OFF;
+ /* no useful addresses found */
+ address_state = LINK_ADDRESS_STATE_OFF;
+
+ /* Mapping of address and carrier state vs operational state
+ * carrier state
+ * | off | no-carrier | dormant | degraded-carrier | carrier | enslaved
+ * ------------------------------------------------------------------------------
+ * off | off | no-carrier | dormant | degraded-carrier | carrier | enslaved
+ * address_state degraded | off | no-carrier | dormant | degraded-carrier | degraded | enslaved
+ * routable | off | no-carrier | dormant | degraded-carrier | routable | routable
+ */
- if (IN_SET(operstate, LINK_OPERSTATE_DEGRADED, LINK_OPERSTATE_CARRIER) &&
- link_is_enslaved(link))
+ if (carrier_state < LINK_CARRIER_STATE_CARRIER || address_state == LINK_ADDRESS_STATE_OFF)
+ operstate = (LinkOperationalState) carrier_state;
+ else if (address_state == LINK_ADDRESS_STATE_ROUTABLE)
+ operstate = LINK_OPERSTATE_ROUTABLE;
+ else if (carrier_state == LINK_CARRIER_STATE_CARRIER)
+ operstate = LINK_OPERSTATE_DEGRADED;
+ else
operstate = LINK_OPERSTATE_ENSLAVED;
- if (operstate >= LINK_OPERSTATE_CARRIER) {
- Link *slave;
-
- SET_FOREACH(slave, link->slaves, i) {
- link_update_operstate(slave, false);
+ if (link->carrier_state != carrier_state) {
+ link->carrier_state = carrier_state;
+ changed = true;
+ if (strv_extend(&p, "CarrierState") < 0)
+ log_oom();
+ }
- if (slave->operstate < LINK_OPERSTATE_CARRIER)
- operstate = LINK_OPERSTATE_DEGRADED_CARRIER;
- }
+ if (link->address_state != address_state) {
+ link->address_state = address_state;
+ changed = true;
+ if (strv_extend(&p, "AddressState") < 0)
+ log_oom();
}
if (link->operstate != operstate) {
link->operstate = operstate;
- link_send_changed(link, "OperationalState", NULL);
- link_dirty(link);
+ changed = true;
+ if (strv_extend(&p, "OperationalState") < 0)
+ log_oom();
}
+ if (p)
+ link_send_changed_strv(link, p);
+ if (changed)
+ link_dirty(link);
+
if (also_update_master && link->network) {
link_update_master_operstate(link, link->network->bond);
link_update_master_operstate(link, link->network->bridge);
.rtnl_extended_attrs = true,
.ifindex = ifindex,
.iftype = iftype,
+ .sysctl_ipv6_enabled = -1,
};
link->ifname = strdup(ifname);
link_dirty(link);
}
-int link_stop_clients(Link *link) {
+int link_stop_clients(Link *link, bool may_keep_dhcp) {
int r = 0, k;
assert(link);
assert(link->manager);
assert(link->manager->event);
- if (link->dhcp_client) {
+ dhcp4_release_old_lease(link);
+
+ if (link->dhcp_client && (!may_keep_dhcp || !link->network ||
+ !FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP_ON_STOP))) {
k = sd_dhcp_client_stop(link->dhcp_client);
if (k < 0)
r = log_link_warning_errno(link, k, "Could not stop DHCPv4 client: %m");
link_set_state(link, LINK_STATE_FAILED);
- link_stop_clients(link);
+ link_stop_clients(link, false);
link_dirty(link);
}
-static Address* link_find_dhcp_server_address(Link *link) {
- Address *address;
-
- assert(link);
- assert(link->network);
-
- /* The first statically configured address if there is any */
- LIST_FOREACH(addresses, address, link->network->static_addresses) {
-
- if (address->family != AF_INET)
- continue;
-
- if (in_addr_is_null(address->family, &address->in_addr))
- continue;
-
- return address;
- }
-
- /* If that didn't work, find a suitable address we got from the pool */
- LIST_FOREACH(addresses, address, link->pool_addresses) {
- if (address->family != AF_INET)
- continue;
-
- return address;
- }
-
- return NULL;
-}
-
static int link_join_netdevs_after_configured(Link *link) {
NetDev *netdev;
Iterator i;
link->routing_policy_rules_configured = false;
LIST_FOREACH(rules, rule, link->network->rules) {
- r = routing_policy_rule_get(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to,
- rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif,
- rule->protocol, &rule->sport, &rule->dport, &rrule);
- if (r == 0) {
- (void) routing_policy_rule_make_local(link->manager, rrule);
+ r = routing_policy_rule_get(link->manager, rule, &rrule);
+ if (r >= 0) {
+ if (r == 0)
+ (void) routing_policy_rule_make_local(link->manager, rrule);
continue;
}
- r = routing_policy_rule_configure(rule, link, NULL, false);
+ r = routing_policy_rule_configure(rule, link, NULL);
if (r < 0) {
log_link_warning_errno(link, r, "Could not set routing policy rules: %m");
link_enter_failed(link);
return r;
}
-
- link->routing_policy_rule_messages++;
+ if (r > 0)
+ link->routing_policy_rule_messages++;
}
routing_policy_rule_purge(link->manager, link);
link_enter_failed(link);
return r;
}
-
- link->route_messages++;
+ if (r > 0)
+ link->route_messages++;
}
if (link->route_messages == 0) {
if (!link->routing_policy_rules_configured)
return;
- if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !(link->ipv4ll_address && link->ipv4ll_route))
- return;
+ if (link_has_carrier(link) || !link->network->configure_without_carrier) {
- if (link_ipv6ll_enabled(link) &&
- in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
- return;
+ if (link_ipv4ll_enabled(link, ADDRESS_FAMILY_IPV4) && !(link->ipv4ll_address && link->ipv4ll_route))
+ return;
- if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) &&
- !link->dhcp4_configured &&
- !link->dhcp6_configured &&
- !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address && link->ipv4ll_route))
- /* When DHCP is enabled, at least one protocol must provide an address, or
- * an IPv4ll fallback address must be configured. */
- return;
+ if (link_ipv6ll_enabled(link) &&
+ in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address))
+ return;
- if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
- return;
+ if ((link_dhcp4_enabled(link) || link_dhcp6_enabled(link)) &&
+ !link->dhcp4_configured &&
+ !link->dhcp6_configured &&
+ !(link_ipv4ll_enabled(link, ADDRESS_FAMILY_FALLBACK_IPV4) && link->ipv4ll_address && link->ipv4ll_route))
+ /* When DHCP is enabled, at least one protocol must provide an address, or
+ * an IPv4ll fallback address must be configured. */
+ return;
+
+ if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured)
+ return;
+ }
if (link->state != LINK_STATE_CONFIGURED)
link_enter_configured(link);
return 1;
}
-static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) {
- _cleanup_free_ struct in_addr *addresses = NULL;
- size_t n_addresses = 0, n_allocated = 0;
- unsigned i;
-
- log_debug("Copying DNS server information from %s", link->ifname);
-
- if (!link->network)
- return 0;
-
- for (i = 0; i < link->network->n_dns; i++) {
- struct in_addr ia;
-
- /* Only look for IPv4 addresses */
- if (link->network->dns[i].family != AF_INET)
- continue;
-
- ia = link->network->dns[i].address.in;
-
- /* Never propagate obviously borked data */
- if (in4_addr_is_null(&ia) || in4_addr_is_localhost(&ia))
- continue;
-
- if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
- return log_oom();
-
- addresses[n_addresses++] = ia;
- }
-
- if (link->network->dhcp_use_dns && link->dhcp_lease) {
- const struct in_addr *da = NULL;
- int j, n;
-
- n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da);
- if (n > 0) {
-
- if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
- return log_oom();
-
- for (j = 0; j < n; j++)
- if (in4_addr_is_non_local(&da[j]))
- addresses[n_addresses++] = da[j];
- }
- }
-
- if (n_addresses <= 0)
- return 0;
-
- return sd_dhcp_server_set_dns(s, addresses, n_addresses);
-}
-
-static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) {
- _cleanup_free_ struct in_addr *addresses = NULL;
- size_t n_addresses = 0, n_allocated = 0;
- char **a;
-
- if (!link->network)
- return 0;
-
- log_debug("Copying NTP server information from %s", link->ifname);
-
- STRV_FOREACH(a, link->network->ntp) {
- union in_addr_union ia;
-
- /* Only look for IPv4 addresses */
- if (in_addr_from_string(AF_INET, *a, &ia) <= 0)
- continue;
-
- /* Never propagate obviously borked data */
- if (in4_addr_is_null(&ia.in) || in4_addr_is_localhost(&ia.in))
- continue;
-
- if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + 1))
- return log_oom();
-
- addresses[n_addresses++] = ia.in;
- }
-
- if (link->network->dhcp_use_ntp && link->dhcp_lease) {
- const struct in_addr *da = NULL;
- int j, n;
-
- n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da);
- if (n > 0) {
-
- if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n))
- return log_oom();
-
- for (j = 0; j < n; j++)
- if (in4_addr_is_non_local(&da[j]))
- addresses[n_addresses++] = da[j];
- }
- }
-
- if (n_addresses <= 0)
- return 0;
-
- return sd_dhcp_server_set_ntp(s, addresses, n_addresses);
-}
-
static int link_set_bridge_fdb(Link *link) {
FdbEntry *fdb_entry;
int r;
link_enter_failed(link);
return r;
}
-
- link->address_messages++;
+ if (r > 0)
+ link->address_messages++;
}
LIST_FOREACH(labels, label, link->network->address_labels) {
/* now that we can figure out a default address for the dhcp server,
start it */
if (link_dhcp4_server_enabled(link) && (link->flags & IFF_UP)) {
- Address *address;
- Link *uplink = NULL;
- bool acquired_uplink = false;
-
- address = link_find_dhcp_server_address(link);
- if (!address) {
- log_link_warning(link, "Failed to find suitable address for DHCPv4 server instance.");
+ r = dhcp4_server_configure(link);
+ if (r < 0) {
link_enter_failed(link);
- return 0;
- }
-
- /* use the server address' subnet as the pool */
- r = sd_dhcp_server_configure_pool(link->dhcp_server, &address->in_addr.in, address->prefixlen,
- link->network->dhcp_server_pool_offset, link->network->dhcp_server_pool_size);
- if (r < 0)
- return r;
-
- /* TODO:
- r = sd_dhcp_server_set_router(link->dhcp_server,
- &main_address->in_addr.in);
- if (r < 0)
return r;
- */
-
- if (link->network->dhcp_server_max_lease_time_usec > 0) {
- r = sd_dhcp_server_set_max_lease_time(
- link->dhcp_server,
- DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC));
- if (r < 0)
- return r;
}
-
- if (link->network->dhcp_server_default_lease_time_usec > 0) {
- r = sd_dhcp_server_set_default_lease_time(
- link->dhcp_server,
- DIV_ROUND_UP(link->network->dhcp_server_default_lease_time_usec, USEC_PER_SEC));
- if (r < 0)
- return r;
- }
-
- if (link->network->dhcp_server_emit_dns) {
-
- if (link->network->n_dhcp_server_dns > 0)
- r = sd_dhcp_server_set_dns(link->dhcp_server, link->network->dhcp_server_dns, link->network->n_dhcp_server_dns);
- else {
- uplink = manager_find_uplink(link->manager, link);
- acquired_uplink = true;
-
- if (!uplink) {
- log_link_debug(link, "Not emitting DNS server information on link, couldn't find suitable uplink.");
- r = 0;
- } else
- r = link_push_uplink_dns_to_dhcp_server(uplink, link->dhcp_server);
- }
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to set DNS server for DHCP server, ignoring: %m");
- }
-
- if (link->network->dhcp_server_emit_ntp) {
-
- if (link->network->n_dhcp_server_ntp > 0)
- r = sd_dhcp_server_set_ntp(link->dhcp_server, link->network->dhcp_server_ntp, link->network->n_dhcp_server_ntp);
- else {
- if (!acquired_uplink)
- uplink = manager_find_uplink(link->manager, link);
-
- if (!uplink) {
- log_link_debug(link, "Not emitting NTP server information on link, couldn't find suitable uplink.");
- r = 0;
- } else
- r = link_push_uplink_ntp_to_dhcp_server(uplink, link->dhcp_server);
-
- }
- if (r < 0)
- log_link_warning_errno(link, r, "Failed to set NTP server for DHCP server, ignoring: %m");
- }
-
- r = sd_dhcp_server_set_emit_router(link->dhcp_server, link->network->dhcp_server_emit_router);
- if (r < 0)
- return log_link_warning_errno(link, r, "Failed to set router emission for DHCP server: %m");
-
- if (link->network->dhcp_server_emit_timezone) {
- _cleanup_free_ char *buffer = NULL;
- const char *tz = NULL;
-
- if (link->network->dhcp_server_timezone)
- tz = link->network->dhcp_server_timezone;
- else {
- r = get_timezone(&buffer);
- if (r < 0)
- log_warning_errno(r, "Failed to determine timezone: %m");
- else
- tz = buffer;
- }
-
- if (tz) {
- r = sd_dhcp_server_set_timezone(link->dhcp_server, tz);
- if (r < 0)
- return r;
- }
- }
- if (!sd_dhcp_server_is_running(link->dhcp_server)) {
- r = sd_dhcp_server_start(link->dhcp_server);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not start DHCPv4 server instance: %m");
-
- link_enter_failed(link);
-
- return 0;
- }
- }
-
log_link_debug(link, "Offering DHCPv4 leases");
}
return false;
}
+static bool link_address_is_dynamic(Link *link, Address *address) {
+ Route *route;
+ Iterator i;
+
+ assert(link);
+ assert(address);
+
+ if (address->cinfo.ifa_prefered != CACHE_INFO_INFINITY_LIFE_TIME)
+ return true;
+
+ /* Even when the address is leased from a DHCP server, networkd assign the address
+ * without lifetime when KeepConfiguration=dhcp. So, let's check that we have
+ * corresponding routes with RTPROT_DHCP. */
+ SET_FOREACH(route, link->routes_foreign, i) {
+ if (route->protocol != RTPROT_DHCP)
+ continue;
+
+ if (address->family != route->family)
+ continue;
+
+ if (in_addr_equal(address->family, &address->in_addr, &route->prefsrc))
+ return true;
+ }
+
+ return false;
+}
+
static int link_drop_foreign_config(Link *link) {
Address *address;
Route *route;
if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1)
continue;
+ if (link_address_is_dynamic(link, address)) {
+ if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
+ continue;
+ } else if (FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
+ continue;
+
if (link_is_static_address_configured(link, address)) {
r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL);
if (r < 0)
if (route->protocol == RTPROT_KERNEL)
continue;
+ if (route->protocol == RTPROT_STATIC &&
+ FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_STATIC))
+ continue;
+
+ if (route->protocol == RTPROT_DHCP &&
+ FLAGS_SET(link->network->keep_configuration, KEEP_CONFIGURATION_DHCP))
+ continue;
+
if (link_is_static_route_configured(link, route)) {
r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL);
if (r < 0)
/* Drop foreign config, but ignore loopback or critical devices.
* We do not want to remove loopback address or addresses used for root NFS. */
- if (!(link->flags & IFF_LOOPBACK) && !(link->network->dhcp_critical)) {
+ if (!(link->flags & IFF_LOOPBACK) &&
+ link->network->keep_configuration != KEEP_CONFIGURATION_YES) {
r = link_drop_foreign_config(link);
if (r < 0)
return r;
if (link->setting_mtu)
return 0;
- r = link_stop_clients(link);
+ r = link_stop_clients(link, false);
if (r < 0) {
link_enter_failed(link);
return r;
int link_save(Link *link) {
_cleanup_free_ char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
- const char *admin_state, *oper_state;
+ const char *admin_state, *oper_state, *carrier_state, *address_state;
Address *a;
Route *route;
Iterator i;
oper_state = link_operstate_to_string(link->operstate);
assert(oper_state);
+ carrier_state = link_carrier_state_to_string(link->carrier_state);
+ assert(carrier_state);
+
+ address_state = link_address_state_to_string(link->address_state);
+ assert(address_state);
+
r = fopen_temporary(link->state_file, &f, &temp_path);
if (r < 0)
goto fail;
fprintf(f,
"# This is private data. Do not parse.\n"
"ADMIN_STATE=%s\n"
- "OPER_STATE=%s\n",
- admin_state, oper_state);
+ "OPER_STATE=%s\n"
+ "CARRIER_STATE=%s\n"
+ "ADDRESS_STATE=%s\n",
+ admin_state, oper_state, carrier_state, address_state);
if (link->network) {
char **dhcp6_domains = NULL, **dhcp_domains = NULL;
space = true;
}
- if (link->network->dhcp_use_dns && dhcp6_lease) {
+ if (link->network->dhcp6_use_dns && dhcp6_lease) {
struct in6_addr *in6_addrs;
r = sd_dhcp6_lease_get_dns(dhcp6_lease, &in6_addrs);
space = true;
}
- if (link->network->dhcp_use_ntp && dhcp6_lease) {
+ if (link->network->dhcp6_use_ntp && dhcp6_lease) {
struct in6_addr *in6_addrs;
char **hosts;
LinkState state;
LinkOperationalState operstate;
+ LinkCarrierState carrier_state;
+ LinkAddressState address_state;
unsigned address_messages;
unsigned address_label_messages;
bool addresses_ready;
sd_dhcp_client *dhcp_client;
- sd_dhcp_lease *dhcp_lease;
+ sd_dhcp_lease *dhcp_lease, *dhcp_lease_old;
char *lease_file;
uint32_t original_mtu;
unsigned dhcp4_messages;
/* For speed meter */
struct rtnl_link_stats64 stats_old, stats_new;
bool stats_updated;
+
+ int sysctl_ipv6_enabled;
} Link;
typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*);
int link_set_mtu(Link *link, uint32_t mtu);
-int ipv4ll_configure(Link *link);
bool link_ipv4ll_enabled(Link *link, AddressFamilyBoolean mask);
-int dhcp4_configure(Link *link);
-int dhcp4_set_client_identifier(Link *link);
-int dhcp4_set_promote_secondaries(Link *link);
-int dhcp6_request_prefix_delegation(Link *link);
-int dhcp6_configure(Link *link);
-int dhcp6_request_address(Link *link, int ir);
-int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link);
-
-int link_stop_clients(Link *link);
+int link_stop_clients(Link *link, bool may_keep_dhcp);
const char* link_state_to_string(LinkState s) _const_;
LinkState link_state_from_string(const char *s) _pure_;
-extern const sd_bus_vtable link_vtable[];
-
-int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-int link_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-int link_send_changed(Link *link, const char *property, ...) _sentinel_;
-
uint32_t link_get_vrf_table(Link *link);
uint32_t link_get_dhcp_route_table(Link *link);
uint32_t link_get_ipv6_accept_ra_route_table(Link *link);
int link_request_set_routes(Link *link);
+int link_sysctl_ipv6_enabled(Link *link);
+
#define ADDRESS_FMT_VAL(address) \
be32toh((address).s_addr) >> 24, \
(be32toh((address).s_addr) >> 16) & 0xFFu, \
#include "alloc-util.h"
#include "bus-util.h"
+#include "networkd-link-bus.h"
#include "networkd-manager.h"
#include "strv.h"
-static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_operational_state, link_operstate, LinkOperationalState);
-
const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("OperationalState", "s", property_get_operational_state, offsetof(Manager, operational_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("CarrierState", "s", property_get_carrier_state, offsetof(Manager, carrier_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
+ SD_BUS_PROPERTY("AddressState", "s", property_get_address_state, offsetof(Manager, address_state), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_VTABLE_END
};
-int manager_send_changed(Manager *manager, const char *property, ...) {
- char **l;
-
+int manager_send_changed_strv(Manager *manager, char **properties) {
assert(manager);
+ assert(properties);
if (!manager->bus)
- return 0; /* replace by assert when we have kdbus */
-
- l = strv_from_stdarg_alloca(property);
+ return 0;
return sd_bus_emit_properties_changed_strv(
manager->bus,
"/org/freedesktop/network1",
"org.freedesktop.network1.Manager",
- l);
+ properties);
}
#include "local-addresses.h"
#include "netlink-util.h"
#include "network-internal.h"
+#include "networkd-dhcp6.h"
+#include "networkd-link-bus.h"
#include "networkd-manager.h"
+#include "networkd-network-bus.h"
#include "networkd-speed-meter.h"
#include "ordered-set.h"
#include "path-util.h"
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
- log_warning_errno(r, "rtnl: failed to receive route, ignoring: %m");
+ log_warning_errno(r, "rtnl: failed to receive route message, ignoring: %m");
return 0;
}
log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
return 0;
} else if (!IN_SET(type, RTM_NEWROUTE, RTM_DELROUTE)) {
- log_warning("rtnl: received unexpected message type when processing route, ignoring");
+ log_warning("rtnl: received unexpected message type %u when processing route, ignoring.", type);
return 0;
}
log_debug("rtnl: received route without ifindex, ignoring");
return 0;
} else if (r < 0) {
- log_warning_errno(r, "rtnl: could not get ifindex from route, ignoring: %m");
+ log_warning_errno(r, "rtnl: could not get ifindex from route message, ignoring: %m");
return 0;
} else if (ifindex <= 0) {
- log_warning("rtnl: received route message with invalid ifindex, ignoring: %d", ifindex);
- return 0;
- } else {
- r = link_get(m, ifindex, &link);
- if (r < 0 || !link) {
- /* when enumerating we might be out of sync, but we will
- * get the route again, so just ignore it */
- if (!m->enumerating)
- log_warning("rtnl: received route for nonexistent link (%d), ignoring", ifindex);
- return 0;
- }
+ log_warning("rtnl: received route message with invalid ifindex %d, ignoring.", ifindex);
+ return 0;
+ }
+
+ r = link_get(m, ifindex, &link);
+ if (r < 0 || !link) {
+ /* when enumerating we might be out of sync, but we will
+ * get the route again, so just ignore it */
+ if (!m->enumerating)
+ log_warning("rtnl: received route for link (%d) we do not know about, ignoring", ifindex);
+ return 0;
}
r = sd_rtnl_message_route_get_family(message, &family);
if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
- log_link_warning(link, "rtnl: received address with invalid family, ignoring");
+ log_link_warning(link, "rtnl: received route message with invalid family, ignoring");
return 0;
}
r = sd_rtnl_message_route_get_protocol(message, &protocol);
if (r < 0) {
- log_warning_errno(r, "rtnl: could not get route protocol: %m");
+ log_warning_errno(r, "rtnl: received route message with invalid route protocol: %m");
return 0;
}
case AF_INET:
r = sd_netlink_message_read_in_addr(message, RTA_DST, &dst.in);
if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: received route without valid destination, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
return 0;
}
r = sd_netlink_message_read_in_addr(message, RTA_GATEWAY, &gw.in);
if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: received route with invalid gateway, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
return 0;
}
r = sd_netlink_message_read_in_addr(message, RTA_SRC, &src.in);
if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: received route with invalid source, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
return 0;
}
r = sd_netlink_message_read_in_addr(message, RTA_PREFSRC, &prefsrc.in);
if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: received route with invalid preferred source, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
return 0;
}
case AF_INET6:
r = sd_netlink_message_read_in6_addr(message, RTA_DST, &dst.in6);
if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: received route without valid destination, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received route message without valid destination, ignoring: %m");
return 0;
}
r = sd_netlink_message_read_in6_addr(message, RTA_GATEWAY, &gw.in6);
if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: received route with invalid gateway, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received route message without valid gateway, ignoring: %m");
return 0;
}
r = sd_netlink_message_read_in6_addr(message, RTA_SRC, &src.in6);
if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: received route with invalid source, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received route message without valid source, ignoring: %m");
return 0;
}
r = sd_netlink_message_read_in6_addr(message, RTA_PREFSRC, &prefsrc.in6);
if (r < 0 && r != -ENODATA) {
- log_link_warning_errno(link, r, "rtnl: received route with invalid preferred source, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received route message without valid preferred source, ignoring: %m");
return 0;
}
break;
default:
- assert_not_reached("Received unsupported address family");
+ assert_not_reached("Received route message with unsupported address family");
return 0;
}
log_link_debug(link,
"%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s",
- type == RTM_DELROUTE ? "Removing" : route ? "Updating" : "Adding",
+ type == RTM_DELROUTE ? "Forgetting" : route ? "Updating remembered" : "Remembering",
strna(buf_dst), strempty(buf_dst_prefixlen),
strna(buf_src), strna(buf_gw), strna(buf_prefsrc));
}
/* A route appeared that we did not request */
r = route_add_foreign(link, family, &dst, dst_prefixlen, tos, priority, table, &route);
if (r < 0) {
- log_link_warning_errno(link, r, "Failed to add route, ignoring: %m");
+ log_link_warning_errno(link, r, "Failed to remember foreign route, ignoring: %m");
return 0;
}
}
break;
default:
- assert_not_reached("Received invalid RTNL message type");
+ assert_not_reached("Received route message with invalid RTNL message type");
}
return 1;
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
- log_warning_errno(r, "rtnl: failed to receive address, ignoring: %m");
+ log_warning_errno(r, "rtnl: failed to receive address message, ignoring: %m");
return 0;
}
log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
return 0;
} else if (!IN_SET(type, RTM_NEWADDR, RTM_DELADDR)) {
- log_warning("rtnl: received unexpected message type when processing address, ignoring");
+ log_warning("rtnl: received unexpected message type %u when processing address, ignoring.", type);
return 0;
}
r = sd_rtnl_message_addr_get_ifindex(message, &ifindex);
if (r < 0) {
- log_warning_errno(r, "rtnl: could not get ifindex from address, ignoring: %m");
+ log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m");
return 0;
} else if (ifindex <= 0) {
- log_warning("rtnl: received address message with invalid ifindex, ignoring: %d", ifindex);
- return 0;
- } else {
- r = link_get(m, ifindex, &link);
- if (r < 0 || !link) {
- /* when enumerating we might be out of sync, but we will
- * get the address again, so just ignore it */
- if (!m->enumerating)
- log_warning("rtnl: received address for nonexistent link (%d), ignoring", ifindex);
- return 0;
- }
+ log_warning("rtnl: received address message with invalid ifindex %d, ignoring.", ifindex);
+ return 0;
+ }
+
+ r = link_get(m, ifindex, &link);
+ if (r < 0 || !link) {
+ /* when enumerating we might be out of sync, but we will get the address again, so just
+ * ignore it */
+ if (!m->enumerating)
+ log_warning("rtnl: received address for link '%d' we don't know about, ignoring.", ifindex);
+ return 0;
}
r = sd_rtnl_message_addr_get_family(message, &family);
if (r < 0 || !IN_SET(family, AF_INET, AF_INET6)) {
- log_link_warning(link, "rtnl: received address with invalid family, ignoring");
+ log_link_warning(link, "rtnl: received address message with invalid family, ignoring.");
return 0;
}
r = sd_rtnl_message_addr_get_prefixlen(message, &prefixlen);
if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address with invalid prefixlen, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received address message with invalid prefixlen, ignoring: %m");
return 0;
}
r = sd_rtnl_message_addr_get_scope(message, &scope);
if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address with invalid scope, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received address message with invalid scope, ignoring: %m");
return 0;
}
r = sd_rtnl_message_addr_get_flags(message, &flags);
if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address with invalid flags, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received address message with invalid flags, ignoring: %m");
return 0;
}
case AF_INET:
r = sd_netlink_message_read_in_addr(message, IFA_LOCAL, &in_addr.in);
if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m");
return 0;
}
case AF_INET6:
r = sd_netlink_message_read_in6_addr(message, IFA_ADDRESS, &in_addr.in6);
if (r < 0) {
- log_link_warning_errno(link, r, "rtnl: received address without valid address, ignoring: %m");
+ log_link_warning_errno(link, r, "rtnl: received address message without valid address, ignoring: %m");
return 0;
}
}
r = in_addr_to_string(family, &in_addr, &buf);
- if (r < 0) {
- log_link_warning_errno(link, r, "Could not print address, ignoring: %m");
- return 0;
- }
+ if (r < 0)
+ log_link_warning_errno(link, r, "Could not print address: %m");
r = sd_netlink_message_read_cache_info(message, IFA_CACHEINFO, &cinfo);
if (r < 0 && r != -ENODATA) {
switch (type) {
case RTM_NEWADDR:
if (address)
- log_link_debug(link, "Updating address: %s/%u (valid %s%s)", buf, prefixlen,
+ log_link_debug(link, "Remembering updated address: %s/%u (valid %s%s)",
+ strnull(buf), prefixlen,
valid_str ? "for " : "forever", strempty(valid_str));
else {
/* An address appeared that we did not request */
r = address_add_foreign(link, family, &in_addr, prefixlen, &address);
if (r < 0) {
- log_link_warning_errno(link, r, "Failed to add address %s/%u, ignoring: %m", buf, prefixlen);
+ log_link_warning_errno(link, r, "Failed to remember foreign address %s/%u, ignoring: %m",
+ strnull(buf), prefixlen);
return 0;
} else
- log_link_debug(link, "Adding address: %s/%u (valid %s%s)", buf, prefixlen,
+ log_link_debug(link, "Remembering foreign address: %s/%u (valid %s%s)",
+ strnull(buf), prefixlen,
valid_str ? "for " : "forever", strempty(valid_str));
}
- r = address_update(address, flags, scope, &cinfo);
- if (r < 0) {
- log_link_warning_errno(link, r, "Failed to update address %s/%u, ignoring: %m", buf, prefixlen);
- return 0;
- }
+ /* address_update() logs internally, so we don't need to. */
+ (void) address_update(address, flags, scope, &cinfo);
break;
case RTM_DELADDR:
-
if (address) {
- log_link_debug(link, "Removing address: %s/%u (valid %s%s)", buf, prefixlen,
+ log_link_debug(link, "Forgetting address: %s/%u (valid %s%s)",
+ strnull(buf), prefixlen,
valid_str ? "for " : "forever", strempty(valid_str));
(void) address_drop(address);
} else
- log_link_warning(link, "Removing non-existent address: %s/%u (valid %s%s), ignoring", buf, prefixlen,
- valid_str ? "for " : "forever", strempty(valid_str));
+ log_link_info(link, "Kernel removed an address we don't remember: %s/%u (valid %s%s), ignoring.",
+ strnull(buf), prefixlen,
+ valid_str ? "for " : "forever", strempty(valid_str));
break;
+
default:
assert_not_reached("Received invalid RTNL message type");
}
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
- log_warning_errno(r, "rtnl: Could not receive link, ignoring: %m");
+ log_warning_errno(r, "rtnl: Could not receive link message, ignoring: %m");
return 0;
}
log_warning_errno(r, "rtnl: Could not get message type, ignoring: %m");
return 0;
} else if (!IN_SET(type, RTM_NEWLINK, RTM_DELLINK)) {
- log_warning("rtnl: Received unexpected message type when processing link, ignoring");
+ log_warning("rtnl: Received unexpected message type %u when processing link, ignoring.", type);
return 0;
}
r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
if (r < 0) {
- log_warning_errno(r, "rtnl: Could not get ifindex from link, ignoring: %m");
+ log_warning_errno(r, "rtnl: Could not get ifindex from link message, ignoring: %m");
return 0;
} else if (ifindex <= 0) {
- log_warning("rtnl: received link message with invalid ifindex %d, ignoring", ifindex);
+ log_warning("rtnl: received link message with invalid ifindex %d, ignoring.", ifindex);
return 0;
}
/* link is new, so add it */
r = link_add(m, message, &link);
if (r < 0) {
- log_warning_errno(r, "Could not add new link, ignoring: %m");
+ log_warning_errno(r, "Could not process new link message, ignoring: %m");
return 0;
}
}
/* netdev exists, so make sure the ifindex matches */
r = netdev_set_ifindex(netdev, message);
if (r < 0) {
- log_warning_errno(r, "Could not set ifindex on netdev, ignoring: %m");
+ log_warning_errno(r, "Could not process new link message for netdev, ignoring: %m");
return 0;
}
}
r = link_update(link, message);
if (r < 0) {
- log_warning_errno(r, "Could not update link, ignoring: %m");
+ log_warning_errno(r, "Could not process link message, ignoring: %m");
return 0;
}
break;
default:
- assert_not_reached("Received invalid RTNL message type.");
+ assert_not_reached("Received link message with invalid RTNL message type.");
}
return 1;
}
int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, void *userdata) {
- uint8_t tos = 0, to_prefixlen = 0, from_prefixlen = 0, protocol = 0;
- struct fib_rule_port_range sport = {}, dport = {};
- union in_addr_union to = IN_ADDR_NULL, from = IN_ADDR_NULL;
+ _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *tmp = NULL;
RoutingPolicyRule *rule = NULL;
- uint32_t fwmark = 0, table = 0;
const char *iif = NULL, *oif = NULL;
Manager *m = userdata;
+ unsigned flags;
uint16_t type;
- int family;
int r;
assert(rtnl);
if (sd_netlink_message_is_error(message)) {
r = sd_netlink_message_get_errno(message);
if (r < 0)
- log_warning_errno(r, "rtnl: failed to receive rule, ignoring: %m");
+ log_warning_errno(r, "rtnl: failed to receive rule message, ignoring: %m");
return 0;
}
log_warning_errno(r, "rtnl: could not get message type, ignoring: %m");
return 0;
} else if (!IN_SET(type, RTM_NEWRULE, RTM_DELRULE)) {
- log_warning("rtnl: received unexpected message type '%u' when processing rule, ignoring", type);
+ log_warning("rtnl: received unexpected message type %u when processing rule, ignoring.", type);
return 0;
}
- r = sd_rtnl_message_get_family(message, &family);
+ r = routing_policy_rule_new(&tmp);
+ if (r < 0) {
+ log_oom();
+ return 0;
+ }
+
+ r = sd_rtnl_message_get_family(message, &tmp->family);
if (r < 0) {
log_warning_errno(r, "rtnl: could not get rule family, ignoring: %m");
return 0;
- } else if (!IN_SET(family, AF_INET, AF_INET6)) {
- log_debug("rtnl: received address with invalid family %u, ignoring", family);
+ } else if (!IN_SET(tmp->family, AF_INET, AF_INET6)) {
+ log_debug("rtnl: received rule message with invalid family %d, ignoring.", tmp->family);
return 0;
}
- switch (family) {
+ switch (tmp->family) {
case AF_INET:
- r = sd_netlink_message_read_in_addr(message, FRA_SRC, &from.in);
+ r = sd_netlink_message_read_in_addr(message, FRA_SRC, &tmp->from.in);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m");
return 0;
} else if (r >= 0) {
- r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen);
+ r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &tmp->from_prefixlen);
if (r < 0) {
- log_warning_errno(r, "rtnl: failed to retrieve rule from prefix length, ignoring: %m");
+ log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m");
return 0;
}
}
- r = sd_netlink_message_read_in_addr(message, FRA_DST, &to.in);
+ r = sd_netlink_message_read_in_addr(message, FRA_DST, &tmp->to.in);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m");
return 0;
} else if (r >= 0) {
- r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen);
+ r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &tmp->to_prefixlen);
if (r < 0) {
- log_warning_errno(r, "rtnl: failed to retrieve rule to prefix length, ignoring: %m");
+ log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m");
return 0;
}
}
break;
case AF_INET6:
- r = sd_netlink_message_read_in6_addr(message, FRA_SRC, &from.in6);
+ r = sd_netlink_message_read_in6_addr(message, FRA_SRC, &tmp->from.in6);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_SRC attribute, ignoring: %m");
return 0;
} else if (r >= 0) {
- r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &from_prefixlen);
+ r = sd_rtnl_message_routing_policy_rule_get_rtm_src_prefixlen(message, &tmp->from_prefixlen);
if (r < 0) {
- log_warning_errno(r, "rtnl: failed to retrieve rule from prefix length, ignoring: %m");
+ log_warning_errno(r, "rtnl: received rule message without valid source prefix length, ignoring: %m");
return 0;
}
}
- r = sd_netlink_message_read_in6_addr(message, FRA_DST, &to.in6);
+ r = sd_netlink_message_read_in6_addr(message, FRA_DST, &tmp->to.in6);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_DST attribute, ignoring: %m");
return 0;
} else if (r >= 0) {
- r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &to_prefixlen);
+ r = sd_rtnl_message_routing_policy_rule_get_rtm_dst_prefixlen(message, &tmp->to_prefixlen);
if (r < 0) {
- log_warning_errno(r, "rtnl: failed to retrieve rule to prefix length, ignoring: %m");
+ log_warning_errno(r, "rtnl: received rule message without valid destination prefix length, ignoring: %m");
return 0;
}
}
break;
default:
- assert_not_reached("Received unsupported address family");
+ assert_not_reached("Received rule message with unsupported address family");
}
- if (from_prefixlen == 0 && to_prefixlen == 0)
+ if (tmp->from_prefixlen == 0 && tmp->to_prefixlen == 0)
return 0;
- r = sd_netlink_message_read_u32(message, FRA_FWMARK, &fwmark);
+ r = sd_rtnl_message_routing_policy_rule_get_flags(message, &flags);
+ if (r < 0) {
+ log_warning_errno(r, "rtnl: received rule message without valid flag, ignoring: %m");
+ return 0;
+ }
+ tmp->invert_rule = flags & FIB_RULE_INVERT;
+
+ r = sd_netlink_message_read_u32(message, FRA_FWMARK, &tmp->fwmark);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_FWMARK attribute, ignoring: %m");
return 0;
}
- r = sd_netlink_message_read_u32(message, FRA_TABLE, &table);
+ r = sd_netlink_message_read_u32(message, FRA_FWMASK, &tmp->fwmask);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_FWMASK attribute, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_u32(message, FRA_PRIORITY, &tmp->priority);
+ if (r < 0 && r != -ENODATA) {
+ log_warning_errno(r, "rtnl: could not get FRA_PRIORITY attribute, ignoring: %m");
+ return 0;
+ }
+
+ r = sd_netlink_message_read_u32(message, FRA_TABLE, &tmp->table);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_TABLE attribute, ignoring: %m");
return 0;
}
- r = sd_rtnl_message_routing_policy_rule_get_tos(message, &tos);
+ r = sd_rtnl_message_routing_policy_rule_get_tos(message, &tmp->tos);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get ip rule TOS, ignoring: %m");
return 0;
log_warning_errno(r, "rtnl: could not get FRA_IIFNAME attribute, ignoring: %m");
return 0;
}
+ r = free_and_strdup(&tmp->iif, iif);
+ if (r < 0)
+ return log_oom();
r = sd_netlink_message_read_string(message, FRA_OIFNAME, &oif);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_OIFNAME attribute, ignoring: %m");
return 0;
}
+ r = free_and_strdup(&tmp->oif, oif);
+ if (r < 0)
+ return log_oom();
- r = sd_netlink_message_read_u8(message, FRA_IP_PROTO, &protocol);
+ r = sd_netlink_message_read_u8(message, FRA_IP_PROTO, &tmp->protocol);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_IP_PROTO attribute, ignoring: %m");
return 0;
}
- r = sd_netlink_message_read(message, FRA_SPORT_RANGE, sizeof(sport), (void *) &sport);
+ r = sd_netlink_message_read(message, FRA_SPORT_RANGE, sizeof(tmp->sport), &tmp->sport);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_SPORT_RANGE attribute, ignoring: %m");
return 0;
}
- r = sd_netlink_message_read(message, FRA_DPORT_RANGE, sizeof(dport), (void *) &dport);
+ r = sd_netlink_message_read(message, FRA_DPORT_RANGE, sizeof(tmp->dport), &tmp->dport);
if (r < 0 && r != -ENODATA) {
log_warning_errno(r, "rtnl: could not get FRA_DPORT_RANGE attribute, ignoring: %m");
return 0;
}
- (void) routing_policy_rule_get(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, &sport, &dport, &rule);
+ (void) routing_policy_rule_get(m, tmp, &rule);
switch (type) {
case RTM_NEWRULE:
if (!rule) {
- r = routing_policy_rule_add_foreign(m, family, &from, from_prefixlen, &to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, &sport, &dport, &rule);
+ r = routing_policy_rule_add_foreign(m, tmp, &rule);
if (r < 0) {
- log_warning_errno(r, "Could not add rule, ignoring: %m");
+ log_warning_errno(r, "Could not remember foreign rule, ignoring: %m");
return 0;
}
}
Link *link;
Iterator i;
_cleanup_free_ char *temp_path = NULL;
+ _cleanup_strv_free_ char **p = NULL;
_cleanup_fclose_ FILE *f = NULL;
LinkOperationalState operstate = LINK_OPERSTATE_OFF;
- const char *operstate_str;
+ LinkCarrierState carrier_state = LINK_CARRIER_STATE_OFF;
+ LinkAddressState address_state = LINK_ADDRESS_STATE_OFF;
+ const char *operstate_str, *carrier_state_str, *address_state_str;
int r;
assert(m);
if (link->operstate > operstate)
operstate = link->operstate;
+ if (link->carrier_state > carrier_state)
+ carrier_state = link->carrier_state;
+
+ if (link->address_state > address_state)
+ address_state = link->address_state;
+
if (!link->network)
continue;
}
}
+ if (carrier_state >= LINK_CARRIER_STATE_ENSLAVED)
+ carrier_state = LINK_CARRIER_STATE_CARRIER;
+
operstate_str = link_operstate_to_string(operstate);
assert(operstate_str);
+ carrier_state_str = link_carrier_state_to_string(carrier_state);
+ assert(carrier_state_str);
+
+ address_state_str = link_address_state_to_string(address_state);
+ assert(address_state_str);
+
r = fopen_temporary(m->state_file, &f, &temp_path);
if (r < 0)
return r;
fprintf(f,
"# This is private data. Do not parse.\n"
- "OPER_STATE=%s\n", operstate_str);
+ "OPER_STATE=%s\n"
+ "CARRIER_STATE=%s\n"
+ "ADDRESS_STATE=%s\n",
+ operstate_str, carrier_state_str, address_state_str);
ordered_set_print(f, "DNS=", dns);
ordered_set_print(f, "NTP=", ntp);
if (m->operational_state != operstate) {
m->operational_state = operstate;
- r = manager_send_changed(m, "OperationalState", NULL);
+ if (strv_extend(&p, "OperationalState") < 0)
+ log_oom();
+ }
+
+ if (m->carrier_state != carrier_state) {
+ m->carrier_state = carrier_state;
+ if (strv_extend(&p, "CarrierState") < 0)
+ log_oom();
+ }
+
+ if (m->address_state != address_state) {
+ m->address_state = address_state;
+ if (strv_extend(&p, "AddressState") < 0)
+ log_oom();
+ }
+
+ if (p) {
+ r = manager_send_changed_strv(m, p);
if (r < 0)
- log_error_errno(r, "Could not emit changed OperationalState: %m");
+ log_error_errno(r, "Could not emit changed properties: %m");
}
m->dirty = false;
return 1;
}
-Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr) {
- assert_return(m, NULL);
- assert_return(addr, NULL);
-
- return hashmap_get(m->dhcp6_prefixes, addr);
-}
-
-static int dhcp6_route_add_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
- int r;
-
- assert(link);
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0 && r != -EEXIST)
- log_link_debug_errno(link, r, "Received error adding DHCPv6 Prefix Delegation route: %m");
-
- return 0;
-}
-
-int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link) {
- _cleanup_free_ struct in6_addr *a = NULL;
- _cleanup_free_ char *buf = NULL;
- Link *assigned_link;
- Route *route;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(addr, -EINVAL);
-
- r = route_add(link, AF_INET6, (union in_addr_union *) addr, 64,
- 0, 0, 0, &route);
- if (r < 0)
- return r;
-
- r = route_configure(route, link, dhcp6_route_add_handler);
- if (r < 0)
- return r;
-
- (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
- log_link_debug(link, "Adding prefix route %s/64", strnull(buf));
-
- assigned_link = hashmap_get(m->dhcp6_prefixes, addr);
- if (assigned_link) {
- assert(assigned_link == link);
- return 0;
- }
-
- a = newdup(struct in6_addr, addr, 1);
- if (!a)
- return -ENOMEM;
-
- r = hashmap_ensure_allocated(&m->dhcp6_prefixes, &in6_addr_hash_ops);
- if (r < 0)
- return r;
-
- r = hashmap_put(m->dhcp6_prefixes, a, link);
- if (r < 0)
- return r;
-
- TAKE_PTR(a);
- link_ref(link);
- return 0;
-}
-
-static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Link *link) {
- int r;
-
- assert(link);
-
- r = sd_netlink_message_get_errno(m);
- if (r < 0)
- log_link_debug_errno(link, r, "Received error on DHCPv6 Prefix Delegation route removal: %m");
-
- return 1;
-}
-
-static int manager_dhcp6_prefix_remove(Manager *m, struct in6_addr *addr) {
- _cleanup_free_ struct in6_addr *a = NULL;
- _cleanup_(link_unrefp) Link *l = NULL;
- _cleanup_free_ char *buf = NULL;
- Route *route;
- int r;
-
- assert_return(m, -EINVAL);
- assert_return(addr, -EINVAL);
-
- l = hashmap_remove2(m->dhcp6_prefixes, addr, (void **) &a);
- if (!l)
- return -EINVAL;
-
- (void) sd_radv_remove_prefix(l->radv, addr, 64);
- r = route_get(l, AF_INET6, (union in_addr_union *) addr, 64, 0, 0, 0, &route);
- if (r < 0)
- return r;
-
- r = route_remove(route, l, dhcp6_route_remove_handler);
- if (r < 0)
- return r;
-
- (void) in_addr_to_string(AF_INET6, (union in_addr_union *) addr, &buf);
- log_link_debug(l, "Removing prefix route %s/64", strnull(buf));
-
- return 0;
-}
-
-int manager_dhcp6_prefix_remove_all(Manager *m, Link *link) {
- struct in6_addr *addr;
- Iterator i;
- Link *l;
-
- assert_return(m, -EINVAL);
- assert_return(link, -EINVAL);
-
- HASHMAP_FOREACH_KEY(l, addr, m->dhcp6_prefixes, i)
- if (l == link)
- (void) manager_dhcp6_prefix_remove(m, addr);
-
- return 0;
-}
-
int manager_new(Manager **ret) {
_cleanup_(manager_freep) Manager *m = NULL;
int r;
if (!m->state_file)
return -ENOMEM;
- m->sysctl_ipv6_enabled = -1;
-
r = sd_event_default(&m->event);
if (r < 0)
return r;
free(m->state_file);
while ((a = hashmap_first_key(m->dhcp6_prefixes)))
- (void) manager_dhcp6_prefix_remove(m, a);
+ (void) dhcp6_prefix_remove(m, a);
hashmap_free(m->dhcp6_prefixes);
while ((link = hashmap_steal_first(m->links))) {
if (link->dhcp6_client)
(void) dhcp6_lease_pd_prefix_lost(link->dhcp6_client, link);
- link_stop_clients(link);
+ (void) link_stop_clients(link, true);
link_unref(link);
}
return 0;
}
-
-int manager_sysctl_ipv6_enabled(Manager *manager) {
- _cleanup_free_ char *value = NULL;
- int r;
-
- if (manager->sysctl_ipv6_enabled >= 0)
- return manager->sysctl_ipv6_enabled;
-
- r = sysctl_read_ip_property(AF_INET6, "all", "disable_ipv6", &value);
- if (r < 0)
- return log_warning_errno(r, "Failed to read net.ipv6.conf.all.disable_ipv6 sysctl property: %m");
-
- manager->sysctl_ipv6_enabled = value[0] == '0';
- return manager->sysctl_ipv6_enabled;
-}
char *state_file;
LinkOperationalState operational_state;
+ LinkCarrierState carrier_state;
+ LinkAddressState address_state;
Hashmap *links;
Hashmap *netdevs;
Set *rules_foreign;
Set *rules_saved;
- int sysctl_ipv6_enabled;
-
/* For link speed meter*/
bool use_speed_meter;
sd_event_source *speed_meter_event_source;
int manager_rtnl_process_route(sd_netlink *nl, sd_netlink_message *message, void *userdata);
int manager_rtnl_process_rule(sd_netlink *nl, sd_netlink_message *message, void *userdata);
-int manager_send_changed(Manager *m, const char *property, ...) _sentinel_;
+int manager_send_changed_strv(Manager *m, char **properties);
void manager_dirty(Manager *m);
int manager_address_pool_acquire(Manager *m, int family, unsigned prefixlen, union in_addr_union *found);
int manager_set_timezone(Manager *m, const char *timezone);
int manager_request_product_uuid(Manager *m, Link *link);
-Link *manager_dhcp6_prefix_get(Manager *m, struct in6_addr *addr);
-int manager_dhcp6_prefix_add(Manager *m, struct in6_addr *addr, Link *link);
-int manager_dhcp6_prefix_remove_all(Manager *m, Link *link);
-
-int manager_sysctl_ipv6_enabled(Manager *manager);
-
DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free);
#include "sd-ndisc.h"
#include "missing_network.h"
+#include "networkd-dhcp6.h"
#include "networkd-ndisc.h"
#include "networkd-route.h"
#include "strv.h"
link_enter_failed(link);
return r;
}
-
- link->ndisc_messages++;
+ if (r > 0)
+ link->ndisc_messages++;
return 0;
}
link_enter_failed(link);
return r;
}
-
- link->ndisc_messages++;
+ if (r > 0)
+ link->ndisc_messages++;
return 0;
}
link_enter_failed(link);
return r;
}
-
- link->ndisc_messages++;
+ if (r > 0)
+ link->ndisc_messages++;
return 0;
}
link_enter_failed(link);
return r;
}
-
- link->ndisc_messages++;
+ if (r > 0)
+ link->ndisc_messages++;
return 0;
}
continue;
}
+ if (set_contains(network->ndisc_black_listed_prefix, &ip.in6))
+ continue;
+
r = set_ensure_allocated(&network->ndisc_black_listed_prefix, &in6_addr_hash_ops);
if (r < 0)
return log_oom();
r = set_put(network->ndisc_black_listed_prefix, a);
if (r < 0) {
- if (r == -EEXIST)
- log_syntax(unit, LOG_WARNING, filename, line, r,
- "NDISC black listed prefixs is duplicated, ignoring assignment: %s", n);
- else
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to store NDISC black listed prefix '%s', ignoring assignment: %m", n);
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to store NDISC black listed prefix '%s', ignoring assignment: %m", n);
continue;
}
#include "alloc-util.h"
#include "ether-addr-util.h"
#include "networkd-manager.h"
+#include "networkd-network-bus.h"
#include "string-util.h"
#include "strv.h"
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "sd-bus.h"
+
+typedef struct Link Link;
+
+extern const sd_bus_vtable network_vtable[];
+
+int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
+int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
#include "conf-parser.h"
#include "network-internal.h"
#include "networkd-conf.h"
+#include "networkd-dhcp-common.h"
+#include "networkd-dhcp-server.h"
+#include "networkd-dhcp4.h"
+#include "networkd-ipv4ll.h"
#include "networkd-ndisc.h"
#include "networkd-network.h"
#include "vlan-util.h"
%includes
%%
Match.MACAddress, config_parse_hwaddrs, 0, offsetof(Network, match_mac)
-Match.Path, config_parse_strv, 0, offsetof(Network, match_path)
-Match.Driver, config_parse_strv, 0, offsetof(Network, match_driver)
-Match.Type, config_parse_strv, 0, offsetof(Network, match_type)
-Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name)
+Match.Path, config_parse_match_strv, 0, offsetof(Network, match_path)
+Match.Driver, config_parse_match_strv, 0, offsetof(Network, match_driver)
+Match.Type, config_parse_match_strv, 0, offsetof(Network, match_type)
+Match.Name, config_parse_match_ifnames, 0, offsetof(Network, match_name)
+Match.Property, config_parse_match_property, 0, offsetof(Network, match_property)
Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, conditions)
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions)
Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions)
Network.L2TP, config_parse_stacked_netdev, NETDEV_KIND_L2TP, offsetof(Network, stacked_netdev_names)
Network.MACsec, config_parse_stacked_netdev, NETDEV_KIND_MACSEC, offsetof(Network, stacked_netdev_names)
Network.Tunnel, config_parse_stacked_netdev, _NETDEV_KIND_TUNNEL, offsetof(Network, stacked_netdev_names)
+Network.Xfrm, config_parse_stacked_netdev, NETDEV_KIND_XFRM, offsetof(Network, stacked_netdev_names)
Network.VRF, config_parse_ifname, 0, offsetof(Network, vrf_name)
Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp)
Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server)
Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier)
Network.ConfigureWithoutCarrier, config_parse_bool, 0, offsetof(Network, configure_without_carrier)
Network.IgnoreCarrierLoss, config_parse_bool, 0, offsetof(Network, ignore_carrier_loss)
+Network.KeepConfiguration, config_parse_keep_configuration, 0, offsetof(Network, keep_configuration)
Address.Address, config_parse_address, 0, 0
Address.Peer, config_parse_address, 0, 0
Address.Broadcast, config_parse_broadcast, 0, 0
Route.QuickAck, config_parse_quickack, 0, 0
Route.FastOpenNoCookie, config_parse_fast_open_no_cookie, 0, 0
Route.TTLPropagate, config_parse_route_ttl_propagate, 0, 0
-DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
-DHCP.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
-DHCP.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp)
-DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
-DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
-DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
-DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
-DHCP.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
-DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
-DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
-DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
-DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
-DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
-DHCP.MaxAttempts, config_parse_dhcp_max_attempts, 0, 0
-DHCP.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class)
-DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid)
-DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid)
-DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
-DHCP.RouteTable, config_parse_section_route_table, 0, 0
-DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
-DHCP.IAID, config_parse_iaid, 0, 0
-DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
-DHCP.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release)
-DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
-DHCP.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0
-DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
+DHCPv4.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
+DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
+DHCPv4.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp_use_ntp)
+DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
+DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
+DHCPv4.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
+DHCPv4.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
+DHCPv4.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
+DHCPv4.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
+DHCPv4.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
+DHCPv4.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
+DHCPv4.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
+DHCPv4.MaxAttempts, config_parse_dhcp_max_attempts, 0, 0
+DHCPv4.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class)
+DHCPv4.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid)
+DHCPv4.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid)
+DHCPv4.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
+DHCPv4.RouteTable, config_parse_section_route_table, 0, 0
+DHCPv4.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
+DHCPv4.IAID, config_parse_iaid, 0, 0
+DHCPv4.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
+DHCPv4.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release)
+DHCPv4.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0
+DHCPv6.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp6_use_dns)
+DHCPv6.UseNTP, config_parse_bool, 0, offsetof(Network, dhcp6_use_ntp)
+DHCPv6.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
+DHCPv6.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix)
IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix)
IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns)
CAN.TripleSampling, config_parse_tristate, 0, offsetof(Network, can_triple_sampling)
/* backwards compatibility: do not add new entries to this section */
Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local)
-DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns)
-DHCPv4.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
-DHCPv4.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
+DHCP.ClientIdentifier, config_parse_dhcp_client_identifier, 0, offsetof(Network, dhcp_client_identifier)
+DHCP.UseDNS, config_parse_dhcp_use_dns, 0, 0
+DHCP.UseNTP, config_parse_dhcp_use_ntp, 0, 0
+DHCP.UseMTU, config_parse_bool, 0, offsetof(Network, dhcp_use_mtu)
+DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
+DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
DHCP.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
+DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
+DHCP.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
+DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
+DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
+DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)
+DHCP.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
+DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier)
+DHCP.MaxAttempts, config_parse_dhcp_max_attempts, 0, 0
+DHCP.UserClass, config_parse_dhcp_user_class, 0, offsetof(Network, dhcp_user_class)
+DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid)
+DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid)
+DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric)
+DHCP.RouteTable, config_parse_section_route_table, 0, 0
+DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone)
+DHCP.IAID, config_parse_iaid, 0, 0
+DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port)
+DHCP.SendRelease, config_parse_bool, 0, offsetof(Network, dhcp_send_release)
+DHCP.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0
+DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit)
+DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information)
DHCPv4.UseDomainName, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
-DHCPv4.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical)
+DHCPv4.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical)
if (set_isempty(network->match_mac) && strv_isempty(network->match_path) &&
strv_isempty(network->match_driver) && strv_isempty(network->match_type) &&
- strv_isempty(network->match_name) && !network->conditions)
+ strv_isempty(network->match_name) && strv_isempty(network->match_property) &&
+ !network->conditions)
log_warning("%s: No valid settings found in the [Match] section. "
"The file will match all interfaces. "
"If that is intended, please add Name=* in the [Match] section.",
network->dhcp_use_mtu = false;
}
+ if (network->dhcp_critical >= 0) {
+ if (network->keep_configuration >= 0)
+ log_warning("%s: Both KeepConfiguration= and deprecated CriticalConnection= are set. "
+ "Ignoring CriticalConnection=.", network->filename);
+ else if (network->dhcp_critical)
+ /* CriticalConnection=yes also preserve foreign static configurations. */
+ network->keep_configuration = KEEP_CONFIGURATION_YES;
+ else
+ /* For backward compatibility, we do not release DHCP addresses on manager stop. */
+ network->keep_configuration = KEEP_CONFIGURATION_DHCP_ON_STOP;
+ }
+
+ if (network->keep_configuration < 0)
+ /* For backward compatibility, we do not release DHCP addresses on manager stop. */
+ network->keep_configuration = KEEP_CONFIGURATION_DHCP_ON_STOP;
+
LIST_FOREACH_SAFE(addresses, address, address_next, network->static_addresses)
if (address_section_verify(address) < 0)
address_free(address);
.required_for_online = true,
.required_operstate_for_online = LINK_OPERSTATE_DEGRADED,
.dhcp = ADDRESS_FAMILY_NO,
+ .dhcp_critical = -1,
.dhcp_use_ntp = true,
.dhcp_use_dns = true,
.dhcp_use_hostname = true,
.dhcp_use_timezone = false,
.rapid_commit = true,
+ .dhcp6_use_ntp = true,
+ .dhcp6_use_dns = true,
+
.dhcp_server_emit_dns = true,
.dhcp_server_emit_ntp = true,
.dhcp_server_emit_router = true,
.ipv6_accept_ra_route_table = RT_TABLE_MAIN,
.ipv6_accept_ra_route_table_set = false,
+ .keep_configuration = _KEEP_CONFIGURATION_INVALID,
+
.can_triple_sampling = -1,
};
"Route\0"
"DHCP\0"
"DHCPv4\0" /* compat */
+ "DHCPv6\0"
"DHCPServer\0"
"IPv6AcceptRA\0"
"IPv6NDPProxyAddress\0"
strv_free(network->match_driver);
strv_free(network->match_type);
strv_free(network->match_name);
+ strv_free(network->match_property);
condition_free_list(network->conditions);
free(network->description);
int network_get(Manager *manager, sd_device *device,
const char *ifname, const struct ether_addr *address,
Network **ret) {
- const char *path = NULL, *driver = NULL, *devtype = NULL;
Network *network;
Iterator i;
assert(manager);
assert(ret);
- if (device) {
- (void) sd_device_get_property_value(device, "ID_PATH", &path);
-
- (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &driver);
-
- (void) sd_device_get_devtype(device, &devtype);
- }
-
ORDERED_HASHMAP_FOREACH(network, manager->networks, i)
- if (net_match_config(network->match_mac, network->match_path,
- network->match_driver, network->match_type,
- network->match_name,
- address, path, driver, devtype, ifname)) {
+ if (net_match_config(network->match_mac, network->match_path, network->match_driver,
+ network->match_type, network->match_name, network->match_property,
+ device, address, ifname)) {
if (network->match_name && device) {
const char *attr;
uint8_t name_assign_type = NET_NAME_UNKNOWN;
assert(IN_SET(kind,
NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP,
NETDEV_KIND_IPVLAN, NETDEV_KIND_IPVTAP, NETDEV_KIND_VXLAN,
- NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL));
+ NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, _NETDEV_KIND_TUNNEL,
+ NETDEV_KIND_XFRM));
if (!ifname_valid(rvalue)) {
log_syntax(unit, LOG_ERR, filename, line, 0,
return 0;
}
-int config_parse_ipv4ll(
- const char* unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- AddressFamilyBoolean *link_local = data;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- /* Note that this is mostly like
- * config_parse_address_family_boolean(), except that it
- * applies only to IPv4 */
-
- r = parse_boolean(rvalue);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse %s=%s, ignoring assignment. "
- "Note that the setting %s= is deprecated, please use LinkLocalAddressing= instead.",
- lvalue, rvalue, lvalue);
- return 0;
- }
-
- SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, r);
-
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "%s=%s is deprecated, please use LinkLocalAddressing=%s instead.",
- lvalue, rvalue, address_family_boolean_to_string(*link_local));
-
- return 0;
-}
-
-int config_parse_dhcp(
- const char* unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- AddressFamilyBoolean *dhcp = data, s;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- /* Note that this is mostly like
- * config_parse_address_family_boolean(), except that it
- * understands some old names for the enum values */
-
- s = address_family_boolean_from_string(rvalue);
- if (s < 0) {
-
- /* Previously, we had a slightly different enum here,
- * support its values for compatibility. */
-
- if (streq(rvalue, "none"))
- s = ADDRESS_FAMILY_NO;
- else if (streq(rvalue, "v4"))
- s = ADDRESS_FAMILY_IPV4;
- else if (streq(rvalue, "v6"))
- s = ADDRESS_FAMILY_IPV6;
- else if (streq(rvalue, "both"))
- s = ADDRESS_FAMILY_YES;
- else {
- log_syntax(unit, LOG_ERR, filename, line, 0,
- "Failed to parse DHCP option, ignoring: %s", rvalue);
- return 0;
- }
-
- log_syntax(unit, LOG_WARNING, filename, line, 0,
- "DHCP=%s is deprecated, please use DHCP=%s instead.",
- rvalue, address_family_boolean_to_string(s));
- }
-
- *dhcp = s;
- return 0;
-}
-
-static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = {
- [DHCP_CLIENT_ID_MAC] = "mac",
- [DHCP_CLIENT_ID_DUID] = "duid",
- [DHCP_CLIENT_ID_DUID_ONLY] = "duid-only",
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DHCPClientIdentifier);
-DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DHCPClientIdentifier,
- "Failed to parse client identifier type");
-
int config_parse_ipv6token(
const char* unit,
const char *filename,
return free_and_replace(*datap, tz);
}
-int config_parse_dhcp_server_dns(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *n = data;
- const char *p = rvalue;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- for (;;) {
- _cleanup_free_ char *w = NULL;
- union in_addr_union a;
- struct in_addr *m;
-
- r = extract_first_word(&p, &w, NULL, 0);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to extract word, ignoring: %s", rvalue);
- return 0;
- }
- if (r == 0)
- break;
-
- r = in_addr_from_string(AF_INET, w, &a);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse DNS server address '%s', ignoring assignment: %m", w);
- continue;
- }
-
- m = reallocarray(n->dhcp_server_dns, n->n_dhcp_server_dns + 1, sizeof(struct in_addr));
- if (!m)
- return log_oom();
-
- m[n->n_dhcp_server_dns++] = a.in;
- n->dhcp_server_dns = m;
- }
-
- return 0;
-}
-
-int config_parse_radv_dns(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *n = data;
- const char *p = rvalue;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- for (;;) {
- _cleanup_free_ char *w = NULL;
- union in_addr_union a;
-
- r = extract_first_word(&p, &w, NULL, 0);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to extract word, ignoring: %s", rvalue);
- return 0;
- }
- if (r == 0)
- break;
-
- if (in_addr_from_string(AF_INET6, w, &a) >= 0) {
- struct in6_addr *m;
-
- m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
- if (!m)
- return log_oom();
-
- m[n->n_router_dns++] = a.in6;
- n->router_dns = m;
-
- } else
- log_syntax(unit, LOG_ERR, filename, line, 0,
- "Failed to parse DNS server address, ignoring: %s", w);
- }
-
- return 0;
-}
-
-int config_parse_radv_search_domains(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *n = data;
- const char *p = rvalue;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- for (;;) {
- _cleanup_free_ char *w = NULL, *idna = NULL;
-
- r = extract_first_word(&p, &w, NULL, 0);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to extract word, ignoring: %s", rvalue);
- return 0;
- }
- if (r == 0)
- break;
-
- r = dns_name_apply_idna(w, &idna);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
- continue;
- } else if (r == 0)
- /* transfer ownership to simplify subsequent operations */
- idna = TAKE_PTR(w);
-
- r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops);
- if (r < 0)
- return r;
-
- r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
- if (r < 0)
- return r;
- }
-
- return 0;
-}
-
-int config_parse_dhcp_server_ntp(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *n = data;
- const char *p = rvalue;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
-
- for (;;) {
- _cleanup_free_ char *w = NULL;
- union in_addr_union a;
- struct in_addr *m;
-
- r = extract_first_word(&p, &w, NULL, 0);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to extract word, ignoring: %s", rvalue);
- return 0;
- }
- if (r == 0)
- return 0;
-
- r = in_addr_from_string(AF_INET, w, &a);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse NTP server address '%s', ignoring: %m", w);
- continue;
- }
-
- m = reallocarray(n->dhcp_server_ntp, n->n_dhcp_server_ntp + 1, sizeof(struct in_addr));
- if (!m)
- return log_oom();
-
- m[n->n_dhcp_server_ntp++] = a.in;
- n->dhcp_server_ntp = m;
- }
-}
-
int config_parse_dns(
const char *unit,
const char *filename,
return 0;
}
-int config_parse_dhcp_user_class(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- char ***l = data;
- int r;
-
- assert(l);
- assert(lvalue);
- assert(rvalue);
-
- if (isempty(rvalue)) {
- *l = strv_free(*l);
- return 0;
- }
-
- for (;;) {
- _cleanup_free_ char *w = NULL;
-
- r = extract_first_word(&rvalue, &w, NULL, 0);
- if (r == -ENOMEM)
- return log_oom();
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to split user classes option, ignoring: %s", rvalue);
- break;
- }
- if (r == 0)
- break;
-
- if (strlen(w) > 255) {
- log_syntax(unit, LOG_ERR, filename, line, 0,
- "%s length is not in the range 1-255, ignoring.", w);
- continue;
- }
-
- r = strv_push(l, w);
- if (r < 0)
- return log_oom();
-
- w = NULL;
- }
-
- return 0;
-}
-
-int config_parse_section_route_table(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *network = data;
- uint32_t rt;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = safe_atou32(rvalue, &rt);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue);
- return 0;
- }
-
- if (streq_ptr(section, "DHCP")) {
- network->dhcp_route_table = rt;
- network->dhcp_route_table_set = true;
- } else { /* section is IPv6AcceptRA */
- network->ipv6_accept_ra_route_table = rt;
- network->ipv6_accept_ra_route_table_set = true;
- }
-
- return 0;
-}
-
-int config_parse_dhcp_max_attempts(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *network = data;
- uint64_t a;
- int r;
-
- assert(network);
- assert(lvalue);
- assert(rvalue);
-
- if (isempty(rvalue)) {
- network->dhcp_max_attempts = 0;
- return 0;
- }
-
- if (streq(rvalue, "infinity")) {
- network->dhcp_max_attempts = (uint64_t) -1;
- return 0;
- }
-
- r = safe_atou64(rvalue, &a);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse DHCP maximum attempts, ignoring: %s", rvalue);
- return 0;
- }
-
- if (a == 0) {
- log_syntax(unit, LOG_ERR, filename, line, 0,
- "%s= must be positive integer or 'infinity', ignoring: %s", lvalue, rvalue);
- return 0;
- }
-
- network->dhcp_max_attempts = a;
-
- return 0;
-}
-
-int config_parse_dhcp_black_listed_ip_address(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *network = data;
- const char *p;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (isempty(rvalue)) {
- network->dhcp_black_listed_ip = set_free(network->dhcp_black_listed_ip);
- return 0;
- }
-
- for (p = rvalue;;) {
- _cleanup_free_ char *n = NULL;
- union in_addr_union ip;
-
- r = extract_first_word(&p, &n, NULL, 0);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to parse DHCP black listed ip address, ignoring assignment: %s",
- rvalue);
- return 0;
- }
- if (r == 0)
- return 0;
-
- r = in_addr_from_string(AF_INET, n, &ip);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "DHCP black listed ip address is invalid, ignoring assignment: %s", n);
- continue;
- }
-
- r = set_ensure_allocated(&network->dhcp_black_listed_ip, NULL);
- if (r < 0)
- return log_oom();
-
- r = set_put(network->dhcp_black_listed_ip, UINT32_TO_PTR(ip.in.s_addr));
- if (r < 0)
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Failed to store DHCP black listed ip address '%s', ignoring assignment: %m", n);
- }
-
- return 0;
-}
-
-DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains,
- "Failed to parse DHCP use domains setting");
-
-static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = {
- [DHCP_USE_DOMAINS_NO] = "no",
- [DHCP_USE_DOMAINS_ROUTE] = "route",
- [DHCP_USE_DOMAINS_YES] = "yes",
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dhcp_use_domains, DHCPUseDomains, DHCP_USE_DOMAINS_YES);
-
-int config_parse_iaid(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
- Network *network = data;
- uint32_t iaid;
- int r;
-
- assert(filename);
- assert(lvalue);
- assert(rvalue);
- assert(network);
-
- r = safe_atou32(rvalue, &iaid);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r,
- "Unable to read IAID, ignoring assignment: %s", rvalue);
- return 0;
- }
-
- network->iaid = iaid;
- network->iaid_set = true;
-
- return 0;
-}
-
int config_parse_required_for_online(
const char *unit,
const char *filename,
return 0;
}
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_keep_configuration, keep_configuration, KeepConfiguration,
+ "Failed to parse KeepConfiguration= setting");
+
+static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = {
+ [KEEP_CONFIGURATION_NO] = "no",
+ [KEEP_CONFIGURATION_DHCP_ON_STOP] = "dhcp-on-stop",
+ [KEEP_CONFIGURATION_DHCP] = "dhcp",
+ [KEEP_CONFIGURATION_STATIC] = "static",
+ [KEEP_CONFIGURATION_YES] = "yes",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES);
#include "condition.h"
#include "conf-parser.h"
-#include "dhcp-identifier.h"
#include "hashmap.h"
#include "netdev/bridge.h"
#include "netdev/netdev.h"
#include "networkd-address-label.h"
#include "networkd-address.h"
#include "networkd-brvlan.h"
+#include "networkd-dhcp-common.h"
+#include "networkd-dhcp4.h"
#include "networkd-fdb.h"
#include "networkd-ipv6-proxy-ndp.h"
#include "networkd-lldp-rx.h"
#include "ordered-set.h"
#include "resolve-util.h"
-#define DHCP_ROUTE_METRIC 1024
-#define IPV4LL_ROUTE_METRIC 2048
-
-#define BRIDGE_VLAN_BITMAP_MAX 4096
-#define BRIDGE_VLAN_BITMAP_LEN (BRIDGE_VLAN_BITMAP_MAX / 32)
-
-typedef enum DHCPClientIdentifier {
- DHCP_CLIENT_ID_MAC,
- DHCP_CLIENT_ID_DUID,
- /* The following option may not be good for RFC regarding DHCP (3315 and 4361).
- * But some setups require this. E.g., Sky Broadband, the second largest provider in the UK
- * requires the client id to be set to a custom string, reported at
- * https://github.com/systemd/systemd/issues/7828 */
- DHCP_CLIENT_ID_DUID_ONLY,
- _DHCP_CLIENT_ID_MAX,
- _DHCP_CLIENT_ID_INVALID = -1,
-} DHCPClientIdentifier;
-
typedef enum IPv6PrivacyExtensions {
/* The values map to the kernel's /proc/sys/net/ipv6/conf/xxx/use_tempaddr values */
IPV6_PRIVACY_EXTENSIONS_NO,
_IPV6_PRIVACY_EXTENSIONS_INVALID = -1,
} IPv6PrivacyExtensions;
-typedef enum DHCPUseDomains {
- DHCP_USE_DOMAINS_NO,
- DHCP_USE_DOMAINS_YES,
- DHCP_USE_DOMAINS_ROUTE,
- _DHCP_USE_DOMAINS_MAX,
- _DHCP_USE_DOMAINS_INVALID = -1,
-} DHCPUseDomains;
-
-typedef struct DUID {
- /* Value of Type in [DHCP] section */
- DUIDType type;
-
- uint8_t raw_data_len;
- uint8_t raw_data[MAX_DUID_LEN];
- usec_t llt_time;
-} DUID;
-
-typedef enum RADVPrefixDelegation {
- RADV_PREFIX_DELEGATION_NONE,
- RADV_PREFIX_DELEGATION_STATIC,
- RADV_PREFIX_DELEGATION_DHCP6,
- RADV_PREFIX_DELEGATION_BOTH,
- _RADV_PREFIX_DELEGATION_MAX,
- _RADV_PREFIX_DELEGATION_INVALID = -1,
-} RADVPrefixDelegation;
+typedef enum KeepConfiguration {
+ KEEP_CONFIGURATION_NO = 0,
+ KEEP_CONFIGURATION_DHCP_ON_START = 1 << 0,
+ KEEP_CONFIGURATION_DHCP_ON_STOP = 1 << 1,
+ KEEP_CONFIGURATION_DHCP = KEEP_CONFIGURATION_DHCP_ON_START | KEEP_CONFIGURATION_DHCP_ON_STOP,
+ KEEP_CONFIGURATION_STATIC = 1 << 2,
+ KEEP_CONFIGURATION_YES = KEEP_CONFIGURATION_DHCP | KEEP_CONFIGURATION_STATIC,
+ _KEEP_CONFIGURATION_MAX,
+ _KEEP_CONFIGURATION_INVALID = -1,
+} KeepConfiguration;
typedef struct Manager Manager;
char **match_driver;
char **match_type;
char **match_name;
+ char **match_property;
LIST_HEAD(Condition, conditions);
char *description;
NetDev *bridge;
NetDev *bond;
NetDev *vrf;
+ NetDev *xfrm;
Hashmap *stacked_netdevs;
char *bridge_name;
char *bond_name;
bool dhcp_anonymize;
bool dhcp_send_hostname;
bool dhcp_broadcast;
- bool dhcp_critical;
+ int dhcp_critical;
bool dhcp_use_dns;
bool dhcp_use_ntp;
bool dhcp_use_mtu;
DHCPUseDomains dhcp_use_domains;
Set *dhcp_black_listed_ip;
+ /* DHCPv6 Client support*/
+ bool dhcp6_use_dns;
+ bool dhcp6_use_ntp;
+
/* DHCP Server Support */
bool dhcp_server;
bool dhcp_server_emit_dns;
bool unmanaged;
bool configure_without_carrier;
bool ignore_carrier_loss;
+ KeepConfiguration keep_configuration;
uint32_t iaid;
DUID duid;
bool network_has_static_ipv6_addresses(Network *network);
CONFIG_PARSER_PROTOTYPE(config_parse_stacked_netdev);
-CONFIG_PARSER_PROTOTYPE(config_parse_domains);
CONFIG_PARSER_PROTOTYPE(config_parse_tunnel);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp);
-CONFIG_PARSER_PROTOTYPE(config_parse_dns);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6token);
CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_privacy_extensions);
+CONFIG_PARSER_PROTOTYPE(config_parse_domains);
+CONFIG_PARSER_PROTOTYPE(config_parse_dns);
CONFIG_PARSER_PROTOTYPE(config_parse_hostname);
CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_dns);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_black_listed_ip_address);
-CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns);
-CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_ntp);
CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_negative_trust_anchors);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains);
-CONFIG_PARSER_PROTOTYPE(config_parse_section_route_table);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class);
CONFIG_PARSER_PROTOTYPE(config_parse_ntp);
-CONFIG_PARSER_PROTOTYPE(config_parse_iaid);
CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online);
-CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_max_attempts);
-/* Legacy IPv4LL support */
-CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll);
+CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration);
const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
-extern const sd_bus_vtable network_vtable[];
-
-int network_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error);
-int network_object_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **found, sd_bus_error *error);
-
const char* ipv6_privacy_extensions_to_string(IPv6PrivacyExtensions i) _const_;
IPv6PrivacyExtensions ipv6_privacy_extensions_from_string(const char *s) _pure_;
-const char* dhcp_use_domains_to_string(DHCPUseDomains p) _const_;
-DHCPUseDomains dhcp_use_domains_from_string(const char *s) _pure_;
-
-const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_;
-RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_;
+const char* keep_configuration_to_string(KeepConfiguration i) _const_;
+KeepConfiguration keep_configuration_from_string(const char *s) _pure_;
#include <netinet/icmp6.h>
#include <arpa/inet.h>
+#include "dns-domain.h"
#include "networkd-address.h"
#include "networkd-manager.h"
#include "networkd-radv.h"
#include "string-table.h"
#include "strv.h"
-static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
- [RADV_PREFIX_DELEGATION_NONE] = "no",
- [RADV_PREFIX_DELEGATION_STATIC] = "static",
- [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
- [RADV_PREFIX_DELEGATION_BOTH] = "yes",
-};
-
-DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
- radv_prefix_delegation,
- RADVPrefixDelegation,
- RADV_PREFIX_DELEGATION_BOTH);
-
-int config_parse_router_prefix_delegation(
- const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
-
- Network *network = userdata;
- RADVPrefixDelegation d;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- d = radv_prefix_delegation_from_string(rvalue);
- if (d < 0) {
- log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid router prefix delegation '%s', ignoring assignment.", rvalue);
- return 0;
- }
-
- network->router_prefix_delegation = d;
-
- return 0;
-}
-
-int config_parse_router_preference(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
- Network *network = userdata;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- if (streq(rvalue, "high"))
- network->router_preference = SD_NDISC_PREFERENCE_HIGH;
- else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
- network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
- else if (streq(rvalue, "low"))
- network->router_preference = SD_NDISC_PREFERENCE_LOW;
- else
- log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue);
-
- return 0;
-}
-
void prefix_free(Prefix *prefix) {
if (!prefix)
return;
return radv_emit_dns(link);
}
+
+int config_parse_radv_dns(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *n = data;
+ const char *p = rvalue;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ for (;;) {
+ _cleanup_free_ char *w = NULL;
+ union in_addr_union a;
+
+ r = extract_first_word(&p, &w, NULL, 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to extract word, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ break;
+
+ if (in_addr_from_string(AF_INET6, w, &a) >= 0) {
+ struct in6_addr *m;
+
+ m = reallocarray(n->router_dns, n->n_router_dns + 1, sizeof(struct in6_addr));
+ if (!m)
+ return log_oom();
+
+ m[n->n_router_dns++] = a.in6;
+ n->router_dns = m;
+
+ } else
+ log_syntax(unit, LOG_ERR, filename, line, 0,
+ "Failed to parse DNS server address, ignoring: %s", w);
+ }
+
+ return 0;
+}
+
+int config_parse_radv_search_domains(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *n = data;
+ const char *p = rvalue;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ for (;;) {
+ _cleanup_free_ char *w = NULL, *idna = NULL;
+
+ r = extract_first_word(&p, &w, NULL, 0);
+ if (r == -ENOMEM)
+ return log_oom();
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to extract word, ignoring: %s", rvalue);
+ return 0;
+ }
+ if (r == 0)
+ break;
+
+ r = dns_name_apply_idna(w, &idna);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r,
+ "Failed to apply IDNA to domain name '%s', ignoring: %m", w);
+ continue;
+ } else if (r == 0)
+ /* transfer ownership to simplify subsequent operations */
+ idna = TAKE_PTR(w);
+
+ r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna));
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static const char * const radv_prefix_delegation_table[_RADV_PREFIX_DELEGATION_MAX] = {
+ [RADV_PREFIX_DELEGATION_NONE] = "no",
+ [RADV_PREFIX_DELEGATION_STATIC] = "static",
+ [RADV_PREFIX_DELEGATION_DHCP6] = "dhcpv6",
+ [RADV_PREFIX_DELEGATION_BOTH] = "yes",
+};
+
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(
+ radv_prefix_delegation,
+ RADVPrefixDelegation,
+ RADV_PREFIX_DELEGATION_BOTH);
+
+int config_parse_router_prefix_delegation(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ Network *network = userdata;
+ RADVPrefixDelegation d;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ d = radv_prefix_delegation_from_string(rvalue);
+ if (d < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid router prefix delegation '%s', ignoring assignment.", rvalue);
+ return 0;
+ }
+
+ network->router_prefix_delegation = d;
+
+ return 0;
+}
+
+int config_parse_router_preference(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Network *network = userdata;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (streq(rvalue, "high"))
+ network->router_preference = SD_NDISC_PREFERENCE_HIGH;
+ else if (STR_IN_SET(rvalue, "medium", "normal", "default"))
+ network->router_preference = SD_NDISC_PREFERENCE_MEDIUM;
+ else if (streq(rvalue, "low"))
+ network->router_preference = SD_NDISC_PREFERENCE_LOW;
+ else
+ log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Router preference '%s' is invalid, ignoring assignment: %m", rvalue);
+
+ return 0;
+}
typedef struct Prefix Prefix;
+typedef enum RADVPrefixDelegation {
+ RADV_PREFIX_DELEGATION_NONE,
+ RADV_PREFIX_DELEGATION_STATIC,
+ RADV_PREFIX_DELEGATION_DHCP6,
+ RADV_PREFIX_DELEGATION_BOTH,
+ _RADV_PREFIX_DELEGATION_MAX,
+ _RADV_PREFIX_DELEGATION_INVALID = -1,
+} RADVPrefixDelegation;
+
struct Prefix {
Network *network;
NetworkConfigSection *section;
DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free);
+int radv_emit_dns(Link *link);
+int radv_configure(Link *link);
+
+const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_;
+RADVPrefixDelegation radv_prefix_delegation_from_string(const char *s) _pure_;
+
CONFIG_PARSER_PROTOTYPE(config_parse_router_prefix_delegation);
CONFIG_PARSER_PROTOTYPE(config_parse_router_preference);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_flags);
CONFIG_PARSER_PROTOTYPE(config_parse_prefix_lifetime);
-
-int radv_emit_dns(Link *link);
-int radv_configure(Link *link);
+CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns);
+CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains);
#include "in-addr-util.h"
#include "missing_network.h"
#include "netlink-util.h"
+#include "networkd-ipv4ll.h"
#include "networkd-manager.h"
#include "networkd-route.h"
#include "parse-util.h"
assert(IN_SET(route->family, AF_INET, AF_INET6));
assert(callback);
+ if (route->family == AF_INET6 && link_sysctl_ipv6_enabled(link) == 0) {
+ log_link_warning(link, "An IPv6 route is requested, but IPv6 is disabled by sysctl, ignoring.");
+ return 0;
+ }
+
if (route_get(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, NULL) <= 0 &&
set_size(link->routes) >= routes_max())
return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG),
sd_event_source_unref(route->expire);
route->expire = TAKE_PTR(expire);
- return 0;
+ return 1;
}
int network_add_ipv4ll_route(Network *network) {
}
if (rule->manager) {
- set_remove(rule->manager->rules, rule);
- set_remove(rule->manager->rules_foreign, rule);
+ if (set_get(rule->manager->rules, rule) == rule)
+ set_remove(rule->manager->rules, rule);
+ if (set_get(rule->manager->rules_foreign, rule) == rule)
+ set_remove(rule->manager->rules_foreign, rule);
}
network_config_section_free(rule->section);
siphash24_compress(&rule->to, FAMILY_ADDRESS_SIZE(rule->family), state);
siphash24_compress(&rule->to_prefixlen, sizeof(rule->to_prefixlen), state);
+ siphash24_compress_boolean(rule->invert_rule, state);
+
siphash24_compress(&rule->tos, sizeof(rule->tos), state);
siphash24_compress(&rule->fwmark, sizeof(rule->fwmark), state);
+ siphash24_compress(&rule->fwmask, sizeof(rule->fwmask), state);
+ siphash24_compress(&rule->priority, sizeof(rule->priority), state);
siphash24_compress(&rule->table, sizeof(rule->table), state);
siphash24_compress(&rule->protocol, sizeof(rule->protocol), state);
if (r != 0)
return r;
+ r = CMP(a->invert_rule, b->invert_rule);
+ if (r != 0)
+ return r;
+
r = CMP(a->tos, b->tos);
if (r != 0)
return r;
+ r = CMP(a->fwmark, b->fwmark);
+ if (r != 0)
+ return r;
+
r = CMP(a->fwmask, b->fwmask);
if (r != 0)
return r;
+ r = CMP(a->priority, b->priority);
+ if (r != 0)
+ return r;
+
r = CMP(a->table, b->table);
if (r != 0)
return r;
DEFINE_PRIVATE_HASH_OPS(routing_policy_rule_hash_ops, RoutingPolicyRule, routing_policy_rule_hash_func, routing_policy_rule_compare_func);
-int routing_policy_rule_get(Manager *m,
- int family,
- const union in_addr_union *from,
- uint8_t from_prefixlen,
- const union in_addr_union *to,
- uint8_t to_prefixlen,
- uint8_t tos,
- uint32_t fwmark,
- uint32_t table,
- const char *iif,
- const char *oif,
- uint8_t protocol,
- struct fib_rule_port_range *sport,
- struct fib_rule_port_range *dport,
- RoutingPolicyRule **ret) {
-
- RoutingPolicyRule rule, *existing;
-
- assert_return(m, -1);
-
- rule = (RoutingPolicyRule) {
- .family = family,
- .from = *from,
- .from_prefixlen = from_prefixlen,
- .to = *to,
- .to_prefixlen = to_prefixlen,
- .tos = tos,
- .fwmark = fwmark,
- .table = table,
- .iif = (char*) iif,
- .oif = (char*) oif,
- .protocol = protocol,
- .sport = *sport,
- .dport = *dport,
- };
+int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
+
+ RoutingPolicyRule *existing;
- existing = set_get(m->rules, &rule);
+ assert(m);
+
+ existing = set_get(m->rules, rule);
if (existing) {
if (ret)
*ret = existing;
return 1;
}
- existing = set_get(m->rules_foreign, &rule);
+ existing = set_get(m->rules_foreign, rule);
if (existing) {
if (ret)
*ret = existing;
return -ENOENT;
}
-static int routing_policy_rule_add_internal(Manager *m,
- Set **rules,
- int family,
- const union in_addr_union *from,
- uint8_t from_prefixlen,
- const union in_addr_union *to,
- uint8_t to_prefixlen,
- uint8_t tos,
- uint32_t fwmark,
- uint32_t table,
- const char *_iif,
- const char *_oif,
- uint8_t protocol,
- const struct fib_rule_port_range *sport,
- const struct fib_rule_port_range *dport,
- RoutingPolicyRule **ret) {
-
+static int routing_policy_rule_add_internal(Manager *m, Set **rules, RoutingPolicyRule *in, RoutingPolicyRule **ret) {
_cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL;
_cleanup_free_ char *iif = NULL, *oif = NULL;
int r;
- assert_return(rules, -EINVAL);
+ assert(m);
+ assert(rules);
+ assert(in);
- if (_iif) {
- iif = strdup(_iif);
+ if (in->iif) {
+ iif = strdup(in->iif);
if (!iif)
return -ENOMEM;
}
- if (_oif) {
- oif = strdup(_oif);
+ if (in->oif) {
+ oif = strdup(in->oif);
if (!oif)
return -ENOMEM;
}
return r;
rule->manager = m;
- rule->family = family;
- rule->from = *from;
- rule->from_prefixlen = from_prefixlen;
- rule->to = *to;
- rule->to_prefixlen = to_prefixlen;
- rule->tos = tos;
- rule->fwmark = fwmark;
- rule->table = table;
- rule->iif = iif;
- rule->oif = oif;
- rule->protocol = protocol;
- rule->sport = *sport;
- rule->dport = *dport;
+ rule->family = in->family;
+ rule->from = in->from;
+ rule->from_prefixlen = in->from_prefixlen;
+ rule->to = in->to;
+ rule->to_prefixlen = in->to_prefixlen;
+ rule->invert_rule = in->invert_rule;
+ rule->tos = in->tos;
+ rule->fwmark = in->fwmark;
+ rule->fwmask = in->fwmask;
+ rule->priority = in->priority;
+ rule->table = in->table;
+ rule->iif = TAKE_PTR(iif);
+ rule->oif = TAKE_PTR(oif);
+ rule->protocol = in->protocol;
+ rule->sport = in->sport;
+ rule->dport = in->dport;
r = set_ensure_allocated(rules, &routing_policy_rule_hash_ops);
if (r < 0)
if (ret)
*ret = rule;
- rule = NULL;
- iif = oif = NULL;
-
+ TAKE_PTR(rule);
return 0;
}
-int routing_policy_rule_add(Manager *m,
- int family,
- const union in_addr_union *from,
- uint8_t from_prefixlen,
- const union in_addr_union *to,
- uint8_t to_prefixlen,
- uint8_t tos,
- uint32_t fwmark,
- uint32_t table,
- const char *iif,
- const char *oif,
- uint8_t protocol,
- const struct fib_rule_port_range *sport,
- const struct fib_rule_port_range *dport,
- RoutingPolicyRule **ret) {
-
- return routing_policy_rule_add_internal(m, &m->rules, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, sport, dport, ret);
+static int routing_policy_rule_add(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
+ return routing_policy_rule_add_internal(m, &m->rules, rule, ret);
}
-int routing_policy_rule_add_foreign(Manager *m,
- int family,
- const union in_addr_union *from,
- uint8_t from_prefixlen,
- const union in_addr_union *to,
- uint8_t to_prefixlen,
- uint8_t tos,
- uint32_t fwmark,
- uint32_t table,
- const char *iif,
- const char *oif,
- uint8_t protocol,
- const struct fib_rule_port_range *sport,
- const struct fib_rule_port_range *dport,
- RoutingPolicyRule **ret) {
- return routing_policy_rule_add_internal(m, &m->rules_foreign, family, from, from_prefixlen, to, to_prefixlen, tos, fwmark, table, iif, oif, protocol, sport, dport, ret);
+int routing_policy_rule_add_foreign(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret) {
+ return routing_policy_rule_add_internal(m, &m->rules_foreign, rule, ret);
}
static int routing_policy_rule_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
return 1;
}
-int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback, bool update) {
+int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netlink_message_handler_t callback) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
int r;
assert(link->manager);
assert(link->manager->rtnl);
+ if (rule->family == AF_INET6 && link_sysctl_ipv6_enabled(link) == 0) {
+ log_link_warning(link, "An IPv6 routing policy rule is requested, but IPv6 is disabled by sysctl, ignoring.");
+ return 0;
+ }
+
r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, rule->family);
if (r < 0)
return log_error_errno(r, "Could not allocate RTM_NEWRULE message: %m");
link_ref(link);
- r = routing_policy_rule_add(link->manager, rule->family, &rule->from, rule->from_prefixlen, &rule->to,
- rule->to_prefixlen, rule->tos, rule->fwmark, rule->table, rule->iif, rule->oif, rule->protocol, &rule->sport, &rule->dport, NULL);
+ r = routing_policy_rule_add(link->manager, rule, NULL);
if (r < 0)
return log_error_errno(r, "Could not add rule: %m");
- return 0;
+ return 1;
}
static int parse_fwmark_fwmask(const char *s, uint32_t *fwmark, uint32_t *fwmask) {
DEFINE_NETWORK_SECTION_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free);
-int routing_policy_rule_configure(RoutingPolicyRule *address, Link *link, link_netlink_message_handler_t callback, bool update);
+int routing_policy_rule_configure(RoutingPolicyRule *address, Link *link, link_netlink_message_handler_t callback);
int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *link, link_netlink_message_handler_t callback);
-int routing_policy_rule_add(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen,
- uint8_t tos, uint32_t fwmark, uint32_t table, const char *iif, const char *oif, uint8_t protocol, const struct fib_rule_port_range *sport,
- const struct fib_rule_port_range *dport, RoutingPolicyRule **ret);
-int routing_policy_rule_add_foreign(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen,
- uint8_t tos, uint32_t fwmark, uint32_t table, const char *iif, const char *oif, uint8_t protocol, const struct fib_rule_port_range *sport,
- const struct fib_rule_port_range *dport, RoutingPolicyRule **ret);
-int routing_policy_rule_get(Manager *m, int family, const union in_addr_union *from, uint8_t from_prefixlen, const union in_addr_union *to, uint8_t to_prefixlen, uint8_t tos,
- uint32_t fwmark, uint32_t table, const char *iif, const char *oif, uint8_t protocol, struct fib_rule_port_range *sport,
- struct fib_rule_port_range *dport, RoutingPolicyRule **ret);
+int routing_policy_rule_add_foreign(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret);
+int routing_policy_rule_get(Manager *m, RoutingPolicyRule *rule, RoutingPolicyRule **ret);
int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule);
int routing_policy_serialize_rules(Set *rules, FILE *f);
int routing_policy_load_rules(const char *state_file, Set **rules);
#include "sd-event.h"
#include "sd-netlink.h"
+#include "networkd-link-bus.h"
#include "networkd-link.h"
#include "networkd-manager.h"
#include "networkd-speed-meter.h"
assert_se(network = new0(Network, 1));
network->n_ref = 1;
assert_se(network->filename = strdup("hogehoge.network"));
- assert_se(config_parse_ifnames("network", "filename", 1, "section", 1, "Name", 0, "*", &network->match_name, network) == 0);
+ assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "*", &network->match_name, network) == 0);
assert_se(config_parse_address("network", "filename", 1, "section", 1, "Address", 0, rvalue, network, network) == 0);
assert_se(network->n_static_addresses == 1);
assert_se(network_verify(network) >= 0);
test_config_parse_address_one("::1/-1", AF_INET6, 0, NULL, 0);
}
+static void test_config_parse_match_ifnames(void) {
+ _cleanup_strv_free_ char **names = NULL;
+
+ assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "!hoge hogehoge foo", &names, NULL) == 0);
+ assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "!baz", &names, NULL) == 0);
+ assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "aaa bbb ccc", &names, NULL) == 0);
+
+ strv_equal(names, STRV_MAKE("!hoge", "!hogehoge", "!foo", "!baz", "aaa", "bbb", "ccc"));
+}
+
+static void test_config_parse_match_strv(void) {
+ _cleanup_strv_free_ char **names = NULL;
+
+ assert_se(config_parse_match_strv("network", "filename", 1, "section", 1, "Name", 0, "!hoge hogehoge foo", &names, NULL) == 0);
+ assert_se(config_parse_match_strv("network", "filename", 1, "section", 1, "Name", 0, "!baz", &names, NULL) == 0);
+ assert_se(config_parse_match_strv("network", "filename", 1, "section", 1, "Name", 0,
+ "KEY=val \"KEY2=val with space\" \"KEY3=val with \\\"quotation\\\"\"", &names, NULL) == 0);
+
+ strv_equal(names, STRV_MAKE("!hoge", "!hogehoge", "!foo", "!baz", "KEY=val", "KEY2=val with space", "KEY3=val with \"quotation\""));
+}
+
int main(int argc, char **argv) {
log_parse_environment();
log_open();
test_config_parse_duid_rawdata();
test_config_parse_hwaddr();
test_config_parse_address();
+ test_config_parse_match_ifnames();
+ test_config_parse_match_strv();
return 0;
}
if (streq(controller, tok))
break;
- target = prefix_root("/sys/fs/cgroup/", tok);
+ target = path_join("/sys/fs/cgroup/", tok);
if (!target)
return log_oom();
if (!controller)
break;
- origin = prefix_root("/sys/fs/cgroup/", controller);
+ origin = path_join("/sys/fs/cgroup/", controller);
if (!origin)
return log_oom();
else {
_cleanup_free_ char *target = NULL;
- target = prefix_root(dest, origin);
+ target = path_join(dest, origin);
if (!target)
return log_oom();
return NULL;
if (source[0] == '+')
- return prefix_root(dest, source + 1);
+ return path_join(dest, source + 1);
return strdup(source);
}
return log_error_errno(errno, "Failed to acquire temporary directory: %m");
}
- m->source = strjoin(m->rm_rf_tmpdir, "/src");
+ m->source = path_join(m->rm_rf_tmpdir, "src");
if (!m->source)
return log_oom();
FOREACH_STRING(x, "block", "bus", "class", "dev", "devices", "kernel") {
_cleanup_free_ char *from = NULL, *to = NULL;
- from = prefix_root(full, x);
+ from = path_join(full, x);
if (!from)
return log_oom();
- to = prefix_root(top, x);
+ to = path_join(top, x);
if (!to)
return log_oom();
* Requires all file systems at directory and below to be mounted
* MS_PRIVATE or MS_SLAVE so they can be moved.
*/
- directory_pivot_root_new = prefix_root(directory, pivot_root_new);
+ directory_pivot_root_new = path_join(directory, pivot_root_new);
+ if (!directory_pivot_root_new)
+ return log_oom();
/* Remount directory_pivot_root_new to make it movable. */
r = mount_verbose(LOG_ERR, directory_pivot_root_new, directory_pivot_root_new, NULL, MS_BIND, NULL);
}
remove_pivot_tmp = true;
- pivot_tmp_pivot_root_old = prefix_root(pivot_tmp, pivot_root_old);
+ pivot_tmp_pivot_root_old = path_join(pivot_tmp, pivot_root_old);
+ if (!pivot_tmp_pivot_root_old) {
+ r = log_oom();
+ goto done;
+ }
r = mount_verbose(LOG_ERR, directory_pivot_root_new, pivot_tmp, NULL, MS_MOVE, NULL);
if (r < 0)
_cleanup_free_ char *from = NULL, *to = NULL;
struct stat st;
- from = strappend("/dev/", d);
+ from = path_join("/dev/", d);
if (!from)
return log_oom();
- to = prefix_root(dest, from);
+ to = path_join(dest, from);
if (!to)
return log_oom();
if (r < 0)
return log_error_errno(r, "chown() of device node %s failed: %m", to);
- dn = strjoin("/dev/", S_ISCHR(st.st_mode) ? "char" : "block");
+ dn = path_join("/dev", S_ISCHR(st.st_mode) ? "char" : "block");
if (!dn)
return log_oom();
if (asprintf(&sl, "%s/%u:%u", dn, major(st.st_rdev), minor(st.st_rdev)) < 0)
return log_oom();
- prefixed = prefix_root(dest, sl);
+ prefixed = path_join(dest, sl);
if (!prefixed)
return log_oom();
- t = strjoin("../", d);
+ t = path_join("..", d);
if (!t)
return log_oom();
_cleanup_free_ char *path = NULL;
DeviceNode *n = arg_extra_nodes + i;
- path = prefix_root(dest, n->path);
+ path = path_join(dest, n->path);
if (!path)
return log_oom();
return 0;
}
-static int setup_dev_console(const char *dest, const char *console) {
- _cleanup_umask_ mode_t u;
- const char *to;
+static int setup_stdio_as_dev_console(void) {
+ int terminal;
int r;
- assert(dest);
-
- u = umask(0000);
+ terminal = open_terminal("/dev/console", O_RDWR);
+ if (terminal < 0)
+ return log_error_errno(terminal, "Failed to open console: %m");
- if (!console)
- return 0;
+ /* Make sure we can continue logging to the original stderr, even if
+ * stderr points elsewhere now */
+ r = log_dup_console();
+ if (r < 0)
+ return log_error_errno(r, "Failed to duplicate stderr: %m");
- r = chmod_and_chown(console, 0600, arg_uid_shift, arg_uid_shift);
+ /* invalidates 'terminal' on success and failure */
+ r = rearrange_stdio(terminal, terminal, terminal);
if (r < 0)
- return log_error_errno(r, "Failed to correct access mode for TTY: %m");
+ return log_error_errno(r, "Failed to move console to stdin/stdout/stderr: %m");
+
+ return 0;
+}
- /* We need to bind mount the right tty to /dev/console since
- * ptys can only exist on pts file systems. To have something
- * to bind mount things on we create a empty regular file. */
+static int setup_dev_console(const char *console) {
+ _cleanup_free_ char *p = NULL;
+ int r;
- to = prefix_roota(dest, "/dev/console");
- r = touch(to);
+ /* Create /dev/console symlink */
+ r = path_make_relative("/dev", console, &p);
if (r < 0)
- return log_error_errno(r, "touch() for /dev/console failed: %m");
+ return log_error_errno(r, "Failed to create relative path: %m");
+
+ if (symlink(p, "/dev/console") < 0)
+ return log_error_errno(errno, "Failed to create /dev/console symlink: %m");
- return mount_verbose(LOG_ERR, console, to, NULL, MS_BIND, NULL);
+ return 0;
}
static int setup_keyring(void) {
if (q.ambient == (uint64_t) -1 && ambient_capabilities_supported())
q.ambient = 0;
- } else
+
+ if (capability_quintet_mangle(&q))
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Cannot set capabilities that are not in the current bounding set.");
+
+ } else {
q = (CapabilityQuintet) {
.bounding = arg_caps_retain,
.effective = uid == 0 ? arg_caps_retain : 0,
.ambient = ambient_capabilities_supported() ? 0 : (uint64_t) -1,
};
+ /* If we're not using OCI, proceed with mangled capabilities (so we don't error out)
+ * in order to maintain the same behavior as systemd < 242. */
+ if (capability_quintet_mangle(&q))
+ log_warning("Some capabilities will not be set because they are not in the current bounding set.");
+
+ }
+
return capability_quintet_enforce(&q);
}
* search for a machine, but instead create a new one
* in /var/lib/machine. */
- arg_directory = strjoin("/var/lib/machines/", arg_machine);
+ arg_directory = path_join("/var/lib/machines", arg_machine);
if (!arg_directory)
return log_oom();
}
bool secondary,
int kmsg_socket,
int rtnl_socket,
+ int master_pty_socket,
FDSet *fds) {
_cleanup_free_ char *home = NULL;
rtnl_socket = safe_close(rtnl_socket);
}
+ if (arg_console_mode != CONSOLE_PIPE) {
+ _cleanup_close_ int master = -1;
+ _cleanup_free_ char *console = NULL;
+
+ /* Allocate a pty and make it available as /dev/console. */
+ master = openpt_allocate(O_RDWR|O_NONBLOCK, &console);
+ if (master < 0)
+ return log_error_errno(master, "Failed to allocate a pty: %m");
+
+ r = setup_dev_console(console);
+ if (r < 0)
+ return log_error_errno(r, "Failed to setup /dev/console: %m");
+
+ r = send_one_fd(master_pty_socket, master, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to send master fd: %m");
+ master_pty_socket = safe_close(master_pty_socket);
+
+ r = setup_stdio_as_dev_console();
+ if (r < 0)
+ return r;
+ }
+
r = patch_sysctl();
if (r < 0)
return r;
static int outer_child(
Barrier *barrier,
const char *directory,
- const char *console,
DissectedImage *dissected_image,
bool secondary,
int pid_socket,
int kmsg_socket,
int rtnl_socket,
int uid_shift_socket,
+ int master_pty_socket,
int unified_cgroup_hierarchy_socket,
FDSet *fds,
int netns_fd) {
assert(pid_socket >= 0);
assert(uuid_socket >= 0);
assert(notify_socket >= 0);
+ assert(master_pty_socket >= 0);
assert(kmsg_socket >= 0);
log_debug("Outer child is initializing.");
if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0)
return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m");
- if (arg_console_mode != CONSOLE_PIPE) {
- int terminal;
-
- assert(console);
-
- terminal = open_terminal(console, O_RDWR);
- if (terminal < 0)
- return log_error_errno(terminal, "Failed to open console: %m");
-
- /* Make sure we can continue logging to the original stderr, even if stderr points elsewhere now */
- r = log_dup_console();
- if (r < 0)
- return log_error_errno(r, "Failed to duplicate stderr: %m");
-
- r = rearrange_stdio(terminal, terminal, terminal); /* invalidates 'terminal' on success and failure */
- if (r < 0)
- return log_error_errno(r, "Failed to move console to stdin/stdout/stderr: %m");
- }
-
r = reset_audit_loginuid();
if (r < 0)
return r;
if (r < 0)
return r;
- r = setup_dev_console(directory, console);
- if (r < 0)
- return r;
-
r = setup_keyring();
if (r < 0)
return r;
return log_error_errno(r, "Failed to join network namespace: %m");
}
- r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, fds);
+ r = inner_child(barrier, directory, secondary, kmsg_socket, rtnl_socket, master_pty_socket, fds);
if (r < 0)
_exit(EXIT_FAILURE);
l = send_one_fd(notify_socket, fd, 0);
if (l < 0)
- return log_error_errno(errno, "Failed to send notify fd: %m");
+ return log_error_errno(l, "Failed to send notify fd: %m");
pid_socket = safe_close(pid_socket);
uuid_socket = safe_close(uuid_socket);
notify_socket = safe_close(notify_socket);
+ master_pty_socket = safe_close(master_pty_socket);
kmsg_socket = safe_close(kmsg_socket);
rtnl_socket = safe_close(rtnl_socket);
netns_fd = safe_close(netns_fd);
FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn") {
_cleanup_free_ char *j = NULL;
- j = strjoin(i, "/", fn);
+ j = path_join(i, fn);
if (!j)
return log_oom();
return merge_settings(settings, arg_oci_bundle);
}
-static int run_container(int master,
- const char* console,
+static int run_container(
DissectedImage *dissected_image,
bool secondary,
FDSet *fds,
char veth_name[IFNAMSIZ], bool *veth_created,
union in_addr_union *exposed,
- pid_t *pid, int *ret) {
+ int *master, pid_t *pid, int *ret) {
static const struct sigaction sa = {
.sa_handler = nop_signal_handler,
uuid_socket_pair[2] = { -1, -1 },
notify_socket_pair[2] = { -1, -1 },
uid_shift_socket_pair[2] = { -1, -1 },
+ master_pty_socket_pair[2] = { -1, -1 },
unified_cgroup_hierarchy_socket_pair[2] = { -1, -1};
- _cleanup_close_ int notify_socket= -1;
+ _cleanup_close_ int notify_socket = -1;
_cleanup_(barrier_destroy) Barrier barrier = BARRIER_NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *notify_event_source = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, notify_socket_pair) < 0)
return log_error_errno(errno, "Failed to create notify socket pair: %m");
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, master_pty_socket_pair) < 0)
+ return log_error_errno(errno, "Failed to create console socket pair: %m");
+
if (arg_userns_mode != USER_NAMESPACE_NO)
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, uid_shift_socket_pair) < 0)
return log_error_errno(errno, "Failed to create uid shift socket pair: %m");
/* The outer child only has a file system namespace. */
barrier_set_role(&barrier, BARRIER_CHILD);
- master = safe_close(master);
-
kmsg_socket_pair[0] = safe_close(kmsg_socket_pair[0]);
rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
pid_socket_pair[0] = safe_close(pid_socket_pair[0]);
uuid_socket_pair[0] = safe_close(uuid_socket_pair[0]);
notify_socket_pair[0] = safe_close(notify_socket_pair[0]);
+ master_pty_socket_pair[0] = safe_close(master_pty_socket_pair[0]);
uid_shift_socket_pair[0] = safe_close(uid_shift_socket_pair[0]);
unified_cgroup_hierarchy_socket_pair[0] = safe_close(unified_cgroup_hierarchy_socket_pair[0]);
r = outer_child(&barrier,
arg_directory,
- console,
dissected_image,
secondary,
pid_socket_pair[1],
kmsg_socket_pair[1],
rtnl_socket_pair[1],
uid_shift_socket_pair[1],
+ master_pty_socket_pair[1],
unified_cgroup_hierarchy_socket_pair[1],
fds,
netns_fd);
pid_socket_pair[1] = safe_close(pid_socket_pair[1]);
uuid_socket_pair[1] = safe_close(uuid_socket_pair[1]);
notify_socket_pair[1] = safe_close(notify_socket_pair[1]);
+ master_pty_socket_pair[1] = safe_close(master_pty_socket_pair[1]);
uid_shift_socket_pair[1] = safe_close(uid_shift_socket_pair[1]);
unified_cgroup_hierarchy_socket_pair[1] = safe_close(unified_cgroup_hierarchy_socket_pair[1]);
rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]);
- if (IN_SET(arg_console_mode, CONSOLE_INTERACTIVE, CONSOLE_READ_ONLY)) {
- assert(master >= 0);
+ if (arg_console_mode != CONSOLE_PIPE) {
+ _cleanup_close_ int fd = -1;
+ PTYForwardFlags flags = 0;
- r = pty_forward_new(event, master,
- PTY_FORWARD_IGNORE_VHANGUP | (arg_console_mode == CONSOLE_READ_ONLY ? PTY_FORWARD_READ_ONLY : 0),
- &forward);
- if (r < 0)
- return log_error_errno(r, "Failed to create PTY forwarder: %m");
+ /* Retrieve the master pty allocated by inner child */
+ fd = receive_one_fd(master_pty_socket_pair[0], 0);
+ if (fd < 0)
+ return log_error_errno(fd, "Failed to receive master pty from the inner child: %m");
+
+ switch (arg_console_mode) {
+
+ case CONSOLE_READ_ONLY:
+ flags |= PTY_FORWARD_READ_ONLY;
+
+ _fallthrough_;
+
+ case CONSOLE_INTERACTIVE:
+ flags |= PTY_FORWARD_IGNORE_VHANGUP;
+
+ r = pty_forward_new(event, fd, flags, &forward);
+ if (r < 0)
+ return log_error_errno(r, "Failed to create PTY forwarder: %m");
+
+ if (arg_console_width != (unsigned) -1 || arg_console_height != (unsigned) -1)
+ (void) pty_forward_set_width_height(forward,
+ arg_console_width,
+ arg_console_height);
+ break;
- if (arg_console_width != (unsigned) -1 || arg_console_height != (unsigned) -1)
- (void) pty_forward_set_width_height(forward, arg_console_width, arg_console_height);
+ default:
+ assert(arg_console_mode == CONSOLE_PASSIVE);
+ }
+
+ *master = TAKE_FD(fd);
}
r = sd_event_loop(event);
}
static int run(int argc, char *argv[]) {
- _cleanup_free_ char *console = NULL;
_cleanup_close_ int master = -1;
_cleanup_fdset_free_ FDSet *fds = NULL;
int r, n_fd_passed, ret = EXIT_SUCCESS;
if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */
arg_quiet = true;
- if (arg_console_mode != CONSOLE_PIPE) {
- master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK);
- if (master < 0) {
- r = log_error_errno(errno, "Failed to acquire pseudo tty: %m");
- goto finish;
- }
-
- r = ptsname_malloc(master, &console);
- if (r < 0) {
- r = log_error_errno(r, "Failed to determine tty name: %m");
- goto finish;
- }
-
- if (arg_selinux_apifs_context) {
- r = mac_selinux_apply(console, arg_selinux_apifs_context);
- if (r < 0)
- goto finish;
- }
-
- if (unlockpt(master) < 0) {
- r = log_error_errno(errno, "Failed to unlock tty: %m");
- goto finish;
- }
- }
-
if (!arg_quiet)
log_info("Spawning container %s on %s.\nPress ^] three times within 1s to kill container.",
arg_machine, arg_image ?: arg_directory);
}
for (;;) {
- r = run_container(master,
- console,
- dissected_image,
+ r = run_container(dissected_image,
secondary,
fds,
veth_name, &veth_created,
- &exposed,
+ &exposed, &master,
&pid, &ret);
if (r <= 0)
break;
#if HAVE_LIBCRYPTSETUP
crypt_set_log_callback(NULL, cryptsetup_log_glue, NULL);
- crypt_set_debug_level(1);
+ if (DEBUG_LOGGING)
+ crypt_set_debug_level(CRYPT_DEBUG_ALL);
#endif
r = get_block_device_harder(mountpath, &devno);
return -ENOMEM;
fd = -1;
- m->source = strjoin(resolved, "/", de->d_name);
+ m->source = path_join(resolved, de->d_name);
if (!m->source)
return -ENOMEM;
return 0;
if (prefix) {
- path = strjoina(prefix, "/", path);
+ path = prefix_roota(prefix, path);
if (source)
- source = strjoina(prefix, "/", source);
+ source = prefix_roota(prefix, source);
}
return portable_changes_add(changes, n_changes, type, path, source);
assert(m);
assert(dropin_dir);
- dropin = strjoin(dropin_dir, "/20-portable.conf");
+ dropin = path_join(dropin_dir, "20-portable.conf");
if (!dropin)
return -ENOMEM;
return 0;
}
- dropin = strjoin(dropin_dir, "/10-profile.conf");
+ dropin = path_join(dropin_dir, "10-profile.conf");
if (!dropin)
return -ENOMEM;
} else
(void) portable_changes_add(changes, n_changes, PORTABLE_MKDIR, where, NULL);
- path = strjoina(where, "/", m->name);
+ path = prefix_roota(where, m->name);
dropin_dir = strjoin(path, ".d");
if (!dropin_dir)
return -ENOMEM;
umask(0022);
- if (argc != 1) {
- log_error("This program takes no arguments.");
- return -EINVAL;
- }
+ if (argc != 1)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program takes no arguments.");
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGTERM, SIGINT, -1) >= 0);
#include <sys/un.h>
#include "alloc-util.h"
+#include "main-func.h"
#include "fd-util.h"
#include "fileio.h"
#include "log.h"
return 0;
}
-int main(int argc, char *argv[]) {
- _cleanup_free_ char *packet = NULL;
+static int run(int argc, char *argv[]) {
+ _cleanup_(erase_and_freep) char *packet = NULL;
_cleanup_close_ int fd = -1;
size_t length = 0;
int r;
log_setup_service();
- if (argc != 3) {
- log_error("Wrong number of arguments.");
- return EXIT_FAILURE;
- }
+ if (argc != 3)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Wrong number of arguments.");
if (streq(argv[1], "1")) {
- _cleanup_string_free_erase_ char *line = NULL;
+ _cleanup_(erase_and_freep) char *line = NULL;
r = read_line(stdin, LONG_LINE_MAX, &line);
- if (r < 0) {
- log_error_errno(r, "Failed to read password: %m");
- goto finish;
- }
- if (r == 0) {
- log_error("Got EOF while reading password.");
- r = -EIO;
- goto finish;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to read password: %m");
+ if (r == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO),
+ "Got EOF while reading password.");
packet = strjoin("+", line);
- if (!packet) {
- r = log_oom();
- goto finish;
- }
+ if (!packet)
+ return log_oom();
length = 1 + strlen(line) + 1;
} else if (streq(argv[1], "0")) {
packet = strdup("-");
- if (!packet) {
- r = log_oom();
- goto finish;
- }
+ if (!packet)
+ return log_oom();
length = 1;
- } else {
- log_error("Invalid first argument %s", argv[1]);
- r = -EINVAL;
- goto finish;
- }
+ } else
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
+ "Invalid first argument %s", argv[1]);
fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
- if (fd < 0) {
- r = log_error_errno(errno, "socket() failed: %m");
- goto finish;
- }
+ if (fd < 0)
+ return log_error_errno(errno, "socket() failed: %m");
- r = send_on_socket(fd, argv[2], packet, length);
-
-finish:
- explicit_bzero_safe(packet, length);
-
- return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+ return send_on_socket(fd, argv[2], packet, length);
}
+
+DEFINE_MAIN_FUNCTION(run);
for (;;) {
_cleanup_free_ char *word = NULL;
- r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&string, &word, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return r;
if (r == 0)
return r;
for (;;) {
- char *pretty = NULL;
+ _cleanup_free_ char *pretty = NULL;
r = read_dns_server_one(m, false, &pretty);
if (r < 0)
if (isempty(pretty))
continue;
- r = strv_consume(l, pretty);
+ r = strv_consume(l, TAKE_PTR(pretty));
if (r < 0)
return r;
}
return r;
for (;;) {
- char *pretty = NULL;
+ _cleanup_free_ char *pretty = NULL;
r = read_domain_one(m, false, &pretty);
if (r < 0)
if (isempty(pretty))
continue;
- r = strv_consume(l, pretty);
+ r = strv_consume(l, TAKE_PTR(pretty));
if (r < 0)
return r;
}
return r;
for (;;) {
- char *pretty = NULL;
+ _cleanup_free_ char *pretty = NULL;
r = read_dns_server_one(m, true, &pretty);
if (r < 0)
if (isempty(pretty))
continue;
- r = strv_consume(l, pretty);
+ r = strv_consume(l, TAKE_PTR(pretty));
if (r < 0)
return r;
}
return r;
for (;;) {
- char *pretty = NULL;
+ _cleanup_free_ char *pretty = NULL;
r = read_domain_one(m, true, &pretty);
if (r < 0)
if (isempty(pretty))
continue;
- r = strv_consume(l, pretty);
+ r = strv_consume(l, TAKE_PTR(pretty));
if (r < 0)
return r;
}
for (;;) {
_cleanup_free_ char *word = NULL;
- r = extract_first_word(&string, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&string, &word, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return r;
if (r == 0)
int r;
r = extract_first_word(&rvalue, &word, NULL,
- EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX);
+ EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX);
if (r == 0)
break;
if (r == -ENOMEM)
#if ! ENABLE_DNS_OVER_TLS
if (m->dns_over_tls_mode != DNS_OVER_TLS_NO) {
- log_warning("DNS-over-TLS option cannot be set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support.");
+ log_warning("DNS-over-TLS option cannot be enabled or set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support.");
m->dns_over_tls_mode = DNS_OVER_TLS_NO;
}
#endif
#include "dns-type.h"
#include "escape.h"
#include "hexdecoct.h"
+#include "memory-util.h"
#include "resolved-dns-dnssec.h"
#include "resolved-dns-packet.h"
#include "resolved-dns-rr.h"
#define FIELD_EQUAL(a, b, field) \
((a).field ## _size == (b).field ## _size && \
- memcmp((a).field, (b).field, (a).field ## _size) == 0)
+ memcmp_safe((a).field, (b).field, (a).field ## _size) == 0)
int dns_resource_record_payload_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) {
int r;
s->linked = true;
-#if ENABLE_DNS_OVER_TLS
- dnstls_server_init(s);
-#endif
-
/* A new DNS server that isn't fallback is added and the one
* we used so far was a fallback one? Then let's try to pick
* the new one */
log_debug("Reached maximum number of failed TCP connection attempts, trying UDP again...");
s->possible_feature_level = DNS_SERVER_FEATURE_LEVEL_UDP;
} else if (s->n_failed_tls > 0 &&
- DNS_SERVER_FEATURE_LEVEL_IS_TLS(s->possible_feature_level)) {
+ DNS_SERVER_FEATURE_LEVEL_IS_TLS(s->possible_feature_level) && dns_server_get_dns_over_tls_mode(s) != DNS_OVER_TLS_YES) {
/* We tried to connect using DNS-over-TLS, and it didn't work. Downgrade to plaintext UDP
* if we don't require DNS-over-TLS */
const char* dns_server_feature_level_to_string(int i) _const_;
int dns_server_feature_level_from_string(const char *s) _pure_;
+#include "resolved-dnstls.h"
#include "resolved-link.h"
#include "resolved-manager.h"
-#if ENABLE_DNS_OVER_TLS
-#include "resolved-dnstls.h"
-#endif
struct DnsServer {
Manager *manager;
#include "resolved-dns-packet.h"
#include "resolved-dns-transaction.h"
-#include "resolved-manager.h"
-#if ENABLE_DNS_OVER_TLS
#include "resolved-dnstls.h"
-#endif
+#include "resolved-manager.h"
#define DNS_STREAM_WRITE_TLS_DATA 1
#include "random-util.h"
#include "resolved-dns-cache.h"
#include "resolved-dns-transaction.h"
-#include "resolved-llmnr.h"
-#if ENABLE_DNS_OVER_TLS
#include "resolved-dnstls.h"
-#endif
+#include "resolved-llmnr.h"
#include "string-table.h"
#define TRANSACTIONS_MAX 4096
assert(d);
assert(line);
- r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &domain, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return log_warning_errno(r, "Unable to parse domain in line %s:%u: %m", path, line);
assert(d);
assert(line);
- r = extract_first_word(&p, &domain, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &domain, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return log_warning_errno(r, "Unable to parse line %s:%u: %m", path, line);
#include "resolved-dns-stream.h"
#include "resolved-dnstls.h"
+#if GNUTLS_VERSION_NUMBER >= 0x030600
+#define PRIORTY_STRING "NORMAL:-VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.3"
+#else
+#define PRIORTY_STRING "NORMAL:-VERS-ALL:+VERS-TLS1.2"
+#endif
DEFINE_TRIVIAL_CLEANUP_FUNC(gnutls_session_t, gnutls_deinit);
static ssize_t dnstls_stream_writev(gnutls_transport_ptr_t p, const giovec_t *iov, int iovcnt) {
return r;
/* As DNS-over-TLS is a recent protocol, older TLS versions can be disabled */
- r = gnutls_priority_set_direct(gs, "NORMAL:-VERS-ALL:+VERS-TLS1.2", NULL);
+ r = gnutls_priority_set_direct(gs, PRIORTY_STRING, NULL);
if (r < 0)
return r;
- r = gnutls_credentials_set(gs, GNUTLS_CRD_CERTIFICATE, server->dnstls_data.cert_cred);
+ r = gnutls_credentials_set(gs, GNUTLS_CRD_CERTIFICATE, stream->manager->dnstls_data.cert_cred);
if (r < 0)
return r;
server->dnstls_data.session_data.size = 0;
}
+ if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES)
+ gnutls_session_set_verify_cert(gs, NULL, 0);
+
gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
gnutls_transport_set_ptr2(gs, (gnutls_transport_ptr_t) (long) stream->fd, stream);
return ss;
}
-void dnstls_server_init(DnsServer *server) {
+void dnstls_server_free(DnsServer *server) {
assert(server);
- /* Do not verify cerificate */
- gnutls_certificate_allocate_credentials(&server->dnstls_data.cert_cred);
+ if (server->dnstls_data.session_data.data)
+ gnutls_free(server->dnstls_data.session_data.data);
}
-void dnstls_server_free(DnsServer *server) {
- assert(server);
+int dnstls_manager_init(Manager *manager) {
+ int r;
+ assert(manager);
- if (server->dnstls_data.cert_cred)
- gnutls_certificate_free_credentials(server->dnstls_data.cert_cred);
+ r = gnutls_certificate_allocate_credentials(&manager->dnstls_data.cert_cred);
+ if (r < 0)
+ return -ENOMEM;
- if (server->dnstls_data.session_data.data)
- gnutls_free(server->dnstls_data.session_data.data);
+ r = gnutls_certificate_set_x509_system_trust(manager->dnstls_data.cert_cred);
+ if (r < 0)
+ log_warning("Failed to load system trust store: %s", gnutls_strerror(r));
+
+ return 0;
+}
+
+void dnstls_manager_free(Manager *manager) {
+ assert(manager);
+
+ if (manager->dnstls_data.cert_cred)
+ gnutls_certificate_free_credentials(manager->dnstls_data.cert_cred);
}
#include <gnutls/gnutls.h>
#include <stdbool.h>
-struct DnsTlsServerData {
+struct DnsTlsManagerData {
gnutls_certificate_credentials_t cert_cred;
+};
+
+struct DnsTlsServerData {
gnutls_datum_t session_data;
};
assert(stream);
assert(stream->encrypted);
- if (stream->dnstls_data.write_buffer->length > 0) {
+ if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) {
assert(stream->dnstls_data.write_buffer->data);
struct iovec iov[1];
- iov[0] = IOVEC_MAKE(stream->dnstls_data.write_buffer->data,
- stream->dnstls_data.write_buffer->length);
+ iov[0] = IOVEC_MAKE(stream->dnstls_data.write_buffer->data + stream->dnstls_data.buffer_offset,
+ stream->dnstls_data.write_buffer->length - stream->dnstls_data.buffer_offset);
ss = dns_stream_writev(stream, iov, 1, DNS_STREAM_WRITE_TLS_DATA);
if (ss < 0) {
if (ss == -EAGAIN)
return ss;
} else {
- stream->dnstls_data.write_buffer->length -= ss;
- stream->dnstls_data.write_buffer->data += ss;
+ stream->dnstls_data.buffer_offset += ss;
- if (stream->dnstls_data.write_buffer->length > 0) {
+ if (stream->dnstls_data.buffer_offset < stream->dnstls_data.write_buffer->length) {
stream->dnstls_events |= EPOLLOUT;
return -EAGAIN;
+ } else {
+ BIO_reset(SSL_get_wbio(stream->dnstls_data.ssl));
+ stream->dnstls_data.buffer_offset = 0;
}
}
}
int error, r;
assert(stream);
+ assert(stream->manager);
assert(server);
rb = BIO_new_socket(stream->fd, 0);
return -ENOMEM;
BIO_get_mem_ptr(wb, &stream->dnstls_data.write_buffer);
+ stream->dnstls_data.buffer_offset = 0;
- s = SSL_new(server->dnstls_data.ctx);
+ s = SSL_new(stream->manager->dnstls_data.ctx);
if (!s)
return -ENOMEM;
SSL_set_session(s, server->dnstls_data.session);
SSL_set_bio(s, TAKE_PTR(rb), TAKE_PTR(wb));
+ if (server->manager->dns_over_tls_mode == DNS_OVER_TLS_YES) {
+ X509_VERIFY_PARAM *v;
+ const unsigned char *ip;
+
+ SSL_set_verify(s, SSL_VERIFY_PEER, NULL);
+ v = SSL_get0_param(s);
+ ip = server->family == AF_INET ? (const unsigned char*) &server->address.in.s_addr : server->address.in6.s6_addr;
+ if (!X509_VERIFY_PARAM_set1_ip(v, ip, FAMILY_ADDRESS_SIZE(server->family)))
+ return -ECONNREFUSED;
+ }
+
ERR_clear_error();
stream->dnstls_data.handshake = SSL_do_handshake(s);
if (stream->dnstls_data.handshake <= 0) {
}
stream->encrypted = true;
+ stream->dnstls_data.ssl = TAKE_PTR(s);
r = dnstls_flush_write_buffer(stream);
- if (r < 0 && r != -EAGAIN)
+ if (r < 0 && r != -EAGAIN) {
+ SSL_free(TAKE_PTR(stream->dnstls_data.ssl));
return r;
-
- stream->dnstls_data.ssl = TAKE_PTR(s);
+ }
return 0;
}
return ss;
}
-void dnstls_server_init(DnsServer *server) {
+void dnstls_server_free(DnsServer *server) {
assert(server);
- server->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
- if (server->dnstls_data.ctx) {
- SSL_CTX_set_min_proto_version(server->dnstls_data.ctx, TLS1_2_VERSION);
- SSL_CTX_set_options(server->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
- }
+ if (server->dnstls_data.session)
+ SSL_SESSION_free(server->dnstls_data.session);
}
-void dnstls_server_free(DnsServer *server) {
- assert(server);
+int dnstls_manager_init(Manager *manager) {
+ int r;
+ assert(manager);
- if (server->dnstls_data.ctx)
- SSL_CTX_free(server->dnstls_data.ctx);
+ ERR_load_crypto_strings();
+ SSL_load_error_strings();
+ manager->dnstls_data.ctx = SSL_CTX_new(TLS_client_method());
- if (server->dnstls_data.session)
- SSL_SESSION_free(server->dnstls_data.session);
+ if (!manager->dnstls_data.ctx)
+ return -ENOMEM;
+
+ SSL_CTX_set_min_proto_version(manager->dnstls_data.ctx, TLS1_2_VERSION);
+ SSL_CTX_set_options(manager->dnstls_data.ctx, SSL_OP_NO_COMPRESSION);
+ r = SSL_CTX_set_default_verify_paths(manager->dnstls_data.ctx);
+ if (r < 0)
+ log_warning("Failed to load system trust store: %s", ERR_error_string(ERR_get_error(), NULL));
+
+ return 0;
+}
+
+void dnstls_manager_free(Manager *manager) {
+ assert(manager);
+
+ if (manager->dnstls_data.ctx)
+ SSL_CTX_free(manager->dnstls_data.ctx);
}
#include <openssl/ssl.h>
#include <stdbool.h>
-struct DnsTlsServerData {
+struct DnsTlsManagerData {
SSL_CTX *ctx;
+};
+
+struct DnsTlsServerData {
SSL_SESSION *session;
};
bool shutdown;
SSL *ssl;
BUF_MEM *write_buffer;
+ size_t buffer_offset;
};
/* SPDX-License-Identifier: LGPL-2.1+ */
#pragma once
-#if !ENABLE_DNS_OVER_TLS
-#error This source file requires DNS-over-TLS to be enabled
-#endif
+#if ENABLE_DNS_OVER_TLS
+typedef struct DnsTlsManagerData DnsTlsManagerData;
typedef struct DnsTlsServerData DnsTlsServerData;
typedef struct DnsTlsStreamData DnsTlsStreamData;
#include "resolved-dns-stream.h"
#include "resolved-dns-transaction.h"
+#include "resolved-manager.h"
#define DNSTLS_STREAM_CLOSED 1
ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count);
ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count);
-void dnstls_server_init(DnsServer *server);
void dnstls_server_free(DnsServer *server);
+
+int dnstls_manager_init(Manager *manager);
+void dnstls_manager_free(Manager *manager);
+
+#endif /* ENABLE_DNS_OVER_TLS */
#if ! ENABLE_DNS_OVER_TLS
if (mode != DNS_OVER_TLS_NO)
- log_warning("DNS-over-TLS option for the link cannot be set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support.");
+ log_warning("DNS-over-TLS option for the link cannot be enabled or set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support.");
return;
#endif
if (r < 0)
log_warning_errno(r, "Failed to parse configuration file: %m");
+#if ENABLE_DNS_OVER_TLS
+ r = dnstls_manager_init(m);
+ if (r < 0)
+ return r;
+#endif
+
r = sd_event_default(&m->event);
if (r < 0)
return r;
while (m->dns_streams)
dns_stream_unref(m->dns_streams);
+#if ENABLE_DNS_OVER_TLS
+ dnstls_manager_free(m);
+#endif
+
hashmap_free(m->links);
hashmap_free(m->dns_transactions);
#include "resolved-dns-server.h"
#include "resolved-dns-stream.h"
#include "resolved-dns-trust-anchor.h"
+#include "resolved-dnstls.h"
#include "resolved-link.h"
#define MANAGER_SEARCH_DOMAINS_MAX 256
bool enable_cache;
DnsStubListenerMode dns_stub_listener_mode;
+#if ENABLE_DNS_OVER_TLS
+ DnsTlsManagerData dnstls_data;
+#endif
+
/* Network */
Hashmap *links;
else if (c.exit_code > 0)
log_info("Main processes terminated with: code=%s/status=%s", sigchld_code_to_string(c.exit_code), signal_to_string(c.exit_status));
- if (c.inactive_enter_usec > 0 && c.inactive_enter_usec != USEC_INFINITY &&
- c.inactive_exit_usec > 0 && c.inactive_exit_usec != USEC_INFINITY &&
+ if (timestamp_is_set(c.inactive_enter_usec) &&
+ timestamp_is_set(c.inactive_exit_usec) &&
c.inactive_enter_usec > c.inactive_exit_usec) {
char ts[FORMAT_TIMESPAN_MAX];
log_info("Service runtime: %s", format_timespan(ts, sizeof(ts), c.inactive_enter_usec - c.inactive_exit_usec, USEC_PER_MSEC));
}
static int retrieve_key(key_serial_t serial, char ***ret) {
- _cleanup_free_ char *p = NULL;
- long m = 100, n;
+ size_t nfinal, m = 100;
char **l;
+ _cleanup_(erase_and_freep) char *pfinal = NULL;
assert(ret);
for (;;) {
+ _cleanup_(erase_and_freep) char *p = NULL;
+ long n;
+
p = new(char, m);
if (!p)
return -ENOMEM;
n = keyctl(KEYCTL_READ, (unsigned long) serial, (unsigned long) p, (unsigned long) m, 0);
if (n < 0)
return -errno;
- if (n < m)
+ if ((size_t) n < m) {
+ nfinal = (size_t) n;
+ pfinal = TAKE_PTR(p);
break;
-
- explicit_bzero_safe(p, m);
+ }
if (m > LONG_MAX / 2) /* overflow check */
return -ENOMEM;
m *= 2;
- if ((long) (size_t) m != m) /* make sure that this still fits if converted to size_t */
- return -ENOMEM;
-
- free(p);
}
- l = strv_parse_nulstr(p, n);
+ l = strv_parse_nulstr(pfinal, nfinal);
if (!l)
return -ENOMEM;
- explicit_bzero_safe(p, n);
-
*ret = l;
return 0;
}
static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **passwords) {
_cleanup_strv_free_erase_ char **l = NULL;
- _cleanup_free_ char *p = NULL;
+ _cleanup_(erase_and_freep) char *p = NULL;
key_serial_t serial;
size_t n;
int r;
return r;
serial = add_key("user", keyname, p, n, KEY_SPEC_USER_KEYRING);
- explicit_bzero_safe(p, n);
if (serial == -1)
return -errno;
return 0;
}
+int bpf_program_load_from_bpf_fs(BPFProgram *p, const char *path) {
+ union bpf_attr attr;
+
+ assert(p);
+
+ if (p->kernel_fd >= 0) /* don't overwrite an assembled or loaded program */
+ return -EBUSY;
+
+ attr = (union bpf_attr) {
+ .pathname = PTR_TO_UINT64(path),
+ };
+
+ p->kernel_fd = bpf(BPF_OBJ_GET, &attr, sizeof(attr));
+ if (p->kernel_fd < 0)
+ return -errno;
+
+ return 0;
+}
+
int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path, uint32_t flags) {
_cleanup_free_ char *copy = NULL;
_cleanup_close_ int fd = -1;
int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *insn, size_t count);
int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size);
+int bpf_program_load_from_bpf_fs(BPFProgram *p, const char *path);
int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path, uint32_t flags);
int bpf_program_cgroup_detach(BPFProgram *p);
#include "condition.h"
#include "cpu-set-util.h"
#include "escape.h"
+#include "exec-util.h"
#include "hexdecoct.h"
#include "hostname-util.h"
#include "in-addr-util.h"
}
static int bus_append_exec_command(sd_bus_message *m, const char *field, const char *eq) {
- bool ignore_failure = false, explicit_path = false, done = false;
- _cleanup_strv_free_ char **l = NULL;
- _cleanup_free_ char *path = NULL;
+ bool explicit_path = false, done = false;
+ _cleanup_strv_free_ char **l = NULL, **ex_opts = NULL;
+ _cleanup_free_ char *path = NULL, *upgraded_name = NULL;
+ ExecCommandFlags flags = 0;
+ bool is_ex_prop = endswith(field, "Ex");
int r;
do {
switch (*eq) {
case '-':
- if (ignore_failure)
+ if (FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE))
done = true;
else {
- ignore_failure = true;
+ flags |= EXEC_COMMAND_IGNORE_FAILURE;
eq++;
}
break;
}
break;
+ case ':':
+ if (FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND))
+ done = true;
+ else {
+ flags |= EXEC_COMMAND_NO_ENV_EXPAND;
+ eq++;
+ }
+ break;
+
case '+':
+ if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))
+ done = true;
+ else {
+ flags |= EXEC_COMMAND_FULLY_PRIVILEGED;
+ eq++;
+ }
+ break;
+
case '!':
- /* The bus API doesn't support +, ! and !! currently, unfortunately. :-( */
- return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
- "Sorry, but +, ! and !! are currently not supported for transient services.");
+ if (flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_AMBIENT_MAGIC))
+ done = true;
+ else if (FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID)) {
+ flags &= ~EXEC_COMMAND_NO_SETUID;
+ flags |= EXEC_COMMAND_AMBIENT_MAGIC;
+ eq++;
+ } else {
+ flags |= EXEC_COMMAND_NO_SETUID;
+ eq++;
+ }
+ break;
default:
done = true;
}
} while (!done);
+ if (!is_ex_prop && (flags & (EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))) {
+ /* Upgrade the ExecXYZ= property to ExecXYZEx= for convenience */
+ is_ex_prop = true;
+ upgraded_name = strappend(field, "Ex");
+ if (!upgraded_name)
+ return log_oom();
+ }
+
+ if (is_ex_prop) {
+ r = exec_command_flags_to_strv(flags, &ex_opts);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert ExecCommandFlags to strv: %m");
+ }
+
if (explicit_path) {
- r = extract_first_word(&eq, &path, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
+ r = extract_first_word(&eq, &path, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
if (r < 0)
return log_error_errno(r, "Failed to parse path: %m");
}
- r = strv_split_extract(&l, eq, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
+ r = strv_split_extract(&l, eq, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
if (r < 0)
return log_error_errno(r, "Failed to parse command line: %m");
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
+ r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, upgraded_name ?: field);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_open_container(m, 'v', "a(sasb)");
+ r = sd_bus_message_open_container(m, 'v', is_ex_prop ? "a(sasas)" : "a(sasb)");
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_open_container(m, 'a', "(sasb)");
+ r = sd_bus_message_open_container(m, 'a', is_ex_prop ? "(sasas)" : "(sasb)");
if (r < 0)
return bus_log_create_error(r);
if (!strv_isempty(l)) {
- r = sd_bus_message_open_container(m, 'r', "sasb");
+ r = sd_bus_message_open_container(m, 'r', is_ex_prop ? "sasas" : "sasb");
if (r < 0)
return bus_log_create_error(r);
if (r < 0)
return bus_log_create_error(r);
- r = sd_bus_message_append(m, "b", ignore_failure);
+ r = is_ex_prop ? sd_bus_message_append_strv(m, ex_opts) : sd_bus_message_append(m, "b", FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE));
if (r < 0)
return bus_log_create_error(r);
if (streq(field, "DisableControllers"))
- return bus_append_strv(m, "DisableControllers", eq, EXTRACT_QUOTES);
+ return bus_append_strv(m, "DisableControllers", eq, EXTRACT_UNQUOTE);
if (streq(field, "Delegate")) {
r = parse_boolean(eq);
if (r < 0)
- return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_QUOTES);
+ return bus_append_strv(m, "DelegateControllers", eq, EXTRACT_UNQUOTE);
r = sd_bus_message_append(m, "(sv)", "Delegate", "b", r);
if (r < 0)
return 1;
}
+ if (STR_IN_SET(field, "IPIngressFilterPath", "IPEgressFilterPath")) {
+ if (isempty(eq))
+ r = sd_bus_message_append(m, "(sv)", field, "as", 0);
+ else
+ r = sd_bus_message_append(m, "(sv)", field, "as", 1, eq);
+
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
return 0;
}
"RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory",
"SupplementaryGroups", "SystemCallArchitectures"))
- return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
+ return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
if (STR_IN_SET(field, "SyslogLevel", "LogLevelMax"))
if (STR_IN_SET(field, "Environment", "UnsetEnvironment", "PassEnvironment"))
- return bus_append_strv(m, field, eq, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
+ return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
if (streq(field, "EnvironmentFile")) {
return bus_append_byte_array(m, field, array, allocated);
}
+ if (streq(field, "NUMAPolicy")) {
+ r = mpol_from_string(eq);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
+
+ r = sd_bus_message_append(m, "(sv)", field, "i", (int32_t) r);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ return 1;
+ }
+
+ if (streq(field, "NUMAMask")) {
+ _cleanup_(cpu_set_reset) CPUSet nodes = {};
+ _cleanup_free_ uint8_t *array = NULL;
+ size_t allocated;
+
+ r = parse_cpu_set(eq, &nodes);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
+
+ r = cpu_set_to_dbus(&nodes, &array, &allocated);
+ if (r < 0)
+ return log_error_errno(r, "Failed to serialize NUMAMask: %m");
+
+ return bus_append_byte_array(m, field, array, allocated);
+ }
+
if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) {
int whitelist = 1;
const char *p = eq;
for (;;) {
_cleanup_free_ char *word = NULL;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
bool ignore_enoent = false;
uint64_t flags = MS_REC;
- r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
+ r = extract_first_word(&p, &source, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
if (r == 0)
}
if (p && p[-1] == ':') {
- r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_QUOTES|EXTRACT_DONT_COALESCE_SEPARATORS);
+ r = extract_first_word(&p, &destination, ":" WHITESPACE, EXTRACT_UNQUOTE|EXTRACT_DONT_COALESCE_SEPARATORS);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
if (r == 0)
if (p && p[-1] == ':') {
_cleanup_free_ char *options = NULL;
- r = extract_first_word(&p, &options, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &options, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
_cleanup_free_ char *word = NULL, *path = NULL;
const char *w;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r < 0)
return log_error_errno(r, "Failed to parse argument: %m");
if (r == 0)
if (STR_IN_SET(field,
"ExecStartPre", "ExecStart", "ExecStartPost",
+ "ExecStartPreEx", "ExecStartEx", "ExecStartPostEx",
"ExecReload", "ExecStop", "ExecStopPost"))
-
return bus_append_exec_command(m, field, eq);
if (STR_IN_SET(field, "RestartPreventExitStatus", "RestartForceExitStatus", "SuccessExitStatus")) {
_cleanup_free_ char *word = NULL;
int val;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0)
break;
if (r == -ENOMEM)
if (streq(field, "Symlinks"))
- return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
+ return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
if (streq(field, "SocketProtocol"))
if (unit_dependency_from_string(field) >= 0 ||
STR_IN_SET(field, "Documentation", "RequiresMountsFor"))
- return bus_append_strv(m, field, eq, EXTRACT_QUOTES);
+ return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
t = condition_type_from_string(field);
if (t >= 0)
#include "mountpoint-util.h"
#include "nsflags.h"
#include "parse-util.h"
+#include "path-util.h"
#include "proc-cmdline.h"
#include "rlimit-util.h"
#include "stdio-util.h"
if (!external_label)
return -ENOMEM;
- p = strjoin(prefix, "/", sender_label, "/", external_label);
+ p = path_join(prefix, sender_label, external_label);
if (!p)
return -ENOMEM;
return sd_bus_send(NULL, reply, NULL);
}
+
+static void bus_message_unref_wrapper(void *m) {
+ sd_bus_message_unref(m);
+}
+
+const struct hash_ops bus_message_hash_ops = {
+ .hash = trivial_hash_func,
+ .compare = trivial_compare_func,
+ .free_value = bus_message_unref_wrapper,
+};
}
int bus_reply_pair_array(sd_bus_message *m, char **l);
+
+extern const struct hash_ops bus_message_hash_ops;
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "bus-util.h"
+#include "bus-wait-for-units.h"
+#include "hashmap.h"
+#include "string-util.h"
+#include "strv.h"
+#include "unit-def.h"
+
+typedef struct WaitForItem {
+ BusWaitForUnits *parent;
+
+ BusWaitForUnitsFlags flags;
+
+ char *bus_path;
+
+ sd_bus_slot *slot_get_all;
+ sd_bus_slot *slot_properties_changed;
+
+ bus_wait_for_units_unit_callback unit_callback;
+ void *userdata;
+
+ char *active_state;
+ uint32_t job_id;
+ char *clean_result;
+} WaitForItem;
+
+typedef struct BusWaitForUnits {
+ sd_bus *bus;
+ sd_bus_slot *slot_disconnected;
+
+ Hashmap *items;
+
+ bus_wait_for_units_ready_callback ready_callback;
+ void *userdata;
+
+ WaitForItem *current;
+
+ BusWaitForUnitsState state;
+ bool has_failed:1;
+} BusWaitForUnits;
+
+static WaitForItem *wait_for_item_free(WaitForItem *item) {
+ int r;
+
+ if (!item)
+ return NULL;
+
+ if (item->parent) {
+ if (FLAGS_SET(item->flags, BUS_WAIT_REFFED) && item->bus_path && item->parent->bus) {
+ r = sd_bus_call_method_async(
+ item->parent->bus,
+ NULL,
+ "org.freedesktop.systemd1",
+ item->bus_path,
+ "org.freedesktop.systemd1.Unit",
+ "Unref",
+ NULL,
+ NULL,
+ NULL);
+ if (r < 0)
+ log_debug_errno(r, "Failed to drop reference to unit %s, ignoring: %m", item->bus_path);
+ }
+
+ assert_se(hashmap_remove(item->parent->items, item->bus_path) == item);
+
+ if (item->parent->current == item)
+ item->parent->current = NULL;
+ }
+
+ sd_bus_slot_unref(item->slot_properties_changed);
+ sd_bus_slot_unref(item->slot_get_all);
+
+ free(item->bus_path);
+ free(item->active_state);
+ free(item->clean_result);
+
+ return mfree(item);
+}
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(WaitForItem*, wait_for_item_free);
+
+static void bus_wait_for_units_clear(BusWaitForUnits *d) {
+ WaitForItem *item;
+
+ assert(d);
+
+ d->slot_disconnected = sd_bus_slot_unref(d->slot_disconnected);
+ d->bus = sd_bus_unref(d->bus);
+
+ while ((item = hashmap_first(d->items))) {
+ d->current = item;
+
+ item->unit_callback(d, item->bus_path, false, item->userdata);
+ wait_for_item_free(item);
+ }
+
+ d->items = hashmap_free(d->items);
+}
+
+static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ BusWaitForUnits *d = userdata;
+
+ assert(m);
+ assert(d);
+
+ log_error("Warning! D-Bus connection terminated.");
+
+ bus_wait_for_units_clear(d);
+
+ if (d->ready_callback)
+ d->ready_callback(d, false, d->userdata);
+ else /* If no ready callback is specified close the connection so that the event loop exits */
+ sd_bus_close(sd_bus_message_get_bus(m));
+
+ return 0;
+}
+
+int bus_wait_for_units_new(sd_bus *bus, BusWaitForUnits **ret) {
+ _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *d = NULL;
+ int r;
+
+ assert(bus);
+ assert(ret);
+
+ d = new(BusWaitForUnits, 1);
+ if (!d)
+ return -ENOMEM;
+
+ *d = (BusWaitForUnits) {
+ .state = BUS_WAIT_SUCCESS,
+ .bus = sd_bus_ref(bus),
+ };
+
+ r = sd_bus_match_signal_async(
+ bus,
+ &d->slot_disconnected,
+ "org.freedesktop.DBus.Local",
+ NULL,
+ "org.freedesktop.DBus.Local",
+ "Disconnected",
+ match_disconnected, NULL, d);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(d);
+ return 0;
+}
+
+BusWaitForUnits* bus_wait_for_units_free(BusWaitForUnits *d) {
+ if (!d)
+ return NULL;
+
+ bus_wait_for_units_clear(d);
+ sd_bus_slot_unref(d->slot_disconnected);
+ sd_bus_unref(d->bus);
+
+ return mfree(d);
+}
+
+static bool bus_wait_for_units_is_ready(BusWaitForUnits *d) {
+ assert(d);
+
+ if (!d->bus) /* Disconnected? */
+ return true;
+
+ return hashmap_isempty(d->items);
+}
+
+void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata) {
+ assert(d);
+
+ d->ready_callback = callback;
+ d->userdata = userdata;
+}
+
+static void bus_wait_for_units_check_ready(BusWaitForUnits *d) {
+ assert(d);
+
+ if (!bus_wait_for_units_is_ready(d))
+ return;
+
+ d->state = d->has_failed ? BUS_WAIT_FAILURE : BUS_WAIT_SUCCESS;
+
+ if (d->ready_callback)
+ d->ready_callback(d, d->state, d->userdata);
+}
+
+static void wait_for_item_check_ready(WaitForItem *item) {
+ BusWaitForUnits *d;
+
+ assert(item);
+ assert(d = item->parent);
+
+ if (FLAGS_SET(item->flags, BUS_WAIT_FOR_MAINTENANCE_END)) {
+
+ if (item->clean_result && !streq(item->clean_result, "success"))
+ d->has_failed = true;
+
+ if (!item->active_state || streq(item->active_state, "maintenance"))
+ return;
+ }
+
+ if (FLAGS_SET(item->flags, BUS_WAIT_NO_JOB) && item->job_id != 0)
+ return;
+
+ if (FLAGS_SET(item->flags, BUS_WAIT_FOR_INACTIVE)) {
+
+ if (streq_ptr(item->active_state, "failed"))
+ d->has_failed = true;
+ else if (!streq_ptr(item->active_state, "inactive"))
+ return;
+ }
+
+ if (item->unit_callback) {
+ d->current = item;
+ item->unit_callback(d, item->bus_path, true, item->userdata);
+ }
+
+ wait_for_item_free(item);
+
+ bus_wait_for_units_check_ready(d);
+}
+
+static int property_map_job(
+ sd_bus *bus,
+ const char *member,
+ sd_bus_message *m,
+ sd_bus_error *error,
+ void *userdata) {
+
+ WaitForItem *item = userdata;
+ const char *path;
+ uint32_t id;
+ int r;
+
+ assert(item);
+
+ r = sd_bus_message_read(m, "(uo)", &id, &path);
+ if (r < 0)
+ return r;
+
+ item->job_id = id;
+ return 0;
+}
+
+static int wait_for_item_parse_properties(WaitForItem *item, sd_bus_message *m) {
+
+ static const struct bus_properties_map map[] = {
+ { "ActiveState", "s", NULL, offsetof(WaitForItem, active_state) },
+ { "Job", "(uo)", property_map_job, 0 },
+ { "CleanResult", "s", NULL, offsetof(WaitForItem, clean_result) },
+ {}
+ };
+
+ int r;
+
+ assert(item);
+ assert(m);
+
+ r = bus_message_map_all_properties(m, map, BUS_MAP_STRDUP, NULL, item);
+ if (r < 0)
+ return r;
+
+ wait_for_item_check_ready(item);
+ return 0;
+}
+
+static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ WaitForItem *item = userdata;
+ const char *interface;
+ int r;
+
+ assert(item);
+
+ r = sd_bus_message_read(m, "s", &interface);
+ if (r < 0) {
+ log_debug_errno(r, "Failed to parse PropertiesChanged signal: %m");
+ return 0;
+ }
+
+ if (!streq(interface, "org.freedesktop.systemd1.Unit"))
+ return 0;
+
+ r = wait_for_item_parse_properties(item, m);
+ if (r < 0)
+ log_debug_errno(r, "Failed to process PropertiesChanged signal: %m");
+
+ return 0;
+}
+
+static int on_get_all_properties(sd_bus_message *m, void *userdata, sd_bus_error *error) {
+ WaitForItem *item = userdata;
+ int r;
+
+ assert(item);
+
+ if (sd_bus_error_is_set(error)) {
+ BusWaitForUnits *d = item->parent;
+
+ d->has_failed = true;
+
+ log_debug_errno(sd_bus_error_get_errno(error), "GetAll() failed for %s: %s",
+ item->bus_path, error->message);
+
+ d->current = item;
+ item->unit_callback(d, item->bus_path, false, item->userdata);
+ wait_for_item_free(item);
+
+ bus_wait_for_units_check_ready(d);
+ return 0;
+ }
+
+ r = wait_for_item_parse_properties(item, m);
+ if (r < 0)
+ log_debug_errno(r, "Failed to process GetAll method reply: %m");
+
+ return 0;
+}
+
+int bus_wait_for_units_add_unit(
+ BusWaitForUnits *d,
+ const char *unit,
+ BusWaitForUnitsFlags flags,
+ bus_wait_for_units_unit_callback callback,
+ void *userdata) {
+
+ _cleanup_(wait_for_item_freep) WaitForItem *item = NULL;
+ int r;
+
+ assert(d);
+ assert(unit);
+
+ assert(flags != 0);
+
+ r = hashmap_ensure_allocated(&d->items, &string_hash_ops);
+ if (r < 0)
+ return r;
+
+ item = new(WaitForItem, 1);
+ if (!item)
+ return -ENOMEM;
+
+ *item = (WaitForItem) {
+ .flags = flags,
+ .bus_path = unit_dbus_path_from_name(unit),
+ .unit_callback = callback,
+ .userdata = userdata,
+ .job_id = UINT32_MAX,
+ };
+
+ if (!item->bus_path)
+ return -ENOMEM;
+
+ if (!FLAGS_SET(item->flags, BUS_WAIT_REFFED)) {
+ r = sd_bus_call_method_async(
+ d->bus,
+ NULL,
+ "org.freedesktop.systemd1",
+ item->bus_path,
+ "org.freedesktop.systemd1.Unit",
+ "Ref",
+ NULL,
+ NULL,
+ NULL);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add reference to unit %s: %m", unit);
+
+
+ item->flags |= BUS_WAIT_REFFED;
+ }
+
+ r = sd_bus_match_signal_async(
+ d->bus,
+ &item->slot_properties_changed,
+ "org.freedesktop.systemd1",
+ item->bus_path,
+ "org.freedesktop.DBus.Properties",
+ "PropertiesChanged",
+ on_properties_changed,
+ NULL,
+ item);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to request match for PropertiesChanged signal: %m");
+
+ r = sd_bus_call_method_async(
+ d->bus,
+ &item->slot_get_all,
+ "org.freedesktop.systemd1",
+ item->bus_path,
+ "org.freedesktop.DBus.Properties",
+ "GetAll",
+ on_get_all_properties,
+ item,
+ "s", FLAGS_SET(item->flags, BUS_WAIT_FOR_MAINTENANCE_END) ? NULL : "org.freedesktop.systemd1.Unit");
+ if (r < 0)
+ return log_debug_errno(r, "Failed to request properties of unit %s: %m", unit);
+
+ r = hashmap_put(d->items, item->bus_path, item);
+ if (r < 0)
+ return r;
+
+ d->state = BUS_WAIT_RUNNING;
+ item->parent = d;
+ TAKE_PTR(item);
+ return 0;
+}
+
+int bus_wait_for_units_run(BusWaitForUnits *d) {
+ int r;
+
+ assert(d);
+
+ while (d->state == BUS_WAIT_RUNNING) {
+
+ r = sd_bus_process(d->bus, NULL);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ continue;
+
+ r = sd_bus_wait(d->bus, (uint64_t) -1);
+ if (r < 0)
+ return r;
+ }
+
+ return d->state;
+}
+
+BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d) {
+ assert(d);
+
+ return d->state;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "macro.h"
+#include "sd-bus.h"
+
+typedef struct BusWaitForUnits BusWaitForUnits;
+
+typedef enum BusWaitForUnitsState {
+ BUS_WAIT_SUCCESS, /* Nothing to wait for anymore and nothing failed */
+ BUS_WAIT_FAILURE, /* dito, but something failed */
+ BUS_WAIT_RUNNING, /* Still something to wait for */
+ _BUS_WAIT_FOR_UNITS_STATE_MAX,
+ _BUS_WAIT_FOR_UNITS_STATE_INVALID = -1,
+} BusWaitForUnitsState;
+
+typedef enum BusWaitForUnitsFlags {
+ BUS_WAIT_FOR_MAINTENANCE_END = 1 << 0, /* Wait until the unit is no longer in maintenance state */
+ BUS_WAIT_FOR_INACTIVE = 1 << 1, /* Wait until the unit is back in inactive or dead state */
+ BUS_WAIT_NO_JOB = 1 << 2, /* Wait until there's no more job pending */
+ BUS_WAIT_REFFED = 1 << 3, /* The unit is already reffed with RefUnit() */
+} BusWaitForUnitsFlags;
+
+typedef void (*bus_wait_for_units_ready_callback)(BusWaitForUnits *d, BusWaitForUnitsState state, void *userdata);
+typedef void (*bus_wait_for_units_unit_callback)(BusWaitForUnits *d, const char *unit_path, bool good, void *userdata);
+
+int bus_wait_for_units_new(sd_bus *bus, BusWaitForUnits **ret);
+BusWaitForUnits* bus_wait_for_units_free(BusWaitForUnits *d);
+
+BusWaitForUnitsState bus_wait_for_units_state(BusWaitForUnits *d);
+void bus_wait_for_units_set_ready_callback(BusWaitForUnits *d, bus_wait_for_units_ready_callback callback, void *userdata);
+int bus_wait_for_units_add_unit(BusWaitForUnits *d, const char *unit, BusWaitForUnitsFlags flags, bus_wait_for_units_unit_callback callback, void *userdata);
+int bus_wait_for_units_run(BusWaitForUnits *d);
+
+DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForUnits*, bus_wait_for_units_free);
while ((r = cg_read_subgroup(d, &gn)) > 0) {
_cleanup_free_ char *k = NULL;
- k = strjoin(fn, "/", gn);
+ k = path_join(fn, gn);
free(gn);
if (!k)
return -ENOMEM;
_cleanup_free_ char *word = NULL;
bool found;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
if (r < 0)
return r;
if (r == 0)
OrderOperator order;
struct utsname u;
const char *p;
+ bool first = true;
assert(c);
assert(c->parameter);
assert_se(uname(&u) >= 0);
p = c->parameter;
- order = parse_order(&p);
- /* No prefix? Then treat as glob string */
- if (order < 0)
- return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0;
+ for (;;) {
+ _cleanup_free_ char *word = NULL;
+ const char *s;
+ int r;
+
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse condition string \"%s\": %m", p);
+ if (r == 0)
+ break;
- return test_order(str_verscmp(u.release, skip_leading_chars(p, NULL)), order);
+ s = strstrip(word);
+ order = parse_order(&s);
+ if (order >= 0) {
+ s += strspn(s, WHITESPACE);
+ if (isempty(s)) {
+ if (first) {
+ /* For backwards compatibility, allow whitespace between the operator and
+ * value, without quoting, but only in the first expression. */
+ word = mfree(word);
+ r = extract_first_word(&p, &word, NULL, 0);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse condition string \"%s\": %m", p);
+ if (r == 0)
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
+ s = word;
+ } else
+ return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Unexpected end of expression: %s", p);
+ }
+
+ r = test_order(str_verscmp(u.release, s), order);
+ } else
+ /* No prefix? Then treat as glob string */
+ r = fnmatch(s, u.release, 0) == 0;
+
+ if (r == 0)
+ return false;
+
+ first = false;
+ }
+
+ return true;
}
static int condition_test_memory(Condition *c) {
int condition_test(Condition *c) {
static int (*const condition_tests[_CONDITION_TYPE_MAX])(Condition *c) = {
- [CONDITION_PATH_EXISTS] = condition_test_path_exists,
- [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
- [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
- [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
- [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
- [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
- [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
- [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
- [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
- [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
- [CONDITION_KERNEL_VERSION] = condition_test_kernel_version,
- [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
- [CONDITION_SECURITY] = condition_test_security,
- [CONDITION_CAPABILITY] = condition_test_capability,
- [CONDITION_HOST] = condition_test_host,
- [CONDITION_AC_POWER] = condition_test_ac_power,
- [CONDITION_ARCHITECTURE] = condition_test_architecture,
- [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
- [CONDITION_FIRST_BOOT] = condition_test_first_boot,
- [CONDITION_USER] = condition_test_user,
- [CONDITION_GROUP] = condition_test_group,
+ [CONDITION_PATH_EXISTS] = condition_test_path_exists,
+ [CONDITION_PATH_EXISTS_GLOB] = condition_test_path_exists_glob,
+ [CONDITION_PATH_IS_DIRECTORY] = condition_test_path_is_directory,
+ [CONDITION_PATH_IS_SYMBOLIC_LINK] = condition_test_path_is_symbolic_link,
+ [CONDITION_PATH_IS_MOUNT_POINT] = condition_test_path_is_mount_point,
+ [CONDITION_PATH_IS_READ_WRITE] = condition_test_path_is_read_write,
+ [CONDITION_DIRECTORY_NOT_EMPTY] = condition_test_directory_not_empty,
+ [CONDITION_FILE_NOT_EMPTY] = condition_test_file_not_empty,
+ [CONDITION_FILE_IS_EXECUTABLE] = condition_test_file_is_executable,
+ [CONDITION_KERNEL_COMMAND_LINE] = condition_test_kernel_command_line,
+ [CONDITION_KERNEL_VERSION] = condition_test_kernel_version,
+ [CONDITION_VIRTUALIZATION] = condition_test_virtualization,
+ [CONDITION_SECURITY] = condition_test_security,
+ [CONDITION_CAPABILITY] = condition_test_capability,
+ [CONDITION_HOST] = condition_test_host,
+ [CONDITION_AC_POWER] = condition_test_ac_power,
+ [CONDITION_ARCHITECTURE] = condition_test_architecture,
+ [CONDITION_NEEDS_UPDATE] = condition_test_needs_update,
+ [CONDITION_FIRST_BOOT] = condition_test_first_boot,
+ [CONDITION_USER] = condition_test_user,
+ [CONDITION_GROUP] = condition_test_group,
[CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller,
- [CONDITION_NULL] = condition_test_null,
- [CONDITION_CPUS] = condition_test_cpus,
- [CONDITION_MEMORY] = condition_test_memory,
+ [CONDITION_NULL] = condition_test_null,
+ [CONDITION_CPUS] = condition_test_cpus,
+ [CONDITION_MEMORY] = condition_test_memory,
};
int r, b;
r = condition_test(c);
if (logger) {
+ const char *p = c->type == CONDITION_NULL ? "true" : c->parameter;
+ assert(p);
+
if (r < 0)
- logger(userdata, LOG_WARNING, r, __FILE__, __LINE__, __func__,
+ logger(userdata, LOG_WARNING, r, PROJECT_FILE, __LINE__, __func__,
"Couldn't determine result for %s=%s%s%s, assuming failed: %m",
to_string(c->type),
c->trigger ? "|" : "",
c->negate ? "!" : "",
- c->parameter);
+ p);
else
- logger(userdata, LOG_DEBUG, 0, __FILE__, __LINE__, __func__,
+ logger(userdata, LOG_DEBUG, 0, PROJECT_FILE, __LINE__, __func__,
"%s=%s%s%s %s.",
to_string(c->type),
c->trigger ? "|" : "",
c->negate ? "!" : "",
- c->parameter,
+ p,
condition_result_to_string(c->result));
}
return r;
}
- if (strchr(COMMENTS, *skip_leading_chars(buf, WHITESPACE)))
+ l = skip_leading_chars(buf, WHITESPACE);
+ if (*l != '\0' && strchr(COMMENTS, *l))
continue;
l = buf;
for (;;) {
char *word = NULL;
- r = extract_first_word(&rvalue, &word, NULL, EXTRACT_QUOTES|EXTRACT_RETAIN_ESCAPE);
+ r = extract_first_word(&rvalue, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RETAIN_ESCAPE);
if (r == 0)
break;
if (r == -ENOMEM)
#include "alloc-util.h"
#include "cpu-set-util.h"
+#include "dirent-util.h"
+#include "errno-util.h"
#include "extract-word.h"
+#include "fd-util.h"
#include "log.h"
#include "macro.h"
#include "memory-util.h"
+#include "missing_syscall.h"
#include "parse-util.h"
+#include "stat-util.h"
#include "string-util.h"
+#include "string-table.h"
+#include "strv.h"
+#include "util.h"
char* cpu_set_to_string(const CPUSet *a) {
_cleanup_free_ char *str = NULL;
unsigned cpu_lower, cpu_upper;
int r;
- r = extract_first_word(&p, &word, WHITESPACE ",", EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, WHITESPACE ",", EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return warn ? log_oom() : -ENOMEM;
if (r < 0)
s = (CPUSet) {};
return 0;
}
+
+bool numa_policy_is_valid(const NUMAPolicy *policy) {
+ assert(policy);
+
+ if (!mpol_is_valid(numa_policy_get_type(policy)))
+ return false;
+
+ if (!policy->nodes.set &&
+ !IN_SET(numa_policy_get_type(policy), MPOL_DEFAULT, MPOL_LOCAL, MPOL_PREFERRED))
+ return false;
+
+ if (policy->nodes.set &&
+ numa_policy_get_type(policy) == MPOL_PREFERRED &&
+ CPU_COUNT_S(policy->nodes.allocated, policy->nodes.set) != 1)
+ return false;
+
+ return true;
+}
+
+static int numa_policy_to_mempolicy(const NUMAPolicy *policy, unsigned long *ret_maxnode, unsigned long **ret_nodes) {
+ unsigned node, bits = 0, ulong_bits;
+ _cleanup_free_ unsigned long *out = NULL;
+
+ assert(policy);
+ assert(ret_maxnode);
+ assert(ret_nodes);
+
+ if (IN_SET(numa_policy_get_type(policy), MPOL_DEFAULT, MPOL_LOCAL) ||
+ (numa_policy_get_type(policy) == MPOL_PREFERRED && !policy->nodes.set)) {
+ *ret_nodes = NULL;
+ *ret_maxnode = 0;
+ return 0;
+ }
+
+ bits = policy->nodes.allocated * 8;
+ ulong_bits = sizeof(unsigned long) * 8;
+
+ out = new0(unsigned long, DIV_ROUND_UP(policy->nodes.allocated, sizeof(unsigned long)));
+ if (!out)
+ return -ENOMEM;
+
+ /* We don't make any assumptions about internal type libc is using to store NUMA node mask.
+ Hence we need to convert the node mask to the representation expected by set_mempolicy() */
+ for (node = 0; node < bits; node++)
+ if (CPU_ISSET_S(node, policy->nodes.allocated, policy->nodes.set))
+ out[node / ulong_bits] |= 1ul << (node % ulong_bits);
+
+ *ret_nodes = TAKE_PTR(out);
+ *ret_maxnode = bits + 1;
+ return 0;
+}
+
+int apply_numa_policy(const NUMAPolicy *policy) {
+ int r;
+ _cleanup_free_ unsigned long *nodes = NULL;
+ unsigned long maxnode;
+
+ assert(policy);
+
+ if (get_mempolicy(NULL, NULL, 0, 0, 0) < 0 && errno == ENOSYS)
+ return -EOPNOTSUPP;
+
+ if (!numa_policy_is_valid(policy))
+ return -EINVAL;
+
+ r = numa_policy_to_mempolicy(policy, &maxnode, &nodes);
+ if (r < 0)
+ return r;
+
+ r = set_mempolicy(numa_policy_get_type(policy), nodes, maxnode);
+ if (r < 0)
+ return -errno;
+
+ return 0;
+}
+
+static const char* const mpol_table[] = {
+ [MPOL_DEFAULT] = "default",
+ [MPOL_PREFERRED] = "preferred",
+ [MPOL_BIND] = "bind",
+ [MPOL_INTERLEAVE] = "interleave",
+ [MPOL_LOCAL] = "local",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(mpol, int);
#include <sched.h>
#include "macro.h"
+#include "missing_syscall.h"
/* This wraps the libc interface with a variable to keep the allocated size. */
typedef struct CPUSet {
int cpu_set_from_dbus(const uint8_t *bits, size_t size, CPUSet *set);
int cpus_in_affinity_mask(void);
+
+static inline bool mpol_is_valid(int t) {
+ return t >= MPOL_DEFAULT && t <= MPOL_LOCAL;
+}
+
+typedef struct NUMAPolicy {
+ /* Always use numa_policy_get_type() to read the value */
+ int type;
+ CPUSet nodes;
+} NUMAPolicy;
+
+bool numa_policy_is_valid(const NUMAPolicy *p);
+
+static inline int numa_policy_get_type(const NUMAPolicy *p) {
+ return p->type < 0 ? (p->nodes.set ? MPOL_PREFERRED : -1) : p->type;
+}
+
+static inline void numa_policy_reset(NUMAPolicy *p) {
+ assert(p);
+ cpu_set_reset(&p->nodes);
+ p->type = -1;
+}
+
+int apply_numa_policy(const NUMAPolicy *policy);
+
+const char* mpol_to_string(int i) _const_;
+int mpol_from_string(const char *s) _pure_;
}
if (prefix) {
- link_name = prefix_root(prefix, k);
+ link_name = path_join(prefix, k);
if (!link_name)
return -ENOMEM;
for (i = 0; i < ELEMENTSOF(table); i++) {
_cleanup_free_ char *path = NULL;
- path = prefix_root(root, table[i].name);
+ path = path_join(root, table[i].name);
if (!path)
return log_oom();
#include "device-nodes.h"
#include "device-util.h"
#include "dissect-image.h"
+#include "dm-util.h"
#include "env-file.h"
#include "fd-util.h"
#include "fileio.h"
if (!filename_is_valid(name))
return -EINVAL;
- node = strjoin(crypt_get_dir(), "/", name);
+ node = path_join(crypt_get_dir(), name);
if (!node)
return -ENOMEM;
}
}
-#if HAVE_LIBCRYPTSETUP
-static int deferred_remove(DecryptedPartition *p) {
- struct dm_ioctl dm = {
- .version = {
- DM_VERSION_MAJOR,
- DM_VERSION_MINOR,
- DM_VERSION_PATCHLEVEL
- },
- .data_size = sizeof(dm),
- .flags = DM_DEFERRED_REMOVE,
- };
-
- _cleanup_close_ int fd = -1;
-
- assert(p);
-
- /* Unfortunately, libcryptsetup doesn't provide a proper API for this, hence call the ioctl() directly. */
-
- fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
- if (fd < 0)
- return -errno;
-
- if (strlen(p->name) > sizeof(dm.name))
- return -ENAMETOOLONG;
-
- strncpy(dm.name, p->name, sizeof(dm.name));
-
- if (ioctl(fd, DM_DEV_REMOVE, &dm))
- return -errno;
-
- return 0;
-}
-#endif
-
int decrypted_image_relinquish(DecryptedImage *d) {
#if HAVE_LIBCRYPTSETUP
if (p->relinquished)
continue;
- r = deferred_remove(p);
+ r = dm_deferred_remove(p->name);
if (r < 0)
return log_debug_errno(r, "Failed to mark %s for auto-removal: %m", p->name);
--- /dev/null
+#include <fcntl.h>
+#include <linux/dm-ioctl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "dm-util.h"
+#include "fd-util.h"
+#include "string-util.h"
+
+int dm_deferred_remove(const char *name) {
+
+ struct dm_ioctl dm = {
+ .version = {
+ DM_VERSION_MAJOR,
+ DM_VERSION_MINOR,
+ DM_VERSION_PATCHLEVEL
+ },
+ .data_size = sizeof(dm),
+ .flags = DM_DEFERRED_REMOVE,
+ };
+
+ _cleanup_close_ int fd = -1;
+
+ assert(name);
+
+ /* Unfortunately, libcryptsetup doesn't provide a proper API for this, hence call the ioctl()
+ * directly. */
+
+ if (strlen(name) >= sizeof(dm.name))
+ return -ENODEV; /* A device with a name longer than this cannot possibly exist */
+
+ fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ strncpy_exact(dm.name, name, sizeof(dm.name));
+
+ if (ioctl(fd, DM_DEV_REMOVE, &dm))
+ return -errno;
+
+ return 0;
+}
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+int dm_deferred_remove(const char *name);
#include "sd-id128.h"
+#include "efi/loader-features.h"
#include "time-util.h"
#define EFI_VENDOR_LOADER SD_ID128_MAKE(4a,67,b0,82,0a,4c,41,cf,b6,c7,44,0b,29,bb,8c,4f)
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002
#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004
-#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT (UINT64_C(1) << 0)
-#define EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT (UINT64_C(1) << 1)
-#define EFI_LOADER_FEATURE_ENTRY_DEFAULT (UINT64_C(1) << 2)
-#define EFI_LOADER_FEATURE_ENTRY_ONESHOT (UINT64_C(1) << 3)
-#define EFI_LOADER_FEATURE_BOOT_COUNTING (UINT64_C(1) << 4)
-
#if ENABLE_EFI
bool is_efi_boot(void);
#include "conf-parser.h"
#include "ethtool-util.h"
-#include "link-config.h"
+#include "extract-word.h"
#include "log.h"
#include "memory-util.h"
#include "missing.h"
};
static const char* const ethtool_link_mode_bit_table[] = {
- [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = "10baset-half",
- [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = "10baset-full",
- [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = "100baset-half",
- [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = "100baset-full",
- [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = "1000baset-half",
- [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = "1000baset-full",
- [ETHTOOL_LINK_MODE_Autoneg_BIT] = "autonegotiation",
- [ETHTOOL_LINK_MODE_TP_BIT] = "tp",
- [ETHTOOL_LINK_MODE_AUI_BIT] = "aui",
- [ETHTOOL_LINK_MODE_MII_BIT] = "mii",
- [ETHTOOL_LINK_MODE_FIBRE_BIT] = "fibre",
- [ETHTOOL_LINK_MODE_BNC_BIT] = "bnc",
- [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = "10000baset-full",
- [ETHTOOL_LINK_MODE_Pause_BIT] = "pause",
- [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = "asym-pause",
- [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = "2500basex-full",
- [ETHTOOL_LINK_MODE_Backplane_BIT] = "backplane",
- [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = "1000basekx-full",
- [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = "10000basekx4-full",
- [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = "10000basekr-full",
- [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = "10000baser-fec",
- [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = "20000basemld2-full",
- [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = "20000basekr2-full",
- [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = "40000basekr4-full",
- [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = "40000basecr4-full",
- [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = "40000basesr4-full",
- [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = "40000baselr4-full",
- [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = "56000basekr4-full",
- [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = "56000basecr4-full",
- [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = "56000basesr4-full",
- [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = "56000baselr4-full",
- [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = "25000basecr-full",
- [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = "25000basekr-full",
- [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = "25000basesr-full",
- [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = "50000basecr2-full",
- [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = "50000basekr2-full",
- [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = "100000basekr4-full",
- [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = "100000basesr4-full",
- [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = "100000basecr4-full",
- [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = "100000baselr4-er4-full",
- [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = "50000basesr2-full",
- [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = "1000basex-full",
- [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = "10000basecr-full",
- [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = "10000basesr-full",
- [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = "10000baselr-full",
- [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = "10000baselrm-full",
- [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = "10000baseer-full",
- [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = "2500baset-full",
- [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = "5000baset-full",
- [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = "fec-none",
- [ETHTOOL_LINK_MODE_FEC_RS_BIT] = "fec-rs",
- [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = "fec-baser",
+ [ETHTOOL_LINK_MODE_10baseT_Half_BIT] = "10baset-half",
+ [ETHTOOL_LINK_MODE_10baseT_Full_BIT] = "10baset-full",
+ [ETHTOOL_LINK_MODE_100baseT_Half_BIT] = "100baset-half",
+ [ETHTOOL_LINK_MODE_100baseT_Full_BIT] = "100baset-full",
+ [ETHTOOL_LINK_MODE_1000baseT_Half_BIT] = "1000baset-half",
+ [ETHTOOL_LINK_MODE_1000baseT_Full_BIT] = "1000baset-full",
+ [ETHTOOL_LINK_MODE_Autoneg_BIT] = "autonegotiation",
+ [ETHTOOL_LINK_MODE_TP_BIT] = "tp",
+ [ETHTOOL_LINK_MODE_AUI_BIT] = "aui",
+ [ETHTOOL_LINK_MODE_MII_BIT] = "mii",
+ [ETHTOOL_LINK_MODE_FIBRE_BIT] = "fibre",
+ [ETHTOOL_LINK_MODE_BNC_BIT] = "bnc",
+ [ETHTOOL_LINK_MODE_10000baseT_Full_BIT] = "10000baset-full",
+ [ETHTOOL_LINK_MODE_Pause_BIT] = "pause",
+ [ETHTOOL_LINK_MODE_Asym_Pause_BIT] = "asym-pause",
+ [ETHTOOL_LINK_MODE_2500baseX_Full_BIT] = "2500basex-full",
+ [ETHTOOL_LINK_MODE_Backplane_BIT] = "backplane",
+ [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT] = "1000basekx-full",
+ [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT] = "10000basekx4-full",
+ [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT] = "10000basekr-full",
+ [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT] = "10000baser-fec",
+ [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT] = "20000basemld2-full",
+ [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT] = "20000basekr2-full",
+ [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT] = "40000basekr4-full",
+ [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT] = "40000basecr4-full",
+ [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT] = "40000basesr4-full",
+ [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT] = "40000baselr4-full",
+ [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT] = "56000basekr4-full",
+ [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT] = "56000basecr4-full",
+ [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT] = "56000basesr4-full",
+ [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT] = "56000baselr4-full",
+ [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT] = "25000basecr-full",
+ [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT] = "25000basekr-full",
+ [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT] = "25000basesr-full",
+ [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT] = "50000basecr2-full",
+ [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT] = "50000basekr2-full",
+ [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT] = "100000basekr4-full",
+ [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT] = "100000basesr4-full",
+ [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT] = "100000basecr4-full",
+ [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT] = "100000baselr4-er4-full",
+ [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT] = "50000basesr2-full",
+ [ETHTOOL_LINK_MODE_1000baseX_Full_BIT] = "1000basex-full",
+ [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT] = "10000basecr-full",
+ [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT] = "10000basesr-full",
+ [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT] = "10000baselr-full",
+ [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT] = "10000baselrm-full",
+ [ETHTOOL_LINK_MODE_10000baseER_Full_BIT] = "10000baseer-full",
+ [ETHTOOL_LINK_MODE_2500baseT_Full_BIT] = "2500baset-full",
+ [ETHTOOL_LINK_MODE_5000baseT_Full_BIT] = "5000baset-full",
+ [ETHTOOL_LINK_MODE_FEC_NONE_BIT] = "fec-none",
+ [ETHTOOL_LINK_MODE_FEC_RS_BIT] = "fec-rs",
+ [ETHTOOL_LINK_MODE_FEC_BASER_BIT] = "fec-baser",
+ [ETHTOOL_LINK_MODE_50000baseKR_Full_BIT] = "50000basekr_full",
+ [ETHTOOL_LINK_MODE_50000baseSR_Full_BIT] = "50000basesr_full",
+ [ETHTOOL_LINK_MODE_50000baseCR_Full_BIT] = "50000basecr_full",
+ [ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT] = "50000baselr_er_fr_full",
+ [ETHTOOL_LINK_MODE_50000baseDR_Full_BIT] = "50000basedr_full",
+ [ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT] = "100000basekr2_full",
+ [ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT] = "100000basesr2_full",
+ [ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT] = "100000basecr2_full",
+ [ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT] = "100000baselr2_er2_fr2_full",
+ [ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT] = "100000basedr2_full",
+ [ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT] = "200000basekr4_full",
+ [ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT] = "200000basesr4_full",
+ [ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT] = "200000basecr4_full",
+ [ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT] = "200000baselr4_er4_fr4_full",
+ [ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT] = "200000basedr4_full",
};
/* Make sure the array is large enough to fit all bits */
-assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < ELEMENTSOF(((struct link_config){}).advertise));
+assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table)-1) / 32 < N_ADVERTISE);
DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit, enum ethtool_link_mode_bit_indices);
-int ethtool_connect(int *ret) {
+static int ethtool_connect_or_warn(int *ret, bool warn) {
int fd;
assert_return(ret, -EINVAL);
fd = socket_ioctl_fd();
if (fd < 0)
- return fd;
+ return log_full_errno(warn ? LOG_WARNING: LOG_DEBUG, fd,
+ "ethtool: could not create control socket: %m");
*ret = fd;
int r;
if (*fd < 0) {
- r = ethtool_connect(fd);
+ r = ethtool_connect_or_warn(fd, true);
if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
+ return r;
}
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
return 0;
}
+int ethtool_get_link_info(int *fd, const char *ifname,
+ int *ret_autonegotiation, size_t *ret_speed,
+ Duplex *ret_duplex, NetDevPort *ret_port) {
+ struct ethtool_cmd ecmd = {
+ .cmd = ETHTOOL_GSET,
+ };
+ struct ifreq ifr = {
+ .ifr_data = (void*) &ecmd,
+ };
+ int r;
+
+ if (*fd < 0) {
+ r = ethtool_connect_or_warn(fd, false);
+ if (r < 0)
+ return r;
+ }
+
+ strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
+
+ r = ioctl(*fd, SIOCETHTOOL, &ifr);
+ if (r < 0)
+ return -errno;
+
+ if (ret_autonegotiation)
+ *ret_autonegotiation = ecmd.autoneg;
+
+ if (ret_speed) {
+ uint32_t speed;
+
+ speed = ethtool_cmd_speed(&ecmd);
+ *ret_speed = speed == (uint32_t) SPEED_UNKNOWN ?
+ SIZE_MAX : (size_t) speed * 1000 * 1000;
+ }
+
+ if (ret_duplex)
+ *ret_duplex = ecmd.duplex;
+
+ if (ret_port)
+ *ret_port = ecmd.port;
+
+ return 0;
+}
+
int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex) {
struct ethtool_cmd ecmd = {
.cmd = ETHTOOL_GSET
return 0;
if (*fd < 0) {
- r = ethtool_connect(fd);
+ r = ethtool_connect_or_warn(fd, true);
if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
+ return r;
}
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
return 0;
if (*fd < 0) {
- r = ethtool_connect(fd);
+ r = ethtool_connect_or_warn(fd, true);
if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
+ return r;
}
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
struct ifreq ifr = {};
if (*fd < 0) {
- r = ethtool_connect(fd);
+ r = ethtool_connect_or_warn(fd, true);
if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
+ return r;
}
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
r = get_stringset(*fd, &ifr, ETH_SS_FEATURES, &strings);
if (r < 0)
- return log_warning_errno(r, "link_config: could not get ethtool features for %s", ifname);
+ return log_warning_errno(r, "ethtool: could not get ethtool features for %s", ifname);
sfeatures = alloca0(sizeof(struct ethtool_sfeatures) + DIV_ROUND_UP(strings->len, 32U) * sizeof(sfeatures->features[0]));
sfeatures->cmd = ETHTOOL_SFEATURES;
r = find_feature_index(strings, netdev_feature_table[i]);
if (r < 0) {
- log_warning_errno(r, "link_config: could not find feature: %s", netdev_feature_table[i]);
+ log_warning_errno(r, "ethtool: could not find feature: %s", netdev_feature_table[i]);
continue;
}
r = ioctl(*fd, SIOCETHTOOL, &ifr);
if (r < 0)
- return log_warning_errno(r, "link_config: could not set ethtool features for %s", ifname);
+ return log_warning_errno(r, "ethtool: could not set ethtool features for %s", ifname);
return 0;
}
if (ecmd.req.link_mode_masks_nwords <= 0 || ecmd.req.cmd != ETHTOOL_GLINKSETTINGS)
return -EOPNOTSUPP;
- u = new0(struct ethtool_link_usettings , 1);
+ u = new(struct ethtool_link_usettings, 1);
if (!u)
return -ENOMEM;
- u->base = ecmd.req;
+ *u = (struct ethtool_link_usettings) {
+ .base = ecmd.req,
+ };
offset = 0;
memcpy(u->link_modes.supported, &ecmd.link_mode_data[offset], 4 * ecmd.req.link_mode_masks_nwords);
if (r < 0)
return -errno;
- e = new0(struct ethtool_link_usettings, 1);
+ e = new(struct ethtool_link_usettings, 1);
if (!e)
return -ENOMEM;
- e->base.cmd = ETHTOOL_GSET;
-
- e->base.link_mode_masks_nwords = 1;
- e->base.speed = ethtool_cmd_speed(&ecmd);
- e->base.duplex = ecmd.duplex;
- e->base.port = ecmd.port;
- e->base.phy_address = ecmd.phy_address;
- e->base.autoneg = ecmd.autoneg;
- e->base.mdio_support = ecmd.mdio_support;
-
- e->link_modes.supported[0] = ecmd.supported;
- e->link_modes.advertising[0] = ecmd.advertising;
- e->link_modes.lp_advertising[0] = ecmd.lp_advertising;
+ *e = (struct ethtool_link_usettings) {
+ .base.cmd = ETHTOOL_GSET,
+ .base.link_mode_masks_nwords = 1,
+ .base.speed = ethtool_cmd_speed(&ecmd),
+ .base.duplex = ecmd.duplex,
+ .base.port = ecmd.port,
+ .base.phy_address = ecmd.phy_address,
+ .base.autoneg = ecmd.autoneg,
+ .base.mdio_support = ecmd.mdio_support,
+
+ .link_modes.supported[0] = ecmd.supported,
+ .link_modes.advertising[0] = ecmd.advertising,
+ .link_modes.lp_advertising[0] = ecmd.lp_advertising,
+ };
*u = e;
* link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
* enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
*/
-int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link) {
+int ethtool_set_glinksettings(
+ int *fd,
+ const char *ifname,
+ int autonegotiation,
+ uint32_t advertise[static N_ADVERTISE],
+ size_t speed,
+ Duplex duplex,
+ NetDevPort port) {
_cleanup_free_ struct ethtool_link_usettings *u = NULL;
struct ifreq ifr = {};
int r;
- if (link->autonegotiation != AUTONEG_DISABLE && eqzero(link->advertise)) {
- log_info("link_config: autonegotiation is unset or enabled, the speed and duplex are not writable.");
+ if (autonegotiation != AUTONEG_DISABLE && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
+ log_info("ethtool: autonegotiation is unset or enabled, the speed and duplex are not writable.");
return 0;
}
if (*fd < 0) {
- r = ethtool_connect(fd);
+ r = ethtool_connect_or_warn(fd, true);
if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
+ return r;
}
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
if (r < 0) {
r = get_gset(*fd, &ifr, &u);
if (r < 0)
- return log_warning_errno(r, "link_config: Cannot get device settings for %s : %m", ifname);
+ return log_warning_errno(r, "ethtool: Cannot get device settings for %s : %m", ifname);
}
- if (link->speed)
- u->base.speed = DIV_ROUND_UP(link->speed, 1000000);
+ if (speed > 0)
+ u->base.speed = DIV_ROUND_UP(speed, 1000000);
- if (link->duplex != _DUP_INVALID)
- u->base.duplex = link->duplex;
+ if (duplex != _DUP_INVALID)
+ u->base.duplex = duplex;
- if (link->port != _NET_DEV_PORT_INVALID)
- u->base.port = link->port;
+ if (port != _NET_DEV_PORT_INVALID)
+ u->base.port = port;
- if (link->autonegotiation >= 0)
- u->base.autoneg = link->autonegotiation;
+ if (autonegotiation >= 0)
+ u->base.autoneg = autonegotiation;
- if (!eqzero(link->advertise)) {
+ if (!memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE)) {
u->base.autoneg = AUTONEG_ENABLE;
- memcpy(&u->link_modes.advertising, link->advertise, sizeof(link->advertise));
- memzero((uint8_t*) &u->link_modes.advertising + sizeof(link->advertise),
- ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(link->advertise));
+ memcpy(&u->link_modes.advertising, advertise, sizeof(uint32_t) * N_ADVERTISE);
+ memzero((uint8_t*) &u->link_modes.advertising + sizeof(uint32_t) * N_ADVERTISE,
+ ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE);
}
if (u->base.cmd == ETHTOOL_GLINKSETTINGS)
else
r = set_sset(*fd, &ifr, u);
if (r < 0)
- return log_warning_errno(r, "link_config: Cannot set device settings for %s : %m", ifname);
+ return log_warning_errno(r, "ethtool: Cannot set device settings for %s : %m", ifname);
return r;
}
-int config_parse_channel(const char *unit,
- const char *filename,
- unsigned line,
- const char *section,
- unsigned section_line,
- const char *lvalue,
- int ltype,
- const char *rvalue,
- void *data,
- void *userdata) {
- link_config *config = data;
- uint32_t k;
- int r;
-
- assert(filename);
- assert(section);
- assert(lvalue);
- assert(rvalue);
- assert(data);
-
- r = safe_atou32(rvalue, &k);
- if (r < 0) {
- log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse channel value, ignoring: %s", rvalue);
- return 0;
- }
-
- if (k < 1) {
- log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid %s value, ignoring: %s", lvalue, rvalue);
- return 0;
- }
-
- if (streq(lvalue, "RxChannels")) {
- config->channels.rx_count = k;
- config->channels.rx_count_set = true;
- } else if (streq(lvalue, "TxChannels")) {
- config->channels.tx_count = k;
- config->channels.tx_count_set = true;
- } else if (streq(lvalue, "OtherChannels")) {
- config->channels.other_count = k;
- config->channels.other_count_set = true;
- } else if (streq(lvalue, "CombinedChannels")) {
- config->channels.combined_count = k;
- config->channels.combined_count_set = true;
- }
-
- return 0;
-}
-
int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels) {
struct ethtool_channels ecmd = {
.cmd = ETHTOOL_GCHANNELS
int r;
if (*fd < 0) {
- r = ethtool_connect(fd);
+ r = ethtool_connect_or_warn(fd, true);
if (r < 0)
- return log_warning_errno(r, "link_config: could not connect to ethtool: %m");
+ return r;
}
strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
return 0;
}
+int config_parse_channel(const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ netdev_channels *channels = data;
+ uint32_t k;
+ int r;
+
+ assert(filename);
+ assert(section);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ r = safe_atou32(rvalue, &k);
+ if (r < 0) {
+ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse channel value, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ if (k < 1) {
+ log_syntax(unit, LOG_ERR, filename, line, -EINVAL, "Invalid %s value, ignoring: %s", lvalue, rvalue);
+ return 0;
+ }
+
+ if (streq(lvalue, "RxChannels")) {
+ channels->rx_count = k;
+ channels->rx_count_set = true;
+ } else if (streq(lvalue, "TxChannels")) {
+ channels->tx_count = k;
+ channels->tx_count_set = true;
+ } else if (streq(lvalue, "OtherChannels")) {
+ channels->other_count = k;
+ channels->other_count_set = true;
+ } else if (streq(lvalue, "CombinedChannels")) {
+ channels->combined_count = k;
+ channels->combined_count_set = true;
+ }
+
+ return 0;
+}
+
int config_parse_advertise(const char *unit,
const char *filename,
unsigned line,
const char *rvalue,
void *data,
void *userdata) {
- link_config *config = data;
+ uint32_t *advertise = data;
const char *p;
int r;
if (isempty(rvalue)) {
/* Empty string resets the value. */
- zero(config->advertise);
+ memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
return 0;
}
continue;
}
- config->advertise[mode / 32] |= 1UL << (mode % 32);
+ advertise[mode / 32] |= 1UL << (mode % 32);
}
return 0;
#include "conf-parser.h"
-struct link_config;
+#define N_ADVERTISE 3
/* we can't use DUPLEX_ prefix, as it
* clashes with <linux/ethtool.h> */
bool combined_count_set;
} netdev_channels;
-int ethtool_connect(int *ret);
-
int ethtool_get_driver(int *fd, const char *ifname, char **ret);
+int ethtool_get_link_info(int *fd, const char *ifname,
+ int *ret_autonegotiation, size_t *ret_speed,
+ Duplex *ret_duplex, NetDevPort *ret_port);
int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex);
int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol);
int ethtool_set_features(int *fd, const char *ifname, int *features);
-int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *link);
+int ethtool_set_glinksettings(int *fd, const char *ifname,
+ int autonegotiation, uint32_t advertise[static N_ADVERTISE],
+ size_t speed, Duplex duplex, NetDevPort port);
int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels);
const char *duplex_to_string(Duplex d) _const_;
#include "set.h"
#include "signal-util.h"
#include "stat-util.h"
+#include "string-table.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
return r;
}
+int exec_command_flags_from_strv(char **ex_opts, ExecCommandFlags *flags) {
+ ExecCommandFlags ex_flag, ret_flags = 0;
+ char **opt;
+
+ assert(flags);
+
+ STRV_FOREACH(opt, ex_opts) {
+ ex_flag = exec_command_flags_from_string(*opt);
+ if (ex_flag >= 0)
+ ret_flags |= ex_flag;
+ else
+ return -EINVAL;
+ }
+
+ *flags = ret_flags;
+
+ return 0;
+}
+
+int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ex_opts) {
+ _cleanup_strv_free_ char **ret_opts = NULL;
+ ExecCommandFlags it = flags;
+ const char *str;
+ int i, r;
+
+ assert(ex_opts);
+
+ for (i = 0; it != 0; it &= ~(1 << i), i++) {
+ if (FLAGS_SET(flags, (1 << i))) {
+ str = exec_command_flags_to_string(1 << i);
+ if (!str)
+ return -EINVAL;
+
+ r = strv_extend(&ret_opts, str);
+ if (r < 0)
+ return r;
+ }
+ }
+
+ *ex_opts = TAKE_PTR(ret_opts);
+
+ return 0;
+}
+
const gather_stdout_callback_t gather_environment[] = {
gather_environment_generate,
gather_environment_collect,
gather_environment_consume,
};
+
+static const char* const exec_command_strings[] = {
+ "ignore-failure", /* EXEC_COMMAND_IGNORE_FAILURE */
+ "privileged", /* EXEC_COMMAND_FULLY_PRIVILEGED */
+ "no-setuid", /* EXEC_COMMAND_NO_SETUID */
+ "ambient", /* EXEC_COMMAND_AMBIENT_MAGIC */
+ "no-env-expand", /* EXEC_COMMAND_NO_ENV_EXPAND */
+};
+
+const char* exec_command_flags_to_string(ExecCommandFlags i) {
+ size_t idx;
+
+ for (idx = 0; idx < ELEMENTSOF(exec_command_strings); idx++)
+ if (i == (1 << idx))
+ return exec_command_strings[idx];
+
+ return NULL;
+}
+
+ExecCommandFlags exec_command_flags_from_string(const char *s) {
+ ssize_t idx;
+
+ idx = string_table_lookup(exec_command_strings, ELEMENTSOF(exec_command_strings), s);
+
+ if (idx < 0)
+ return _EXEC_COMMAND_FLAGS_INVALID;
+ else
+ return 1 << idx;
+}
EXEC_DIR_IGNORE_ERRORS = 1 << 1, /* Ignore non-zero exit status of scripts */
} ExecDirFlags;
+typedef enum ExecCommandFlags {
+ EXEC_COMMAND_IGNORE_FAILURE = 1 << 0,
+ EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1,
+ EXEC_COMMAND_NO_SETUID = 1 << 2,
+ EXEC_COMMAND_AMBIENT_MAGIC = 1 << 3,
+ EXEC_COMMAND_NO_ENV_EXPAND = 1 << 4,
+ _EXEC_COMMAND_FLAGS_INVALID = -1,
+} ExecCommandFlags;
+
int execute_directories(
const char* const* directories,
usec_t timeout,
char *envp[],
ExecDirFlags flags);
+int exec_command_flags_from_strv(char **ex_opts, ExecCommandFlags *flags);
+int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ex_opts);
+
extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX];
+
+const char* exec_command_flags_to_string(ExecCommandFlags i);
+ExecCommandFlags exec_command_flags_from_string(const char *s);
case EXIT_CONFIGURATION_DIRECTORY:
return "CONFIGURATION_DIRECTORY";
+ case EXIT_NUMA_POLICY:
+ return "NUMA_POLICY";
+
case EXIT_EXCEPTION:
return "EXCEPTION";
}
EXIT_CACHE_DIRECTORY,
EXIT_LOGS_DIRECTORY, /* 240 */
EXIT_CONFIGURATION_DIRECTORY,
+ EXIT_NUMA_POLICY,
EXIT_EXCEPTION = 255, /* Whenever we want to propagate an abnormal/signal exit, in line with bash */
};
const union in_addr_union *destination,
unsigned destination_prefixlen) {
+ static const xt_chainlabel chain = "POSTROUTING";
_cleanup_(iptc_freep) struct xtc_handle *h = NULL;
struct ipt_entry *entry, *mask;
struct ipt_entry_target *t;
memset(mask, 0xFF, sz);
if (add) {
- if (iptc_check_entry("POSTROUTING", entry, (unsigned char*) mask, h))
+ if (iptc_check_entry(chain, entry, (unsigned char*) mask, h))
return 0;
if (errno != ENOENT) /* if other error than not existing yet, fail */
return -errno;
- if (!iptc_insert_entry("POSTROUTING", entry, 0, h))
+ if (!iptc_insert_entry(chain, entry, 0, h))
return -errno;
} else {
- if (!iptc_delete_entry("POSTROUTING", entry, (unsigned char*) mask, h)) {
+ if (!iptc_delete_entry(chain, entry, (unsigned char*) mask, h)) {
if (errno == ENOENT) /* if it's already gone, all is good! */
return 0;
uint16_t remote_port,
const union in_addr_union *previous_remote) {
+ static const xt_chainlabel chain_pre = "PREROUTING", chain_output = "OUTPUT";
_cleanup_(iptc_freep) struct xtc_handle *h = NULL;
struct ipt_entry *entry, *mask;
struct ipt_entry_target *t;
mr->range[0].flags = NF_NAT_RANGE_PROTO_SPECIFIED|NF_NAT_RANGE_MAP_IPS;
mr->range[0].min_ip = mr->range[0].max_ip = remote->in.s_addr;
if (protocol == IPPROTO_TCP)
- mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htons(remote_port);
+ mr->range[0].min.tcp.port = mr->range[0].max.tcp.port = htobe16(remote_port);
else
- mr->range[0].min.udp.port = mr->range[0].max.udp.port = htons(remote_port);
+ mr->range[0].min.udp.port = mr->range[0].max.udp.port = htobe16(remote_port);
mask = alloca0(sz);
memset(mask, 0xFF, sz);
if (add) {
/* Add the PREROUTING rule, if it is missing so far */
- if (!iptc_check_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
+ if (!iptc_check_entry(chain_pre, entry, (unsigned char*) mask, h)) {
if (errno != ENOENT)
return -EINVAL;
- if (!iptc_insert_entry("PREROUTING", entry, 0, h))
+ if (!iptc_insert_entry(chain_pre, entry, 0, h))
return -errno;
}
if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
- if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
+ if (!iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
if (errno != ENOENT)
return -errno;
}
entry->ip.invflags = IPT_INV_DSTIP;
}
- if (!iptc_check_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
+ if (!iptc_check_entry(chain_output, entry, (unsigned char*) mask, h)) {
if (errno != ENOENT)
return -errno;
- if (!iptc_insert_entry("OUTPUT", entry, 0, h))
+ if (!iptc_insert_entry(chain_output, entry, 0, h))
return -errno;
}
if (previous_remote && previous_remote->in.s_addr != remote->in.s_addr) {
mr->range[0].min_ip = mr->range[0].max_ip = previous_remote->in.s_addr;
- if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
+ if (!iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
if (errno != ENOENT)
return -errno;
}
}
}
} else {
- if (!iptc_delete_entry("PREROUTING", entry, (unsigned char*) mask, h)) {
+ if (!iptc_delete_entry(chain_pre, entry, (unsigned char*) mask, h)) {
if (errno != ENOENT)
return -errno;
}
entry->ip.invflags = IPT_INV_DSTIP;
}
- if (!iptc_delete_entry("OUTPUT", entry, (unsigned char*) mask, h)) {
+ if (!iptc_delete_entry(chain_output, entry, (unsigned char*) mask, h)) {
if (errno != ENOENT)
return -errno;
}
#include "pretty-print.h"
#include "sort-util.h"
#include "string-util.h"
+#include "strxcpyx.h"
#include "terminal-util.h"
#include "time-util.h"
#include "utf8.h"
case TABLE_SIZE:
case TABLE_INT64:
case TABLE_UINT64:
+ case TABLE_BPS:
return sizeof(uint64_t);
case TABLE_INT32:
break;
case TABLE_SIZE:
+ case TABLE_BPS:
buffer.size = va_arg(ap, uint64_t);
data = &buffer.size;
break;
return CMP(a->timespan, b->timespan);
case TABLE_SIZE:
+ case TABLE_BPS:
return CMP(a->size, b->size);
case TABLE_INT:
break;
}
+ case TABLE_BPS: {
+ _cleanup_free_ char *p;
+ size_t n;
+
+ p = new(char, FORMAT_BYTES_MAX+2);
+ if (!p)
+ return NULL;
+
+ if (!format_bytes_full(p, FORMAT_BYTES_MAX, d->size, 0))
+ return "n/a";
+
+ n = strlen(p);
+ strscpy(p + n, FORMAT_BYTES_MAX + 2 - n, "bps");
+
+ d->formatted = TAKE_PTR(p);
+ break;
+ }
+
case TABLE_INT: {
_cleanup_free_ char *p;
return json_variant_new_unsigned(ret, d->timespan);
case TABLE_SIZE:
+ case TABLE_BPS:
if (d->size == (size_t) -1)
return json_variant_new_null(ret);
TABLE_TIMESTAMP,
TABLE_TIMESPAN,
TABLE_SIZE,
+ TABLE_BPS,
TABLE_INT,
TABLE_INT32,
TABLE_INT64,
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+/* exit codes as defined in fsck(8) */
+enum {
+ FSCK_SUCCESS = 0,
+ FSCK_ERROR_CORRECTED = 1 << 0,
+ FSCK_SYSTEM_SHOULD_REBOOT = 1 << 1,
+ FSCK_ERRORS_LEFT_UNCORRECTED = 1 << 2,
+ FSCK_OPERATIONAL_ERROR = 1 << 3,
+ FSCK_USAGE_OR_SYNTAX_ERROR = 1 << 4,
+ FSCK_USER_CANCELLED = 1 << 5,
+ FSCK_SHARED_LIB_ERROR = 1 << 7,
+};
FILE *f;
int r;
- unit = strjoina(dest, "/", name);
+ unit = prefix_roota(dest, name);
r = fopen_unlocked(unit, "wxe", &f);
if (r < 0) {
if (path_equal(where, "/")) {
const char *lnk;
- lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/"SPECIAL_FSCK_ROOT_SERVICE);
+ lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/" SPECIAL_FSCK_ROOT_SERVICE);
(void) mkdir_parents(lnk, 0755);
- if (symlink(SYSTEM_DATA_UNIT_PATH "/"SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0)
+ if (symlink(SYSTEM_DATA_UNIT_PATH "/" SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0)
return log_error_errno(errno, "Failed to create symlink %s: %m", lnk);
} else {
_cleanup_free_ char *_fsck = NULL;
- const char *fsck;
+ const char *fsck, *dep;
if (in_initrd() && path_equal(where, "/sysroot")) {
r = write_fsck_sysroot_service(dir, what);
return r;
fsck = SPECIAL_FSCK_ROOT_SERVICE;
+ dep = "Requires";
} else {
+ /* When this is /usr, then let's add a Wants= dependency, otherwise a Requires=
+ * dependency. Why? We can't possibly unmount /usr during shutdown, but if we have a
+ * Requires= from /usr onto a fsck@.service unit and that unit is shut down, then
+ * we'd have to unmount /usr too. */
+
+ dep = !in_initrd() && path_equal(where, "/usr") ? "Wants" : "Requires";
+
r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck);
if (r < 0)
return log_error_errno(r, "Failed to create fsck service name: %m");
}
fprintf(f,
- "Requires=%1$s\n"
- "After=%1$s\n",
- fsck);
+ "%1$s=%2$s\n"
+ "After=%2$s\n",
+ dep, fsck);
}
return 0;
return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
node);
- unit_file = strjoina(dir, "/", unit);
+ unit_file = prefix_roota(dir, unit);
log_debug("Creating %s", unit_file);
escaped = cescape(node);
return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m",
node);
- unit_file = strjoina(dir, "/", unit);
+ unit_file = prefix_roota(dir, unit);
log_debug("Creating %s", unit_file);
escaped = cescape(node);
return log_error_errno(r, "Failed to make unit name from path \"%s\": %m",
where);
- unit_file = strjoina(dir, "/", unit);
+ unit_file = prefix_roota(dir, unit);
log_debug("Creating %s", unit_file);
f = fopen(unit_file, "wxe");
if (!path_is_absolute(dest)) {
char *x;
- x = prefix_root(root_dir, dest);
+ x = path_join(root_dir, dest);
if (!x)
return -ENOMEM;
if (path_is_absolute(target))
/* This is an absolute path, prefix the root so that we always deal with fully qualified paths */
- info->symlink_target = prefix_root(root_dir, target);
+ info->symlink_target = path_join(root_dir, target);
else
/* This is a relative path, take it relative to the dir the symlink is located in. */
info->symlink_target = file_in_same_dir(path, target);
STRV_FOREACH(p, paths->search_path) {
_cleanup_free_ char *path = NULL;
- path = strjoin(*p, "/", info->name);
+ path = path_join(*p, info->name);
if (!path)
return -ENOMEM;
STRV_FOREACH(p, paths->search_path) {
_cleanup_free_ char *path = NULL;
- path = strjoin(*p, "/", template);
+ path = path_join(*p, template);
if (!path)
return -ENOMEM;
if (r > 0)
return 0;
- path = strjoin(config_path, "/", i->name);
+ path = path_join(config_path, i->name);
if (!path)
return -ENOMEM;
if (!unit_name_is_valid(fn, UNIT_NAME_ANY))
return -EINVAL;
- full = prefix_root(paths.root_dir, *i);
+ full = path_join(paths.root_dir, *i);
if (!full)
return -ENOMEM;
STRV_FOREACH(j, fs) {
_cleanup_free_ char *t = NULL;
- t = strjoin(*i, "/", *j);
+ t = path_join(*i, *j);
if (!t)
return -ENOMEM;
#include <unistd.h>
#include "alloc-util.h"
+#include "errno-util.h"
#include "escape.h"
#include "fd-util.h"
#include "io-util.h"
IMPORTER_STATE_EOF, /* done */
};
-static int iovw_put(struct iovec_wrapper *iovw, void* data, size_t len) {
- if (iovw->count >= ENTRY_FIELD_COUNT_MAX)
- return -E2BIG;
-
- if (!GREEDY_REALLOC(iovw->iovec, iovw->size_bytes, iovw->count + 1))
- return log_oom();
-
- iovw->iovec[iovw->count++] = IOVEC_MAKE(data, len);
- return 0;
-}
-
-static void iovw_free_contents(struct iovec_wrapper *iovw) {
- iovw->iovec = mfree(iovw->iovec);
- iovw->size_bytes = iovw->count = 0;
-}
-
-static void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new) {
- size_t i;
-
- for (i = 0; i < iovw->count; i++)
- iovw->iovec[i].iov_base = (char*) iovw->iovec[i].iov_base - old + new;
-}
-
-size_t iovw_size(struct iovec_wrapper *iovw) {
- size_t n = 0, i;
-
- for (i = 0; i < iovw->count; i++)
- n += iovw->iovec[i].iov_len;
-
- return n;
-}
void journal_importer_cleanup(JournalImporter *imp) {
if (imp->fd >= 0 && !imp->passive_fd) {
free(imp->name);
free(imp->buf);
- iovw_free_contents(&imp->iovw);
+ iovw_free_contents(&imp->iovw, false);
}
static char* realloc_buffer(JournalImporter *imp, size_t size) {
"Failed to store received data of size %zu "
"(in addition to existing %zu bytes with %zu filled): %s",
size, imp->size, imp->filled,
- strerror(ENOMEM));
+ strerror_safe(ENOMEM));
memcpy(imp->buf + imp->filled, data, size);
imp->filled += size;
/* This function drops processed data that along with the iovw that points at it */
- iovw_free_contents(&imp->iovw);
+ iovw_free_contents(&imp->iovw, false);
/* possibly reset buffer position */
remain = imp->filled - imp->offset;
#include <stdbool.h>
#include <sys/uio.h>
+#include "io-util.h"
#include "sd-id128.h"
-
#include "time-util.h"
/* Make sure not to make this smaller than the maximum coredump size.
/* The maximum number of fields in an entry */
#define ENTRY_FIELD_COUNT_MAX 1024
-struct iovec_wrapper {
- struct iovec *iovec;
- size_t size_bytes;
- size_t count;
-};
-
-size_t iovw_size(struct iovec_wrapper *iovw);
-
typedef struct JournalImporter {
int fd;
bool passive_fd;
sd_id128_t boot_id;
} JournalImporter;
+#define JOURNAL_IMPORTER_INIT(_fd) { .fd = (_fd), .iovw = {} }
+#define JOURNAL_IMPORTER_MAKE(_fd) (JournalImporter) JOURNAL_IMPORTER_INIT(_fd)
+
void journal_importer_cleanup(JournalImporter *);
int journal_importer_process_data(JournalImporter *);
int journal_importer_push_data(JournalImporter *, const char *data, size_t size);
assert_return(ret, -EINVAL);
- v = malloc0(offsetof(JsonVariant, value) + space);
+ v = malloc0(MAX(sizeof(JsonVariant),
+ offsetof(JsonVariant, value) + space));
if (!v)
return -ENOMEM;
default:
/* Everything else copy by reference */
- c = malloc0(offsetof(JsonVariant, reference) + sizeof(JsonVariant*));
+ c = malloc0(MAX(sizeof(JsonVariant),
+ offsetof(JsonVariant, reference) + sizeof(JsonVariant*)));
if (!c)
return -ENOMEM;
return 0;
}
- c = malloc0(offsetof(JsonVariant, value) + k);
+ c = malloc0(MAX(sizeof(JsonVariant),
+ offsetof(JsonVariant, value) + k));
if (!c)
return -ENOMEM;
int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) {
_cleanup_strv_free_ char **l = NULL;
char ***s = userdata;
- size_t i;
+ JsonVariant *e;
int r;
assert(variant);
if (!json_variant_is_array(variant))
return json_log(variant, SYNTHETIC_ERRNO(EINVAL), flags, "JSON field '%s' is not an array.", strna(name));
- for (i = 0; i < json_variant_elements(variant); i++) {
- JsonVariant *e;
-
- assert_se(e = json_variant_by_index(variant, i));
-
+ JSON_VARIANT_ARRAY_FOREACH(e, variant) {
if (!json_variant_is_string(e))
return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string.");
({ \
int _level = json_dispatch_level(flags), _e = (error); \
(log_get_max_level() >= LOG_PRI(_level)) \
- ? json_log_internal(variant, _level, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \
+ ? json_log_internal(variant, _level, _e, PROJECT_FILE, __LINE__, __func__, __VA_ARGS__) \
: -ERRNO_VALUE(_e); \
})
-The files in this directory are copied from kernel-5.0, and the following modifications are applied:
+The files in this directory are copied from kernel-5.2, and the following modifications are applied:
- auto_dev-ioctl.h: set AUTOFS_DEV_IOCTL_VERSION_MINOR to 0
- auto_dev-ioctl.h: define AUTOFS_IOCTL if not defined
- bpf_insn.h: This is imported from samples/bpf/bpf_insn.h
- bpf_insn.h: BPF_JMP_A() macro is also imported from include/linux/filter.h
- dm-ioctl.h: set DM_VERSION_MINOR to 27
-- ethtool.h: add a type cast to SPEED_UNKNOWN
- ethtool.h: define __KERNEL_DIV_ROUND_UP if not defined
+- ethtool.h: add casts in ethtool_cmd_speed()
.off = OFF, \
.imm = 0 })
+/* Like BPF_JMP_REG, but with 32-bit wide operands for comparison. */
+
+#define BPF_JMP32_REG(OP, DST, SRC, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP32 | BPF_OP(OP) | BPF_X, \
+ .dst_reg = DST, \
+ .src_reg = SRC, \
+ .off = OFF, \
+ .imm = 0 })
+
/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
.off = OFF, \
.imm = IMM })
+/* Like BPF_JMP_IMM, but with 32-bit wide operands for comparison. */
+
+#define BPF_JMP32_IMM(OP, DST, IMM, OFF) \
+ ((struct bpf_insn) { \
+ .code = BPF_JMP32 | BPF_OP(OP) | BPF_K, \
+ .dst_reg = DST, \
+ .src_reg = 0, \
+ .off = OFF, \
+ .imm = IMM })
+
#define BPF_JMP_A(OFF) \
((struct bpf_insn) { \
.code = BPF_JMP | BPF_JA, \
#define DM_VERSION_MAJOR 4
#define DM_VERSION_MINOR 27
#define DM_VERSION_PATCHLEVEL 0
-#define DM_VERSION_EXTRA "-ioctl (2018-04-03)"
+#define DM_VERSION_EXTRA "-ioctl (2019-01-18)"
/* Status bits */
#define DM_READONLY_FLAG (1 << 0) /* In/Out */
static inline __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep)
{
- return (ep->speed_hi << 16) | ep->speed;
+ return ((__u32) ep->speed_hi << 16) | (__u32) ep->speed;
}
/* Device supports clause 22 register access to PHY or peripherals
#define DOWNSHIFT_DEV_DEFAULT_COUNT 0xff
#define DOWNSHIFT_DEV_DISABLE 0
+/* Time in msecs after which link is reported as down
+ * 0 = lowest time supported by the PHY
+ * 0xff = off, link down detection according to standard
+ */
+#define ETHTOOL_PHY_FAST_LINK_DOWN_ON 0
+#define ETHTOOL_PHY_FAST_LINK_DOWN_OFF 0xff
+
enum phy_tunable_id {
ETHTOOL_PHY_ID_UNSPEC,
ETHTOOL_PHY_DOWNSHIFT,
+ ETHTOOL_PHY_FAST_LINK_DOWN,
/*
* Add your fresh new phy tunable attribute above and remember to update
* phy_tunable_strings[] in net/core/ethtool.c
ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT = 29,
ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT = 30,
ETHTOOL_LINK_MODE_25000baseCR_Full_BIT = 31,
+
+ /* Last allowed bit for __ETHTOOL_LINK_MODE_LEGACY_MASK is bit
+ * 31. Please do NOT define any SUPPORTED_* or ADVERTISED_*
+ * macro for bits > 31. The only way to use indices > 31 is to
+ * use the new ETHTOOL_GLINKSETTINGS/ETHTOOL_SLINKSETTINGS API.
+ */
+
ETHTOOL_LINK_MODE_25000baseKR_Full_BIT = 32,
ETHTOOL_LINK_MODE_25000baseSR_Full_BIT = 33,
ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT = 34,
ETHTOOL_LINK_MODE_FEC_NONE_BIT = 49,
ETHTOOL_LINK_MODE_FEC_RS_BIT = 50,
ETHTOOL_LINK_MODE_FEC_BASER_BIT = 51,
-
- /* Last allowed bit for __ETHTOOL_LINK_MODE_LEGACY_MASK is bit
- * 31. Please do NOT define any SUPPORTED_* or ADVERTISED_*
- * macro for bits > 31. The only way to use indices > 31 is to
- * use the new ETHTOOL_GLINKSETTINGS/ETHTOOL_SLINKSETTINGS API.
- */
-
- __ETHTOOL_LINK_MODE_LAST
- = ETHTOOL_LINK_MODE_FEC_BASER_BIT,
+ ETHTOOL_LINK_MODE_50000baseKR_Full_BIT = 52,
+ ETHTOOL_LINK_MODE_50000baseSR_Full_BIT = 53,
+ ETHTOOL_LINK_MODE_50000baseCR_Full_BIT = 54,
+ ETHTOOL_LINK_MODE_50000baseLR_ER_FR_Full_BIT = 55,
+ ETHTOOL_LINK_MODE_50000baseDR_Full_BIT = 56,
+ ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT = 57,
+ ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT = 58,
+ ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT = 59,
+ ETHTOOL_LINK_MODE_100000baseLR2_ER2_FR2_Full_BIT = 60,
+ ETHTOOL_LINK_MODE_100000baseDR2_Full_BIT = 61,
+ ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT = 62,
+ ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT = 63,
+ ETHTOOL_LINK_MODE_200000baseLR4_ER4_FR4_Full_BIT = 64,
+ ETHTOOL_LINK_MODE_200000baseDR4_Full_BIT = 65,
+ ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT = 66,
+
+ /* must be last entry */
+ __ETHTOOL_LINK_MODE_MASK_NBITS
};
#define __ETHTOOL_LINK_MODE_LEGACY_MASK(base_name) \
#define SPEED_50000 50000
#define SPEED_56000 56000
#define SPEED_100000 100000
+#define SPEED_200000 200000
-#define SPEED_UNKNOWN ((__u32) -1)
+#define SPEED_UNKNOWN -1
static inline int ethtool_validate_speed(__u32 speed)
{
- return speed <= INT_MAX || speed == SPEED_UNKNOWN;
+ return speed <= INT_MAX || speed == (__u32)SPEED_UNKNOWN;
}
/* Duplex, half or full. */
#define ETH_MODULE_SFF_8436 0x4
#define ETH_MODULE_SFF_8436_LEN 256
+#define ETH_MODULE_SFF_8636_MAX_LEN 640
+#define ETH_MODULE_SFF_8436_MAX_LEN 640
+
/* Reset flags */
/* The reset() operation must clear the flags for the components which
* were actually reset. On successful return, the flags indicate the
#define log_link_full(link, level, error, ...) \
({ \
const Link *_l = (link); \
- (_l && _l->ifname) ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _l->ifname, NULL, NULL, ##__VA_ARGS__) : \
- log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
+ (_l && _l->ifname) ? log_object_internal(level, error, PROJECT_FILE, __LINE__, __func__, "INTERFACE=", _l->ifname, NULL, NULL, ##__VA_ARGS__) : \
+ log_internal(level, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \
}) \
#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, 0, ##__VA_ARGS__)
#include "output-mode.h"
#include "parse-util.h"
#include "process-util.h"
+#include "pretty-print.h"
#include "sparse-endian.h"
#include "stdio-util.h"
#include "string-table.h"
const void *data;
size_t length;
size_t n = 0;
- _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL, *transport = NULL, *unit = NULL, *user_unit = NULL;
- size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0, transport_len = 0, unit_len = 0, user_unit_len = 0;
+ _cleanup_free_ char *hostname = NULL, *identifier = NULL, *comm = NULL, *pid = NULL, *fake_pid = NULL, *message = NULL, *realtime = NULL, *monotonic = NULL, *priority = NULL, *transport = NULL, *config_file = NULL, *unit = NULL, *user_unit = NULL;
+ size_t hostname_len = 0, identifier_len = 0, comm_len = 0, pid_len = 0, fake_pid_len = 0, message_len = 0, realtime_len = 0, monotonic_len = 0, priority_len = 0, transport_len = 0, config_file_len = 0, unit_len = 0, user_unit_len = 0;
int p = LOG_INFO;
bool ellipsized = false, audit;
const ParseFieldVec fields[] = {
PARSE_FIELD_VEC_ENTRY("SYSLOG_IDENTIFIER=", &identifier, &identifier_len),
PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
+ PARSE_FIELD_VEC_ENTRY("CONFIG_FILE=", &config_file, &config_file_len),
PARSE_FIELD_VEC_ENTRY("_SYSTEMD_UNIT=", &unit, &unit_len),
PARSE_FIELD_VEC_ENTRY("_SYSTEMD_USER_UNIT=", &user_unit, &user_unit_len),
};
n += hostname_len + 1;
}
- if (mode == OUTPUT_WITH_UNIT && ((unit && shall_print(unit, unit_len, flags)) || (user_unit && shall_print(user_unit, user_unit_len, flags)))) {
+ if (mode == OUTPUT_WITH_UNIT && ((unit && shall_print(unit, unit_len, flags)) ||
+ (user_unit && shall_print(user_unit, user_unit_len, flags)))) {
if (unit) {
fprintf(f, " %.*s", (int) unit_len, unit);
n += unit_len + 1;
fprintf(f, ": [%s blob data]\n", format_bytes(bytes, sizeof(bytes), message_len));
} else {
fputs(": ", f);
+
+ /* URLify config_file string in message, if the message starts with it.
+ * Skip URLification if the highlighted pattern overlaps. */
+ if (config_file &&
+ message_len >= config_file_len &&
+ memcmp(message, config_file, config_file_len) == 0 &&
+ IN_SET(message[config_file_len], ':', ' ', '\0') &&
+ (!highlight || highlight_shifted[0] == 0 || highlight_shifted[0] > config_file_len)) {
+
+ _cleanup_free_ char *t = NULL, *urlified = NULL;
+
+ t = strndup(config_file, config_file_len);
+ if (t && terminal_urlify_path(t, NULL, &urlified) >= 0) {
+ size_t shift = strlen(urlified) - config_file_len;
+ char *joined;
+
+ joined = strjoin(urlified, message + config_file_len);
+ if (joined) {
+ free_and_replace(message, joined);
+ message_len += shift;
+ if (highlight) {
+ highlight_shifted[0] += shift;
+ highlight_shifted[1] += shift;
+ }
+ }
+ }
+ }
+
ellipsized |=
print_multiline(f, n + 2, n_columns, flags, p, audit,
message, message_len,
cursor);
JOURNAL_FOREACH_DATA_RETVAL(j, data, length, r) {
- const char *c;
+ const char *c, *p;
int fieldlen;
const char *on = "", *off = "";
+ _cleanup_free_ char *urlified = NULL;
+ size_t valuelen;
c = memchr(data, '=', length);
if (!c)
r = field_set_test(output_fields, data, fieldlen);
if (r < 0)
return r;
- if (!r)
+ if (r == 0)
continue;
- if (flags & OUTPUT_COLOR && startswith(data, "MESSAGE=")) {
+ valuelen = length - 1 - fieldlen;
+
+ if ((flags & OUTPUT_COLOR) && (p = startswith(data, "MESSAGE="))) {
on = ANSI_HIGHLIGHT;
off = ANSI_NORMAL;
- }
+ } else if ((p = startswith(data, "CONFIG_FILE="))) {
+ if (terminal_urlify_path(p, NULL, &urlified) >= 0) {
+ p = urlified;
+ valuelen = strlen(urlified);
+ }
+ } else
+ p = c + 1;
if ((flags & OUTPUT_SHOW_ALL) ||
(((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
&& utf8_is_printable(data, length))) {
fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, false,
- c + 1, length - fieldlen - 1,
+ p, valuelen,
NULL);
fputs(off, f);
} else {
Set *output_fields,
const size_t highlight[2]) = {
- [OUTPUT_SHORT] = output_short,
- [OUTPUT_SHORT_ISO] = output_short,
+ [OUTPUT_SHORT] = output_short,
+ [OUTPUT_SHORT_ISO] = output_short,
[OUTPUT_SHORT_ISO_PRECISE] = output_short,
- [OUTPUT_SHORT_PRECISE] = output_short,
- [OUTPUT_SHORT_MONOTONIC] = output_short,
- [OUTPUT_SHORT_UNIX] = output_short,
- [OUTPUT_SHORT_FULL] = output_short,
- [OUTPUT_VERBOSE] = output_verbose,
- [OUTPUT_EXPORT] = output_export,
- [OUTPUT_JSON] = output_json,
- [OUTPUT_JSON_PRETTY] = output_json,
- [OUTPUT_JSON_SSE] = output_json,
- [OUTPUT_JSON_SEQ] = output_json,
- [OUTPUT_CAT] = output_cat,
- [OUTPUT_WITH_UNIT] = output_short,
+ [OUTPUT_SHORT_PRECISE] = output_short,
+ [OUTPUT_SHORT_MONOTONIC] = output_short,
+ [OUTPUT_SHORT_UNIX] = output_short,
+ [OUTPUT_SHORT_FULL] = output_short,
+ [OUTPUT_VERBOSE] = output_verbose,
+ [OUTPUT_EXPORT] = output_export,
+ [OUTPUT_JSON] = output_json,
+ [OUTPUT_JSON_PRETTY] = output_json,
+ [OUTPUT_JSON_SSE] = output_json,
+ [OUTPUT_JSON_SEQ] = output_json,
+ [OUTPUT_CAT] = output_cat,
+ [OUTPUT_WITH_UNIT] = output_short,
};
int show_journal_entry(
if (!i->name)
return -ENOMEM;
- if (path)
- i->path = strjoin(path, "/", filename);
- else
- i->path = strdup(filename);
+ i->path = path_join(path, filename);
if (!i->path)
return -ENOMEM;
bus-util.h
bus-wait-for-jobs.c
bus-wait-for-jobs.h
+ bus-wait-for-units.c
+ bus-wait-for-units.h
calendarspec.c
calendarspec.h
cgroup-show.c
dev-setup.h
dissect-image.c
dissect-image.h
+ dm-util.c
+ dm-util.h
dns-domain.c
dns-domain.h
dropin.c
enable-mempool.c
env-file-label.c
env-file-label.h
+ ethtool-util.c
+ ethtool-util.h
exec-util.c
exec-util.h
exit-status.c
firewall-util.h
format-table.c
format-table.h
+ fsck-util.h
fstab-util.c
fstab-util.h
generator.c
_cleanup_free_ char *word = NULL;
const struct libmnt_optmap *ent;
- r = extract_first_word(&p, &word, ",", EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, ",", EXTRACT_UNQUOTE);
if (r < 0)
return r;
if (r == 0)
return -EOPNOTSUPP;
if (tempdir)
- transient = strjoin(tempdir, "/transient");
+ transient = path_join(tempdir, "transient");
else if (scope == UNIT_FILE_SYSTEM)
transient = strdup("/run/systemd/transient");
else
if (!*p)
return 0;
- c = prefix_root(root_dir, *p);
+ c = path_join(root_dir, *p);
if (!c)
return -ENOMEM;
- free(*p);
- *p = c;
-
+ free_and_replace(*p, c);
return 0;
}
static const char* const dns_over_tls_mode_table[_DNS_OVER_TLS_MODE_MAX] = {
[DNS_OVER_TLS_NO] = "no",
[DNS_OVER_TLS_OPPORTUNISTIC] = "opportunistic",
+ [DNS_OVER_TLS_YES] = "yes",
};
-DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_over_tls_mode, DnsOverTlsMode, _DNS_OVER_TLS_MODE_INVALID);
+DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dns_over_tls_mode, DnsOverTlsMode, DNS_OVER_TLS_YES);
* fallback to using an unencrypted connection */
DNS_OVER_TLS_OPPORTUNISTIC,
+ /* Enforce DNS-over-TLS and require valid server certificates */
+ DNS_OVER_TLS_YES,
+
_DNS_OVER_TLS_MODE_MAX,
_DNS_OVER_TLS_MODE_INVALID = -1
};
for (p = s;;) {
_cleanup_free_ char *word = NULL;
- r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&p, &word, NULL, EXTRACT_UNQUOTE);
if (r == -ENOMEM)
return r;
if (r <= 0)
(void) fscanf(f, "%*s %*s %*s %*s %*s\n");
+ // TODO: sort swaps in priority order rather than using first successful option
for (i = 1;; i++) {
_cleanup_free_ char *dev_field = NULL, *type_field = NULL;
size_t size_field, used_field;
* to regulate the code in libudev/ and udev/. */
r = log_set_max_level_from_string_realm(LOG_REALM_UDEV, log);
if (r < 0)
- log_debug_errno(r, "/etc/udev/udev.conf: failed to set udev log level '%s', ignoring: %m", log);
+ log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
+ "failed to set udev log level '%s', ignoring: %m", log);
}
if (ret_children_max && children_max) {
r = safe_atou(children_max, ret_children_max);
if (r < 0)
- log_notice_errno(r, "/etc/udev/udev.conf: failed to set parse children_max=%s, ignoring: %m", children_max);
+ log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
+ "failed to set parse children_max=%s, ignoring: %m", children_max);
}
if (ret_exec_delay_usec && exec_delay) {
r = parse_sec(exec_delay, ret_exec_delay_usec);
if (r < 0)
- log_notice_errno(r, "/etc/udev/udev.conf: failed to set parse exec_delay=%s, ignoring: %m", exec_delay);
+ log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
+ "failed to set parse exec_delay=%s, ignoring: %m", exec_delay);
}
if (ret_event_timeout_usec && event_timeout) {
r = parse_sec(event_timeout, ret_event_timeout_usec);
if (r < 0)
- log_notice_errno(r, "/etc/udev/udev.conf: failed to set parse event_timeout=%s, ignoring: %m", event_timeout);
+ log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
+ "failed to set parse event_timeout=%s, ignoring: %m", event_timeout);
}
if (ret_resolve_name_timing && resolve_names) {
t = resolve_name_timing_from_string(resolve_names);
if (t < 0)
- log_notice("/etc/udev/udev.conf: failed to set parse resolve_names=%s, ignoring.", resolve_names);
+ log_syntax(NULL, LOG_WARNING, "/etc/udev/udev.conf", 0, r,
+ "failed to set parse resolve_names=%s, ignoring.", resolve_names);
else
*ret_resolve_name_timing = t;
}
usec_t t;
assert_return(v, -EINVAL);
- assert_return(!v->server, -ENOTTY);
if (v->state == VARLINK_DISCONNECTED)
return -ENOTCONN;
varlink_set_state(v, VARLINK_IDLE_SERVER);
- r = varlink_attach_event(v, server->event, server->event_priority);
- if (r < 0) {
- varlink_log_errno(v, r, "Failed to attach new connection: %m");
- v->fd = -1; /* take the fd out of the connection again */
- varlink_close(v);
- return r;
+ if (server->event) {
+ r = varlink_attach_event(v, server->event, server->event_priority);
+ if (r < 0) {
+ varlink_log_errno(v, r, "Failed to attach new connection: %m");
+ v->fd = -1; /* take the fd out of the connection again */
+ varlink_close(v);
+ return r;
+ }
}
if (ret)
const Verb *verb;
const char *name;
unsigned i;
- int left, r;
+ int left;
assert(verbs);
assert(verbs[0].dispatch);
return 0;
}
- if (verb->flags & VERB_MUST_BE_ROOT) {
- r = must_be_root();
- if (r < 0)
- return r;
- }
-
if (name)
return verb->dispatch(left, argv, userdata);
else {
typedef enum VerbFlags {
VERB_DEFAULT = 1 << 0,
VERB_ONLINE_ONLY = 1 << 1,
- VERB_MUST_BE_ROOT = 1 << 2,
} VerbFlags;
typedef struct {
return 0;
}
+int parse_vid_range(const char *p, uint16_t *vid, uint16_t *vid_end) {
+ unsigned lower, upper;
+ int r;
+
+ r = parse_range(p, &lower, &upper);
+ if (r < 0)
+ return r;
+
+ if (lower > VLANID_MAX || upper > VLANID_MAX || lower > upper)
+ return -EINVAL;
+
+ *vid = lower;
+ *vid_end = upper;
+ return 0;
+}
+
int config_parse_default_port_vlanid(
const char *unit,
const char *filename,
}
int parse_vlanid(const char *p, uint16_t *ret);
+int parse_vid_range(const char *p, uint16_t *vid, uint16_t *vid_end);
CONFIG_PARSER_PROTOTYPE(config_parse_default_port_vlanid);
CONFIG_PARSER_PROTOTYPE(config_parse_vlanid);
#include "sd-messages.h"
+#include "btrfs-util.h"
#include "def.h"
#include "exec-util.h"
#include "fd-util.h"
static char* arg_verb = NULL;
+STATIC_DESTRUCTOR_REGISTER(arg_verb, freep);
+
static int write_hibernate_location_info(void) {
_cleanup_free_ char *device = NULL, *type = NULL;
_cleanup_free_ struct fiemap *fiemap = NULL;
if (r < 0)
return log_debug_errno(errno, "Unable to stat %s: %m", device);
+ r = btrfs_is_filesystem(fd);
+ if (r < 0)
+ return log_error_errno(r, "Error checking %s for Btrfs filesystem: %m", device);
+
+ if (r)
+ return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
+ "Unable to calculate swapfile offset when using Btrfs: %s", device);
+
r = read_fiemap(fd, &fiemap);
if (r < 0)
return log_debug_errno(r, "Unable to read extent map for '%s': %m", device);
if (r < 0)
return log_debug_errno(r, "Failed to write offset '%s': %m", offset_str);
+ log_debug("Wrote calculated resume_offset value to /sys/power/resume_offset: %s", offset_str);
+
xsprintf(device_str, "%lx", (unsigned long)stb.st_dev);
r = write_string_file("/sys/power/resume", device_str, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0)
return log_debug_errno(r, "Failed to write device '%s': %m", device_str);
+ log_debug("Wrote device id to /sys/power/resume: %s", device_str);
+
return 0;
}
return r;
}
+static int configure_hibernation(void) {
+ _cleanup_free_ char *resume = NULL, *resume_offset = NULL;
+ int r;
+
+ /* check for proper hibernation configuration */
+ r = read_one_line_file("/sys/power/resume", &resume);
+ if (r < 0)
+ return log_debug_errno(r, "Error reading from /sys/power/resume: %m");
+
+ r = read_one_line_file("/sys/power/resume_offset", &resume_offset);
+ if (r < 0)
+ return log_debug_errno(r, "Error reading from /sys/power/resume_offset: %m");
+
+ if (!streq(resume_offset, "0") && !streq(resume, "0:0")) {
+ log_debug("Hibernating using device id and offset read from /sys/power/resume: %s and /sys/power/resume_offset: %s", resume, resume_offset);
+ return 0;
+ } else if (!streq(resume, "0:0")) {
+ log_debug("Hibernating using device id read from /sys/power/resume: %s", resume);
+ return 0;
+ } else if (!streq(resume_offset, "0"))
+ log_debug("Found offset in /sys/power/resume_offset: %s; no device id found in /sys/power/resume; ignoring offset", resume_offset);
+
+ /* if hibernation is not properly configured, attempt to calculate and write values */
+ return write_hibernate_location_info();
+}
+
static int execute(char **modes, char **states) {
char *arguments[] = {
NULL,
/* Configure the hibernation mode */
if (!strv_isempty(modes)) {
- r = write_hibernate_location_info();
+ r = configure_hibernation();
if (r < 0)
- return log_error_errno(r, "Failed to write hibernation disk offset: %m");
+ return log_error_errno(r, "Failed to prepare for hibernation: %m");
+
r = write_mode(modes);
if (r < 0)
return log_error_errno(r, "Failed to write mode to /sys/power/disk: %m");;
"Usage: %s COMMAND",
program_invocation_short_name);
- arg_verb = argv[optind];
+ arg_verb = strdup(argv[optind]);
+ if (!arg_verb)
+ return log_oom();
if (!STR_IN_SET(arg_verb, "suspend", "hibernate", "hybrid-sleep", "suspend-then-hibernate"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
assert_se(arg_dest = dest_early);
r = generate_symlink();
- if (r < 0)
+ if (r <= 0)
return r;
+ /* We parse the command line only to emit warnings. */
r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, 0);
if (r < 0)
log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
#include "bus-unit-util.h"
#include "bus-util.h"
#include "bus-wait-for-jobs.h"
+#include "bus-wait-for-units.h"
#include "cgroup-show.h"
#include "cgroup-util.h"
#include "copy.h"
#include "efivars.h"
#include "env-util.h"
#include "escape.h"
+#include "exec-util.h"
#include "exit-status.h"
#include "fd-util.h"
#include "format-util.h"
static bool arg_now = false;
static bool arg_jobs_before = false;
static bool arg_jobs_after = false;
+static char **arg_clean_what = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_wall, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_types, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_states, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep);
+STATIC_DESTRUCTOR_REGISTER(arg_clean_what, strv_freep);
static int daemon_reload(int argc, char *argv[], void* userdata);
static int trivial_method(int argc, char *argv[], void *userdata);
assert(nw);
assert(next);
- if (next->monotonic != USEC_INFINITY && next->monotonic > 0) {
+ if (timestamp_is_set(next->monotonic)) {
usec_t converted;
if (next->monotonic > nw->monotonic)
else
converted = nw->realtime - (nw->monotonic - next->monotonic);
- if (next->realtime != USEC_INFINITY && next->realtime > 0)
+ if (timestamp_is_set(next->realtime))
next_elapse = MIN(converted, next->realtime);
else
next_elapse = converted;
statelen = STRLEN("STATE"),
failedlen = STRLEN("FAILED"),
jobslen = STRLEN("JOBS");
+ bool state_missing = false;
assert(machine_infos || n == 0);
failedlen = MAX(failedlen, DECIMAL_STR_WIDTH(m->n_failed_units));
jobslen = MAX(jobslen, DECIMAL_STR_WIDTH(m->n_jobs));
- if (!arg_plain && !streq_ptr(m->state, "running"))
+ if (!arg_plain && m->state && !streq(m->state, "running"))
circle_len = 2;
}
if (circle_len > 0)
printf("%s%s%s ", on_state, circle ? special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE) : " ", off_state);
+ if (!m->state)
+ state_missing = true;
+
if (m->is_host)
printf("%-*s (host) %s%-*s%s %s%*" PRIu32 "%s %*" PRIu32 "\n",
- (int) (namelen - (STRLEN(" (host)"))),
+ (int) (namelen - strlen(" (host)")),
strna(m->name),
on_state, statelen, strna(m->state), off_state,
on_failed, failedlen, m->n_failed_units, off_failed,
jobslen, m->n_jobs);
}
- if (!arg_no_legend)
- printf("\n%u machines listed.\n", n);
+ if (!arg_no_legend) {
+ printf("\n");
+ if (state_missing && geteuid() != 0)
+ printf("Notice: some information only available to privileged users was not shown.\n");
+ printf("%u machines listed.\n", n);
+ }
}
static int list_machines(int argc, char *argv[], void *userdata) {
int r;
/**
- * Finds where the unit is defined on disk. Returns 0 if the unit is not found. Returns 1 if it is found, and
- * sets:
+ * Finds where the unit is defined on disk. Returns 0 if the unit is not found. Returns 1 if it is
+ * found, and sets:
* - the path to the unit in *ret_frament_path, if it exists on disk,
- * - and a strv of existing drop-ins in *ret_dropin_paths, if the arg is not NULL and any dropins were found.
+ * - and a strv of existing drop-ins in *ret_dropin_paths, if the arg is not NULL and any dropins
+ * were found.
*
- * Returns -ERFKILL if the unit is masked, and -EKEYREJECTED if the unit file could not be loaded for some
- * reason (the latter only applies if we are going through the service manager)
+ * Returns -ERFKILL if the unit is masked, and -EKEYREJECTED if the unit file could not be loaded for
+ * some reason (the latter only applies if we are going through the service manager).
*/
assert(unit_name);
r = 0;
goto not_found;
}
- if (!streq(load_state, "loaded"))
+ if (!STR_IN_SET(load_state, "loaded", "bad-setting"))
return -EKEYREJECTED;
r = sd_bus_get_property_string(
return "start";
}
-typedef struct {
- sd_bus_slot *match;
- sd_event *event;
- Set *unit_paths;
- bool any_failed;
-} WaitContext;
-
-static void wait_context_free(WaitContext *c) {
- c->match = sd_bus_slot_unref(c->match);
- c->event = sd_event_unref(c->event);
- c->unit_paths = set_free_free(c->unit_paths);
-}
-
-static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error *error) {
- const char *path, *interface, *active_state = NULL, *job_path = NULL;
- WaitContext *c = userdata;
- bool is_failed;
- int r;
-
- /* Called whenever we get a PropertiesChanged signal. Checks if ActiveState changed to inactive/failed.
- *
- * Signal parameters: (s interface, a{sv} changed_properties, as invalidated_properties) */
-
- path = sd_bus_message_get_path(m);
- if (!set_contains(c->unit_paths, path))
- return 0;
-
- r = sd_bus_message_read(m, "s", &interface);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (!streq(interface, "org.freedesktop.systemd1.Unit")) /* ActiveState is on the Unit interface */
- return 0;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
- if (r < 0)
- return bus_log_parse_error(r);
-
- for (;;) {
- const char *s;
-
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
- if (r < 0)
- return bus_log_parse_error(r);
- if (r == 0) /* end of array */
- break;
-
- r = sd_bus_message_read(m, "s", &s); /* Property name */
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (streq(s, "ActiveState")) {
- r = sd_bus_message_read(m, "v", "s", &active_state);
- if (r < 0)
- return bus_log_parse_error(r);
-
- if (job_path) /* Found everything we need */
- break;
-
- } else if (streq(s, "Job")) {
- uint32_t job_id;
-
- r = sd_bus_message_read(m, "v", "(uo)", &job_id, &job_path);
- if (r < 0)
- return bus_log_parse_error(r);
-
- /* There's still a job pending for this unit, let's ignore this for now, and return right-away. */
- if (job_id != 0)
- return 0;
-
- if (active_state) /* Found everything we need */
- break;
-
- } else {
- r = sd_bus_message_skip(m, "v"); /* Other property */
- if (r < 0)
- return bus_log_parse_error(r);
- }
-
- r = sd_bus_message_exit_container(m);
- if (r < 0)
- return bus_log_parse_error(r);
- }
-
- /* If this didn't contain the ActiveState property we can't do anything */
- if (!active_state)
- return 0;
-
- is_failed = streq(active_state, "failed");
- if (streq(active_state, "inactive") || is_failed) {
- log_debug("%s became %s, dropping from --wait tracking", path, active_state);
- free(set_remove(c->unit_paths, path));
- c->any_failed = c->any_failed || is_failed;
- } else
- log_debug("ActiveState on %s changed to %s", path, active_state);
-
- if (set_isempty(c->unit_paths))
- sd_event_exit(c->event, EXIT_SUCCESS);
-
- return 0;
-}
-
-static int wait_context_watch(
- WaitContext *wait_context,
- sd_bus *bus,
- const char *name) {
-
- _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- _cleanup_free_ char *unit_path = NULL;
- int r;
-
- assert(wait_context);
- assert(name);
-
- log_debug("Watching for property changes of %s", name);
- r = sd_bus_call_method(
- bus,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
- "RefUnit",
- &error,
- NULL,
- "s", name);
- if (r < 0)
- return log_error_errno(r, "Failed to add reference to unit %s: %s", name, bus_error_message(&error, r));
-
- unit_path = unit_dbus_path_from_name(name);
- if (!unit_path)
- return log_oom();
-
- r = set_ensure_allocated(&wait_context->unit_paths, &string_hash_ops);
- if (r < 0)
- return log_oom();
-
- r = set_put_strdup(wait_context->unit_paths, unit_path);
- if (r < 0)
- return log_error_errno(r, "Failed to add unit path %s to set: %m", unit_path);
-
- r = sd_bus_match_signal_async(bus,
- &wait_context->match,
- NULL,
- unit_path,
- "org.freedesktop.DBus.Properties",
- "PropertiesChanged",
- on_properties_changed, NULL, wait_context);
- if (r < 0)
- return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m");
-
- return 0;
-}
-
static int start_unit_one(
sd_bus *bus,
const char *method, /* When using classic per-job bus methods */
const char *mode,
sd_bus_error *error,
BusWaitForJobs *w,
- WaitContext *wait_context) {
+ BusWaitForUnits *wu) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
const char *path;
assert(mode);
assert(error);
- if (wait_context) {
- r = wait_context_watch(wait_context, bus, name);
- if (r < 0)
- return r;
- }
-
log_debug("%s dbus call org.freedesktop.systemd1.Manager %s(%s, %s)",
arg_dry_run ? "Would execute" : "Executing",
method, name, mode);
return log_error_errno(r, "Failed to watch job for %s: %m", name);
}
+ if (wu) {
+ r = bus_wait_for_units_add_unit(wu, name, BUS_WAIT_FOR_INACTIVE|BUS_WAIT_NO_JOB, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch unit %s: %m", name);
+ }
+
return 0;
fail:
}
static int start_unit(int argc, char *argv[], void *userdata) {
+ _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *wu = NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
- _cleanup_(wait_context_free) WaitContext wait_context = {};
const char *method, *job_type, *mode, *one_name, *suffix = NULL;
_cleanup_free_ char **stopped_units = NULL; /* Do not use _cleanup_strv_free_ */
_cleanup_strv_free_ char **names = NULL;
if (r < 0)
return log_error_errno(r, "Failed to enable subscription: %m");
- r = sd_event_default(&wait_context.event);
- if (r < 0)
- return log_error_errno(r, "Failed to allocate event loop: %m");
-
- r = sd_bus_attach_event(bus, wait_context.event, 0);
+ r = bus_wait_for_units_new(bus, &wu);
if (r < 0)
- return log_error_errno(r, "Failed to attach bus to event loop: %m");
+ return log_error_errno(r, "Failed to allocate unit watch context: %m");
}
STRV_FOREACH(name, names) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
- r = start_unit_one(bus, method, job_type, *name, mode, &error, w, arg_wait ? &wait_context : NULL);
+ r = start_unit_one(bus, method, job_type, *name, mode, &error, w, wu);
if (ret == EXIT_SUCCESS && r < 0)
ret = translate_bus_error_to_exit_status(r, &error);
(void) check_triggering_units(bus, *name);
}
- if (ret == EXIT_SUCCESS && arg_wait && !set_isempty(wait_context.unit_paths)) {
- r = sd_event_loop(wait_context.event);
+ if (arg_wait) {
+ r = bus_wait_for_units_run(wu);
if (r < 0)
- return log_error_errno(r, "Failed to run event loop: %m");
- if (wait_context.any_failed)
+ return log_error_errno(r, "Failed to wait for units: %m");
+ if (r == BUS_WAIT_FAILURE && ret == EXIT_SUCCESS)
ret = EXIT_FAILURE;
}
return r;
}
+static int clean_unit(int argc, char *argv[], void *userdata) {
+ _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL;
+ _cleanup_strv_free_ char **names = NULL;
+ int r, ret = EXIT_SUCCESS;
+ char **name;
+ sd_bus *bus;
+
+ r = acquire_bus(BUS_FULL, &bus);
+ if (r < 0)
+ return r;
+
+ polkit_agent_open_maybe();
+
+ if (!arg_clean_what) {
+ arg_clean_what = strv_new("cache", "runtime");
+ if (!arg_clean_what)
+ return log_oom();
+ }
+
+ r = expand_names(bus, strv_skip(argv, 1), NULL, &names);
+ if (r < 0)
+ return log_error_errno(r, "Failed to expand names: %m");
+
+ if (!arg_no_block) {
+ r = bus_wait_for_units_new(bus, &w);
+ if (r < 0)
+ return log_error_errno(r, "Failed to allocate unit waiter: %m");
+ }
+
+ STRV_FOREACH(name, names) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
+
+ if (w) {
+ /* If we shall wait for the cleaning to complete, let's add a ref on the unit first */
+ r = sd_bus_call_method(
+ bus,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "RefUnit",
+ &error,
+ NULL,
+ "s", *name);
+ if (r < 0) {
+ log_error_errno(r, "Failed to add reference to unit %s: %s", *name, bus_error_message(&error, r));
+ if (ret == EXIT_SUCCESS)
+ ret = r;
+ continue;
+ }
+ }
+
+ r = sd_bus_message_new_method_call(
+ bus,
+ &m,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ "org.freedesktop.systemd1.Manager",
+ "CleanUnit");
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append(m, "s", *name);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_message_append_strv(m, arg_clean_what);
+ if (r < 0)
+ return bus_log_create_error(r);
+
+ r = sd_bus_call(bus, m, 0, &error, NULL);
+ if (r < 0) {
+ log_error_errno(r, "Failed to clean unit %s: %s", *name, bus_error_message(&error, r));
+ if (ret == EXIT_SUCCESS) {
+ ret = r;
+ continue;
+ }
+ }
+
+ if (w) {
+ r = bus_wait_for_units_add_unit(w, *name, BUS_WAIT_REFFED|BUS_WAIT_FOR_MAINTENANCE_END, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to watch unit %s: %m", *name);
+ }
+ }
+
+ r = bus_wait_for_units_run(w);
+ if (r < 0)
+ return log_error_errno(r, "Failed to wait for units: %m");
+ if (r == BUS_WAIT_FAILURE)
+ ret = EXIT_FAILURE;
+
+ return ret;
+}
+
typedef struct ExecStatusInfo {
char *name;
int code;
int status;
+ ExecCommandFlags flags;
+
LIST_FIELDS(struct ExecStatusInfo, exec);
} ExecStatusInfo;
free(i);
}
-static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) {
+static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i, bool is_ex_prop) {
+ _cleanup_strv_free_ char **ex_opts = NULL;
uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic;
const char *path;
uint32_t pid;
assert(m);
assert(i);
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, "sasbttttuii");
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, is_ex_prop ? "sasasttttuii" : "sasbttttuii");
if (r < 0)
return bus_log_parse_error(r);
else if (r == 0)
if (r < 0)
return bus_log_parse_error(r);
+ r = is_ex_prop ? sd_bus_message_read_strv(m, &ex_opts) : sd_bus_message_read(m, "b", &ignore);
+ if (r < 0)
+ return bus_log_parse_error(r);
+
r = sd_bus_message_read(m,
- "bttttuii",
- &ignore,
+ "ttttuii",
&start_timestamp, &start_timestamp_monotonic,
&exit_timestamp, &exit_timestamp_monotonic,
&pid,
if (r < 0)
return bus_log_parse_error(r);
- i->ignore = ignore;
+ if (is_ex_prop) {
+ r = exec_command_flags_from_strv(ex_opts, &i->flags);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert strv to ExecCommandFlags: %m");
+
+ i->ignore = FLAGS_SET(i->flags, EXEC_COMMAND_IGNORE_FAILURE);
+ } else
+ i->ignore = ignore;
+
i->start_timestamp = (usec_t) start_timestamp;
i->exit_timestamp = (usec_t) exit_timestamp;
i->pid = (pid_t) pid;
if (i->status_text)
printf(" Status: \"%s\"\n", i->status_text);
if (i->status_errno > 0)
- printf(" Error: %i (%s)\n", i->status_errno, strerror(i->status_errno));
+ printf(" Error: %i (%s)\n", i->status_errno, strerror_safe(i->status_errno));
if (i->ip_ingress_bytes != (uint64_t) -1 && i->ip_egress_bytes != (uint64_t) -1) {
char buf_in[FORMAT_BYTES_MAX], buf_out[FORMAT_BYTES_MAX];
_cleanup_free_ ExecStatusInfo *info = NULL;
ExecStatusInfo *last;
UnitStatusInfo *i = userdata;
+ bool is_ex_prop = endswith(member, "Ex");
int r;
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)");
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, is_ex_prop ? "(sasasttttuii)" : "(sasbttttuii)");
if (r < 0)
return r;
LIST_FIND_TAIL(exec, i->exec, last);
- while ((r = exec_status_info_deserialize(m, info)) > 0) {
+ while ((r = exec_status_info_deserialize(m, info, is_ex_prop)) > 0) {
info->name = strdup(member);
if (!info->name)
else if (all)
bus_print_property_value(name, expected_value, value, "[not set]");
+ return 1;
+ } else if (streq(name, "NUMAPolicy")) {
+ int32_t i;
+
+ r = sd_bus_message_read_basic(m, bus_type, &i);
+ if (r < 0)
+ return r;
+
+ bus_print_property_valuef(name, expected_value, value, "%s", strna(mpol_to_string(i)));
+
return 1;
}
break;
} else if (contents[0] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) {
ExecStatusInfo info = {};
+ bool is_ex_prop = endswith(name, "Ex");
- r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)");
+ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, is_ex_prop ? "(sasasttttuii)" : "(sasbttttuii)");
if (r < 0)
return bus_log_parse_error(r);
- while ((r = exec_status_info_deserialize(m, &info)) > 0) {
+ while ((r = exec_status_info_deserialize(m, &info, is_ex_prop)) > 0) {
char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
- _cleanup_free_ char *tt;
+ _cleanup_strv_free_ char **optv = NULL;
+ _cleanup_free_ char *tt, *o = NULL;
tt = strv_join(info.argv, " ");
- bus_print_property_valuef(name, expected_value, value,
- "{ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }",
- strna(info.path),
- strna(tt),
- yes_no(info.ignore),
- strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
- strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
- info.pid,
- sigchld_code_to_string(info.code),
- info.status,
- info.code == CLD_EXITED ? "" : "/",
- strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
+ if (is_ex_prop) {
+ r = exec_command_flags_to_strv(info.flags, &optv);
+ if (r < 0)
+ return log_error_errno(r, "Failed to convert ExecCommandFlags to strv: %m");
+
+ o = strv_join(optv, " ");
+
+ bus_print_property_valuef(name, expected_value, value,
+ "{ path=%s ; argv[]=%s ; flags=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }",
+ strna(info.path),
+ strna(tt),
+ strna(o),
+ strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
+ strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
+ info.pid,
+ sigchld_code_to_string(info.code),
+ info.status,
+ info.code == CLD_EXITED ? "" : "/",
+ strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
+ } else
+ bus_print_property_valuef(name, expected_value, value,
+ "{ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }",
+ strna(info.path),
+ strna(tt),
+ yes_no(info.ignore),
+ strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
+ strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
+ info.pid,
+ sigchld_code_to_string(info.code),
+ info.status,
+ info.code == CLD_EXITED ? "" : "/",
+ strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
free(info.path);
strv_free(info.argv);
bus_print_property_value(name, expected_value, value, strempty(fields));
return 1;
- } else if (contents[0] == SD_BUS_TYPE_BYTE && streq(name, "CPUAffinity")) {
+ } else if (contents[0] == SD_BUS_TYPE_BYTE && STR_IN_SET(name, "CPUAffinity", "NUMAMask")) {
_cleanup_free_ char *affinity = NULL;
_cleanup_(cpu_set_reset) CPUSet set = {};
const void *a;
r = cpu_set_from_dbus(a, n, &set);
if (r < 0)
- return log_error_errno(r, "Failed to deserialize CPUAffinity: %m");
+ return log_error_errno(r, "Failed to deserialize %s: %m", name);
affinity = cpu_set_to_range_string(&set);
if (!affinity)
bool *ellipsized) {
static const struct bus_properties_map property_map[] = {
- { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
- { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
- { "Documentation", "as", NULL, offsetof(UnitStatusInfo, documentation) },
+ { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
+ { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
+ { "Documentation", "as", NULL, offsetof(UnitStatusInfo, documentation) },
{}
}, status_map[] = {
- { "Id", "s", NULL, offsetof(UnitStatusInfo, id) },
- { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
- { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
- { "SubState", "s", NULL, offsetof(UnitStatusInfo, sub_state) },
- { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) },
- { "UnitFilePreset", "s", NULL, offsetof(UnitStatusInfo, unit_file_preset) },
- { "Description", "s", NULL, offsetof(UnitStatusInfo, description) },
- { "Following", "s", NULL, offsetof(UnitStatusInfo, following) },
- { "Documentation", "as", NULL, offsetof(UnitStatusInfo, documentation) },
- { "FragmentPath", "s", NULL, offsetof(UnitStatusInfo, fragment_path) },
- { "SourcePath", "s", NULL, offsetof(UnitStatusInfo, source_path) },
- { "ControlGroup", "s", NULL, offsetof(UnitStatusInfo, control_group) },
- { "DropInPaths", "as", NULL, offsetof(UnitStatusInfo, dropin_paths) },
- { "LoadError", "(ss)", map_load_error, offsetof(UnitStatusInfo, load_error) },
- { "Result", "s", NULL, offsetof(UnitStatusInfo, result) },
- { "InactiveExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, inactive_exit_timestamp) },
- { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(UnitStatusInfo, inactive_exit_timestamp_monotonic) },
- { "ActiveEnterTimestamp", "t", NULL, offsetof(UnitStatusInfo, active_enter_timestamp) },
- { "ActiveExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, active_exit_timestamp) },
- { "InactiveEnterTimestamp", "t", NULL, offsetof(UnitStatusInfo, inactive_enter_timestamp) },
- { "NeedDaemonReload", "b", NULL, offsetof(UnitStatusInfo, need_daemon_reload) },
- { "Transient", "b", NULL, offsetof(UnitStatusInfo, transient) },
- { "ExecMainPID", "u", NULL, offsetof(UnitStatusInfo, main_pid) },
- { "MainPID", "u", map_main_pid, 0 },
- { "ControlPID", "u", NULL, offsetof(UnitStatusInfo, control_pid) },
- { "StatusText", "s", NULL, offsetof(UnitStatusInfo, status_text) },
- { "PIDFile", "s", NULL, offsetof(UnitStatusInfo, pid_file) },
- { "StatusErrno", "i", NULL, offsetof(UnitStatusInfo, status_errno) },
- { "ExecMainStartTimestamp", "t", NULL, offsetof(UnitStatusInfo, start_timestamp) },
- { "ExecMainExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, exit_timestamp) },
- { "ExecMainCode", "i", NULL, offsetof(UnitStatusInfo, exit_code) },
- { "ExecMainStatus", "i", NULL, offsetof(UnitStatusInfo, exit_status) },
- { "ConditionTimestamp", "t", NULL, offsetof(UnitStatusInfo, condition_timestamp) },
- { "ConditionResult", "b", NULL, offsetof(UnitStatusInfo, condition_result) },
- { "Conditions", "a(sbbsi)", map_conditions, 0 },
- { "AssertTimestamp", "t", NULL, offsetof(UnitStatusInfo, assert_timestamp) },
- { "AssertResult", "b", NULL, offsetof(UnitStatusInfo, assert_result) },
- { "Asserts", "a(sbbsi)", map_asserts, 0 },
- { "NextElapseUSecRealtime", "t", NULL, offsetof(UnitStatusInfo, next_elapse_real) },
- { "NextElapseUSecMonotonic", "t", NULL, offsetof(UnitStatusInfo, next_elapse_monotonic) },
- { "NAccepted", "u", NULL, offsetof(UnitStatusInfo, n_accepted) },
- { "NConnections", "u", NULL, offsetof(UnitStatusInfo, n_connections) },
- { "NRefused", "u", NULL, offsetof(UnitStatusInfo, n_refused) },
- { "Accept", "b", NULL, offsetof(UnitStatusInfo, accept) },
- { "Listen", "a(ss)", map_listen, offsetof(UnitStatusInfo, listen) },
- { "SysFSPath", "s", NULL, offsetof(UnitStatusInfo, sysfs_path) },
- { "Where", "s", NULL, offsetof(UnitStatusInfo, where) },
- { "What", "s", NULL, offsetof(UnitStatusInfo, what) },
- { "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) },
- { "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) },
- { "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) },
- { "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) },
- { "MemoryLow", "t", NULL, offsetof(UnitStatusInfo, memory_low) },
- { "MemoryHigh", "t", NULL, offsetof(UnitStatusInfo, memory_high) },
- { "MemoryMax", "t", NULL, offsetof(UnitStatusInfo, memory_max) },
- { "MemorySwapMax", "t", NULL, offsetof(UnitStatusInfo, memory_swap_max) },
- { "MemoryLimit", "t", NULL, offsetof(UnitStatusInfo, memory_limit) },
- { "CPUUsageNSec", "t", NULL, offsetof(UnitStatusInfo, cpu_usage_nsec) },
- { "TasksCurrent", "t", NULL, offsetof(UnitStatusInfo, tasks_current) },
- { "TasksMax", "t", NULL, offsetof(UnitStatusInfo, tasks_max) },
- { "IPIngressBytes", "t", NULL, offsetof(UnitStatusInfo, ip_ingress_bytes) },
- { "IPEgressBytes", "t", NULL, offsetof(UnitStatusInfo, ip_egress_bytes) },
- { "IOReadBytes", "t", NULL, offsetof(UnitStatusInfo, io_read_bytes) },
- { "IOWriteBytes", "t", NULL, offsetof(UnitStatusInfo, io_write_bytes) },
- { "ExecStartPre", "a(sasbttttuii)", map_exec, 0 },
- { "ExecStart", "a(sasbttttuii)", map_exec, 0 },
- { "ExecStartPost", "a(sasbttttuii)", map_exec, 0 },
- { "ExecReload", "a(sasbttttuii)", map_exec, 0 },
- { "ExecStopPre", "a(sasbttttuii)", map_exec, 0 },
- { "ExecStop", "a(sasbttttuii)", map_exec, 0 },
- { "ExecStopPost", "a(sasbttttuii)", map_exec, 0 },
+ { "Id", "s", NULL, offsetof(UnitStatusInfo, id) },
+ { "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
+ { "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
+ { "SubState", "s", NULL, offsetof(UnitStatusInfo, sub_state) },
+ { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) },
+ { "UnitFilePreset", "s", NULL, offsetof(UnitStatusInfo, unit_file_preset) },
+ { "Description", "s", NULL, offsetof(UnitStatusInfo, description) },
+ { "Following", "s", NULL, offsetof(UnitStatusInfo, following) },
+ { "Documentation", "as", NULL, offsetof(UnitStatusInfo, documentation) },
+ { "FragmentPath", "s", NULL, offsetof(UnitStatusInfo, fragment_path) },
+ { "SourcePath", "s", NULL, offsetof(UnitStatusInfo, source_path) },
+ { "ControlGroup", "s", NULL, offsetof(UnitStatusInfo, control_group) },
+ { "DropInPaths", "as", NULL, offsetof(UnitStatusInfo, dropin_paths) },
+ { "LoadError", "(ss)", map_load_error, offsetof(UnitStatusInfo, load_error) },
+ { "Result", "s", NULL, offsetof(UnitStatusInfo, result) },
+ { "InactiveExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, inactive_exit_timestamp) },
+ { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(UnitStatusInfo, inactive_exit_timestamp_monotonic) },
+ { "ActiveEnterTimestamp", "t", NULL, offsetof(UnitStatusInfo, active_enter_timestamp) },
+ { "ActiveExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, active_exit_timestamp) },
+ { "InactiveEnterTimestamp", "t", NULL, offsetof(UnitStatusInfo, inactive_enter_timestamp) },
+ { "NeedDaemonReload", "b", NULL, offsetof(UnitStatusInfo, need_daemon_reload) },
+ { "Transient", "b", NULL, offsetof(UnitStatusInfo, transient) },
+ { "ExecMainPID", "u", NULL, offsetof(UnitStatusInfo, main_pid) },
+ { "MainPID", "u", map_main_pid, 0 },
+ { "ControlPID", "u", NULL, offsetof(UnitStatusInfo, control_pid) },
+ { "StatusText", "s", NULL, offsetof(UnitStatusInfo, status_text) },
+ { "PIDFile", "s", NULL, offsetof(UnitStatusInfo, pid_file) },
+ { "StatusErrno", "i", NULL, offsetof(UnitStatusInfo, status_errno) },
+ { "ExecMainStartTimestamp", "t", NULL, offsetof(UnitStatusInfo, start_timestamp) },
+ { "ExecMainExitTimestamp", "t", NULL, offsetof(UnitStatusInfo, exit_timestamp) },
+ { "ExecMainCode", "i", NULL, offsetof(UnitStatusInfo, exit_code) },
+ { "ExecMainStatus", "i", NULL, offsetof(UnitStatusInfo, exit_status) },
+ { "ConditionTimestamp", "t", NULL, offsetof(UnitStatusInfo, condition_timestamp) },
+ { "ConditionResult", "b", NULL, offsetof(UnitStatusInfo, condition_result) },
+ { "Conditions", "a(sbbsi)", map_conditions, 0 },
+ { "AssertTimestamp", "t", NULL, offsetof(UnitStatusInfo, assert_timestamp) },
+ { "AssertResult", "b", NULL, offsetof(UnitStatusInfo, assert_result) },
+ { "Asserts", "a(sbbsi)", map_asserts, 0 },
+ { "NextElapseUSecRealtime", "t", NULL, offsetof(UnitStatusInfo, next_elapse_real) },
+ { "NextElapseUSecMonotonic", "t", NULL, offsetof(UnitStatusInfo, next_elapse_monotonic) },
+ { "NAccepted", "u", NULL, offsetof(UnitStatusInfo, n_accepted) },
+ { "NConnections", "u", NULL, offsetof(UnitStatusInfo, n_connections) },
+ { "NRefused", "u", NULL, offsetof(UnitStatusInfo, n_refused) },
+ { "Accept", "b", NULL, offsetof(UnitStatusInfo, accept) },
+ { "Listen", "a(ss)", map_listen, offsetof(UnitStatusInfo, listen) },
+ { "SysFSPath", "s", NULL, offsetof(UnitStatusInfo, sysfs_path) },
+ { "Where", "s", NULL, offsetof(UnitStatusInfo, where) },
+ { "What", "s", NULL, offsetof(UnitStatusInfo, what) },
+ { "MemoryCurrent", "t", NULL, offsetof(UnitStatusInfo, memory_current) },
+ { "DefaultMemoryMin", "t", NULL, offsetof(UnitStatusInfo, default_memory_min) },
+ { "DefaultMemoryLow", "t", NULL, offsetof(UnitStatusInfo, default_memory_low) },
+ { "MemoryMin", "t", NULL, offsetof(UnitStatusInfo, memory_min) },
+ { "MemoryLow", "t", NULL, offsetof(UnitStatusInfo, memory_low) },
+ { "MemoryHigh", "t", NULL, offsetof(UnitStatusInfo, memory_high) },
+ { "MemoryMax", "t", NULL, offsetof(UnitStatusInfo, memory_max) },
+ { "MemorySwapMax", "t", NULL, offsetof(UnitStatusInfo, memory_swap_max) },
+ { "MemoryLimit", "t", NULL, offsetof(UnitStatusInfo, memory_limit) },
+ { "CPUUsageNSec", "t", NULL, offsetof(UnitStatusInfo, cpu_usage_nsec) },
+ { "TasksCurrent", "t", NULL, offsetof(UnitStatusInfo, tasks_current) },
+ { "TasksMax", "t", NULL, offsetof(UnitStatusInfo, tasks_max) },
+ { "IPIngressBytes", "t", NULL, offsetof(UnitStatusInfo, ip_ingress_bytes) },
+ { "IPEgressBytes", "t", NULL, offsetof(UnitStatusInfo, ip_egress_bytes) },
+ { "IOReadBytes", "t", NULL, offsetof(UnitStatusInfo, io_read_bytes) },
+ { "IOWriteBytes", "t", NULL, offsetof(UnitStatusInfo, io_write_bytes) },
+ { "ExecStartPre", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStartPreEx", "a(sasasttttuii)", map_exec, 0 },
+ { "ExecStart", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStartEx", "a(sasasttttuii)", map_exec, 0 },
+ { "ExecStartPost", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStartPostEx", "a(sasasttttuii)", map_exec, 0 },
+ { "ExecReload", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStopPre", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStop", "a(sasbttttuii)", map_exec, 0 },
+ { "ExecStopPost", "a(sasbttttuii)", map_exec, 0 },
{}
};
if (init) {
const char *root_systemd_path = NULL, *root_init_path = NULL;
- root_systemd_path = strjoina(root, "/" SYSTEMD_BINARY_PATH);
- root_init_path = strjoina(root, "/", init);
+ root_systemd_path = prefix_roota(root, "/" SYSTEMD_BINARY_PATH);
+ root_init_path = prefix_roota(root, init);
/* If the passed init is actually the same as the
* systemd binary, then let's suppress it. */
assert(name);
assert(ret_path);
- path = strjoin(paths->persistent_config, "/", name);
+ path = path_join(paths->persistent_config, name);
if (!path)
return log_oom();
if (arg_runtime) {
- run = strjoin(paths->runtime_config, "/", name);
+ run = path_join(paths->runtime_config, name);
if (!run)
return log_oom();
}
" When shutting down or sleeping, ignore inhibitors\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
+ " --what=RESOURCES Which types of resources to remove\n"
" --now Start or stop unit in addition to enabling or disabling it\n"
" --dry-run Only print what would be done\n"
" -q --quiet Suppress output\n"
" if supported, otherwise restart\n"
" isolate UNIT Start one unit and stop all others\n"
" kill UNIT... Send signal to processes of a unit\n"
+ " clean UNIT... Clean runtime, cache, state, logs or\n"
+ " or configuration of unit\n"
" is-active PATTERN... Check whether units are active\n"
" is-failed PATTERN... Check whether units are failed\n"
" status [PATTERN...|PID...] Show runtime status of one or more units\n"
ARG_NOW,
ARG_MESSAGE,
ARG_WAIT,
+ ARG_WHAT,
};
static const struct option options[] = {
{ "now", no_argument, NULL, ARG_NOW },
{ "message", required_argument, NULL, ARG_MESSAGE },
{ "show-transaction", no_argument, NULL, 'T' },
+ { "what", required_argument, NULL, ARG_WHAT },
{}
};
arg_show_transaction = true;
break;
+ case ARG_WHAT: {
+ const char *p;
+
+ if (isempty(optarg))
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--what= requires arguments.");
+
+ for (p = optarg;;) {
+ _cleanup_free_ char *k = NULL;
+
+ r = extract_first_word(&p, &k, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse directory type: %s", optarg);
+ if (r == 0)
+ break;
+
+ if (streq(k, "help")) {
+ puts("runtime\n"
+ "state\n"
+ "cache\n"
+ "logs\n"
+ "configuration");
+ return 0;
+ }
+
+ r = strv_consume(&arg_clean_what, TAKE_PTR(k));
+ if (r < 0)
+ return log_oom();
+ }
+
+ break;
+ }
+
case '.':
/* Output an error mimicking getopt, and print a hint afterwards */
log_error("%s: invalid option -- '.'", program_invocation_name);
{ "list-sockets", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_sockets },
{ "list-timers", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_timers },
{ "list-jobs", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_jobs },
- { "list-machines", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY|VERB_MUST_BE_ROOT, list_machines },
+ { "list-machines", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_machines },
{ "clear-jobs", VERB_ANY, 1, VERB_ONLINE_ONLY, trivial_method },
{ "cancel", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, cancel_job },
{ "start", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
{ "condrestart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with RH */
{ "isolate", 2, 2, VERB_ONLINE_ONLY, start_unit },
{ "kill", 2, VERB_ANY, VERB_ONLINE_ONLY, kill_unit },
+ { "clean", 2, VERB_ANY, VERB_ONLINE_ONLY, clean_unit },
{ "is-active", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active },
{ "check", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active }, /* deprecated alias of is-active */
{ "is-failed", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_failed },
sd_notifyf(0, "STATUS=Failed to start up: %s\n"
"ERRNO=%i",
- strerror(errno),
+ strerror_safe(errno),
errno);
See sd_notifyf(3) for more information.
int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type, struct in_addr *data);
int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type, struct in6_addr *data);
int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type);
+int sd_netlink_message_enter_array(sd_netlink_message *m, unsigned short type);
int sd_netlink_message_exit_container(sd_netlink_message *m);
int sd_netlink_message_open_array(sd_netlink_message *m, uint16_t type);
* -ENODATA: networkd is not aware of any links
*/
int sd_network_get_operational_state(char **state);
+int sd_network_get_carrier_state(char **state);
+int sd_network_get_address_state(char **state);
/* Get DNS entries for all links. These are string representations of
* IP addresses */
*/
int sd_network_link_get_operational_state(int ifindex, char **state);
int sd_network_link_get_required_operstate_for_online(int ifindex, char **state);
+int sd_network_link_get_carrier_state(int ifindex, char **state);
+int sd_network_link_get_address_state(int ifindex, char **state);
/* Indicates whether the network is relevant to being online.
* Possible return codes:
/* Indicates whether or not DNS-over-TLS should be enabled for the
* link.
- * Possible levels of support: strict, no, opportunistic
+ * Possible levels of support: yes, no, opportunistic
* Possible return codes:
* -ENODATA: networkd is not aware of the link
*/
/* Parse columns */
p = buffer;
- r = extract_many_words(&p, NULL, EXTRACT_QUOTES,
+ r = extract_many_words(&p, NULL, EXTRACT_UNQUOTE,
&action, &name, &id, &description, &home, &shell, NULL);
if (r < 0)
return log_error_errno(r, "[%s:%u] Syntax error.", fname, line);
assert(service);
assert(alias);
- link = strjoina(arg_dest, "/", alias);
+ link = prefix_roota(arg_dest, alias);
r = symlink(service, link);
if (r < 0) {
if (!path_escaped)
return log_oom();
- unit = strjoina(arg_dest, "/", s->name);
+ unit = prefix_roota(arg_dest, s->name);
/* We might already have a symlink with the same name from a Provides:,
* or from backup files like /etc/init.d/foo.bak. Real scripts always win,
for (;;) {
_cleanup_free_ char *word = NULL, *m = NULL;
- r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
+ r = extract_first_word(&text, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
if (r < 0)
return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line);
if (r == 0)
_cleanup_free_ char *word = NULL, *m = NULL;
bool is_before;
- r = extract_first_word(&text, &word, NULL, EXTRACT_QUOTES|EXTRACT_RELAX);
+ r = extract_first_word(&text, &word, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX);
if (r < 0)
return log_error_errno(r, "[%s:%u] Failed to parse word from provides string: %m", s->path, line);
if (r == 0)
continue;
}
- fpath = strjoin(*path, "/", de->d_name);
+ fpath = path_join(*path, de->d_name);
if (!fpath)
return log_oom();
_cleanup_free_ char *path = NULL;
struct dirent *de;
- path = strjoin(*p, "/", rcnd_table[i].path);
+ path = path_join(*p, rcnd_table[i].path);
if (!path) {
r = log_oom();
goto finish;
if (a < 0 || b < 0)
continue;
- fpath = strjoin(*p, "/", de->d_name);
+ fpath = path_join(*p, de->d_name);
if (!fpath) {
r = log_oom();
goto finish;
[],
[]],
+ [['src/test/test-format-util.c'],
+ [],
+ []],
+
[['src/test/test-ratelimit.c'],
[],
[]],
#include "alloc-util.h"
#include "macro.h"
#include "memory-util.h"
+#include "random-util.h"
#include "tests.h"
static void test_alloca(void) {
log_debug("z: %p", &z);
}
+static void test_auto_erase_memory(void) {
+ _cleanup_(erase_and_freep) uint8_t *p1, *p2;
+
+ assert_se(p1 = new(uint8_t, 1024));
+ assert_se(p2 = new(uint8_t, 1024));
+
+ genuine_random_bytes(p1, 1024, RANDOM_BLOCK);
+
+ /* before we exit the scope, do something with this data, so that the compiler won't optimize this away */
+ memcpy(p2, p1, 1024);
+ for (size_t i = 0; i < 1024; i++)
+ assert_se(p1[i] == p2[i]);
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
test_memdup_multiply_and_greedy_realloc();
test_bool_assign();
test_cleanup_order();
+ test_auto_erase_memory();
return 0;
}
#include "bpf-program.h"
#include "load-fragment.h"
#include "manager.h"
+#include "missing.h"
#include "rm-rf.h"
#include "service.h"
#include "test-helper.h"
int main(int argc, char *argv[]) {
struct bpf_insn exit_insn[] = {
- BPF_MOV64_IMM(BPF_REG_0, 1),
+ BPF_MOV64_IMM(BPF_REG_0, 0), /* drop */
BPF_EXIT_INSN()
};
char log_buf[65535];
struct rlimit rl;
int r;
+ union bpf_attr attr;
+ bool test_custom_filter = false;
+ const char *test_prog = "/sys/fs/bpf/test-dropper";
test_setup_logging(LOG_DEBUG);
return log_tests_skipped("BPF firewalling not supported");
assert_se(r > 0);
- if (r == BPF_FIREWALL_SUPPORTED_WITH_MULTI)
+ if (r == BPF_FIREWALL_SUPPORTED_WITH_MULTI) {
log_notice("BPF firewalling with BPF_F_ALLOW_MULTI supported. Yay!");
- else
+ test_custom_filter = true;
+ } else
log_notice("BPF firewalling (though without BPF_F_ALLOW_MULTI) supported. Good.");
r = bpf_program_load_kernel(p, log_buf, ELEMENTSOF(log_buf));
assert(r >= 0);
+ if (test_custom_filter) {
+ attr = (union bpf_attr) {
+ .pathname = PTR_TO_UINT64(test_prog),
+ .bpf_fd = p->kernel_fd,
+ .file_flags = 0,
+ };
+
+ (void) unlink(test_prog);
+
+ r = bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
+ if (r < 0) {
+ log_warning_errno(errno, "BPF object pinning failed, will not run custom filter test: %m");
+ test_custom_filter = false;
+ }
+ }
+
p = bpf_program_unref(p);
/* The simple tests succeeded. Now let's try full unit-based use-case. */
assert_se(SERVICE(u)->exec_command[SERVICE_EXEC_START]->command_next->exec_status.code != CLD_EXITED ||
SERVICE(u)->exec_command[SERVICE_EXEC_START]->command_next->exec_status.status != EXIT_SUCCESS);
+ if (test_custom_filter) {
+ assert_se(u = unit_new(m, sizeof(Service)));
+ assert_se(unit_add_name(u, "custom-filter.service") == 0);
+ assert_se(cc = unit_get_cgroup_context(u));
+ u->perpetual = true;
+
+ cc->ip_accounting = true;
+
+ assert_se(config_parse_ip_filter_bpf_progs(u->id, "filename", 1, "Service", 1, "IPIngressFilterPath", 0, test_prog, &cc->ip_filters_ingress, u) == 0);
+ assert_se(config_parse_exec(u->id, "filename", 1, "Service", 1, "ExecStart", SERVICE_EXEC_START, "-/bin/ping -c 1 127.0.0.1 -W 5", SERVICE(u)->exec_command, u) == 0);
+
+ SERVICE(u)->type = SERVICE_ONESHOT;
+ u->load_state = UNIT_LOADED;
+
+ assert_se(unit_start(u) >= 0);
+
+ while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED))
+ assert_se(sd_event_run(m->event, UINT64_MAX) >= 0);
+
+ assert_se(SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.code != CLD_EXITED ||
+ SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.status != EXIT_SUCCESS);
+
+ (void) unlink(test_prog);
+ assert_se(SERVICE(u)->state == SERVICE_DEAD);
+ }
+
return 0;
}
#include "btrfs-util.h"
#include "fd-util.h"
#include "fileio.h"
+#include "format-util.h"
#include "log.h"
-#include "parse-util.h"
#include "string-util.h"
#include "util.h"
#include "alloc-util.h"
#include "calendarspec.h"
+#include "errno-util.h"
#include "string-util.h"
#include "util.h"
u = now(CLOCK_REALTIME);
r = calendar_spec_next_usec(c, u, &u);
- printf("Next: %s\n", r < 0 ? strerror(-r) : format_timestamp(buf, sizeof(buf), u));
+ printf("Next: %s\n", r < 0 ? strerror_safe(r) : format_timestamp(buf, sizeof(buf), u));
calendar_spec_free(c);
assert_se(calendar_spec_from_string(p, &c) >= 0);
u = after;
r = calendar_spec_next_usec(c, after, &u);
- printf("At: %s\n", r < 0 ? strerror(-r) : format_timestamp_us(buf, sizeof buf, u));
+ printf("At: %s\n", r < 0 ? strerror_safe(r) : format_timestamp_us(buf, sizeof buf, u));
if (expect != (usec_t)-1)
assert_se(r >= 0 && u == expect);
else
assert_se((r = calendar_spec_next_usec(c, n, &u)) >= 0);
printf("Now: %s (%"PRIu64")\n", format_timestamp_us(buf, sizeof buf, n), n);
- printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror(-r) : format_timestamp_us(buf, sizeof buf, u), u);
+ printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror_safe(r) : format_timestamp_us(buf, sizeof buf, u), u);
assert_se((r = calendar_spec_next_usec(c, u, &w)) >= 0);
- printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror(-r) : format_timestamp_us(zaf, sizeof zaf, w), w);
+ printf("Next hourly: %s (%"PRIu64")\n", r < 0 ? strerror_safe(r) : format_timestamp_us(zaf, sizeof zaf, w), w);
assert_se(n < u);
assert_se(u <= n + USEC_PER_HOUR);
condition = condition_new(CONDITION_PATH_EXISTS, "/bin/s?", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_PATH_EXISTS_GLOB, "/bin/s?", false, true);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_PATH_EXISTS, "/thiscertainlywontexist", false, true);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_DIRECTORY, "/bin", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_DIRECTORY_NOT_EMPTY, "/bin", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_FILE_NOT_EMPTY, "/bin/sh", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/bin/sh", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_FILE_IS_EXECUTABLE, "/etc/passwd", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/proc", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_MOUNT_POINT, "/bin", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_READ_WRITE, "/tmp", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_PATH_IS_SYMBOLIC_LINK, "/dev/stdout", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
}
/* Invalid controllers are ignored */
condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, "thisisnotarealcontroller", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, "thisisnotarealcontroller", false, true);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
assert_se(cg_mask_supported(&system_mask) >= 0);
log_info("this controller is available");
condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, true);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
} else {
log_info("this controller is unavailable");
condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, local_controller_name, false, true);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
}
}
condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_CONTROL_GROUP_CONTROLLER, strempty(controller_name), false, true);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
}
condition = condition_new(CONDITION_HOST, sid, false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_HOST, "garbage value jjjjjjjjjjjjjj", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_HOST, sid, false, true);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
hostname = gethostname_malloc();
else {
condition = condition_new(CONDITION_HOST, hostname, false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
}
}
condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "thisreallyshouldntbeonthekernelcommandline", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_KERNEL_COMMAND_LINE, "andthis=neither", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
}
condition = condition_new(CONDITION_KERNEL_VERSION, "*thisreallyshouldntbeinthekernelversion*", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_KERNEL_VERSION, "*", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
+ /* An artificially empty condition. It evaluates to true, but normally
+ * such condition cannot be created, because the condition list is reset instead. */
condition = condition_new(CONDITION_KERNEL_VERSION, "", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
assert_se(uname(&u) >= 0);
condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
strshorten(u.release, 4);
condition = condition_new(CONDITION_KERNEL_VERSION, u.release, false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
/* 0.1.2 would be a very very very old kernel */
condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, ">0.1.2", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition) > 0);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "'>0.1.2' '<9.0.0'", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition) > 0);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, "> 0.1.2 < 9.0.0", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition) == -EINVAL);
+ condition_free(condition);
+
+ condition = condition_new(CONDITION_KERNEL_VERSION, ">", false, false);
+ assert_se(condition);
+ assert_se(condition_test(condition) == -EINVAL);
condition_free(condition);
condition = condition_new(CONDITION_KERNEL_VERSION, ">= 0.1.2", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_KERNEL_VERSION, "< 0.1.2", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_KERNEL_VERSION, "<= 0.1.2", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_KERNEL_VERSION, "= 0.1.2", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
/* 4711.8.15 is a very very very future kernel */
condition = condition_new(CONDITION_KERNEL_VERSION, "< 4711.8.15", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_KERNEL_VERSION, "<= 4711.8.15", false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_KERNEL_VERSION, "= 4711.8.15", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_KERNEL_VERSION, "> 4711.8.15", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_KERNEL_VERSION, ">= 4711.8.15", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
assert_se(uname(&u) >= 0);
v = strjoina(">=", u.release);
condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
v = strjoina("= ", u.release);
condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
v = strjoina("<=", u.release);
condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
v = strjoina("> ", u.release);
condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
v = strjoina("< ", u.release);
condition = condition_new(CONDITION_KERNEL_VERSION, v, false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
}
condition = condition_new(CONDITION_NULL, NULL, false, false);
assert_se(condition);
- assert_se(condition_test(condition));
+ assert_se(condition_test(condition) > 0);
condition_free(condition);
condition = condition_new(CONDITION_NULL, NULL, false, true);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
}
condition = condition_new(CONDITION_SECURITY, "garbage oifdsjfoidsjoj", false, false);
assert_se(condition);
- assert_se(!condition_test(condition));
+ assert_se(condition_test(condition) == 0);
condition_free(condition);
condition = condition_new(CONDITION_SECURITY, "selinux", false, true);
char **dirs = STRV_MAKE("/dir1", "/dir2", "/dir3");
_cleanup_free_ const char
- *foo1 = prefix_root(root, "/dir1/foo.conf"),
- *foo2 = prefix_root(root, "/dir2/foo.conf"),
- *bar2 = prefix_root(root, "/dir2/bar.conf"),
- *zzz3 = prefix_root(root, "/dir3/zzz.conf"),
- *whatever = prefix_root(root, "/whatever.conf");
+ *foo1 = path_join(root, "/dir1/foo.conf"),
+ *foo2 = path_join(root, "/dir2/foo.conf"),
+ *bar2 = path_join(root, "/dir2/bar.conf"),
+ *zzz3 = path_join(root, "/dir3/zzz.conf"),
+ *whatever = path_join(root, "/whatever.conf");
assert_se(conf_files_insert(&s, root, dirs, "/dir2/foo.conf") == 0);
assert_se(strv_equal(s, STRV_MAKE(foo2)));
"setting1=2\n"
"setting1=1\n", /* repeated settings */
+ "[Section]\n"
+ "[Section]\n"
+ "setting1=1\n"
+ "setting1=2\\\n"
+ " \n" /* empty line breaks continuation */
+ "setting1=1\n", /* repeated settings */
+
"[Section]\n"
"setting1=1\\\n" /* normal continuation */
"2\\\n"
CONFIG_PARSE_WARN, NULL);
switch (i) {
- case 0 ... 3:
+ case 0 ... 4:
assert_se(r == 0);
assert_se(streq(setting1, "1"));
break;
- case 4 ... 9:
+ case 5 ... 10:
assert_se(r == 0);
assert_se(streq(setting1, "1 2 3"));
break;
- case 10:
+ case 11:
assert_se(r == 0);
assert_se(streq(setting1, "1\\\\ \\\\2"));
break;
- case 11:
+ case 12:
assert_se(r == 0);
assert_se(streq(setting1, x1000("ABCD")));
break;
- case 12 ... 13:
+ case 13 ... 14:
assert_se(r == 0);
assert_se(streq(setting1, x1000("ABCD") " foobar"));
break;
- case 14 ... 15:
+ case 15 ... 16:
assert_se(r == -ENOBUFS);
assert_se(setting1 == NULL);
break;
#include "rm-rf.h"
#include "test-helper.h"
#include "tests.h"
+#include "service.h"
int main(int argc, char *argv[]) {
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL;
_cleanup_(manager_freep) Manager *m = NULL;
- Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL, *h = NULL, *unit_with_multiple_dashes = NULL;
+ Unit *a = NULL, *b = NULL, *c = NULL, *d = NULL, *e = NULL, *g = NULL,
+ *h = NULL, *i = NULL, *a_conj = NULL, *unit_with_multiple_dashes = NULL;
Job *j;
int r;
assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, NULL, NULL, &j) == 0);
manager_dump_jobs(m, stdout, "\t");
+ printf("Load5:\n");
+ manager_clear_jobs(m);
+ assert_se(manager_load_startable_unit_or_warn(m, "i.service", NULL, &i) >= 0);
+ SERVICE(a)->state = SERVICE_RUNNING;
+ SERVICE(d)->state = SERVICE_RUNNING;
+ manager_dump_units(m, stdout, "\t");
+
+ printf("Test11: (Start/stop job ordering, execution cycle)\n");
+ assert_se(manager_add_job(m, JOB_START, i, JOB_FAIL, NULL, NULL, &j) == 0);
+ assert_se(a->job && a->job->type == JOB_STOP);
+ assert_se(d->job && d->job->type == JOB_STOP);
+ assert_se(b->job && b->job->type == JOB_START);
+ manager_dump_jobs(m, stdout, "\t");
+
+ printf("Load6:\n");
+ manager_clear_jobs(m);
+ assert_se(manager_load_startable_unit_or_warn(m, "a-conj.service", NULL, &a_conj) >= 0);
+ SERVICE(a)->state = SERVICE_DEAD;
+ manager_dump_units(m, stdout, "\t");
+
+ printf("Test12: (Trivial cycle, Unfixable)\n");
+ assert_se(manager_add_job(m, JOB_START, a_conj, JOB_REPLACE, NULL, NULL, &j) == -EDEADLK);
+ manager_dump_jobs(m, stdout, "\t");
+
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b));
assert_se(!hashmap_get(b->dependencies[UNIT_RELOAD_PROPAGATED_FROM], a));
assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], c));
assert_se(r == 42);
}
+static void test_exec_command_flags_from_strv(void) {
+ ExecCommandFlags flags = 0;
+ char **valid_strv = STRV_MAKE("no-env-expand", "no-setuid", "ignore-failure");
+ char **invalid_strv = STRV_MAKE("no-env-expand", "no-setuid", "nonexistent-option", "ignore-failure");
+ int r;
+
+ r = exec_command_flags_from_strv(valid_strv, &flags);
+
+ assert_se(r == 0);
+ assert_se(FLAGS_SET(flags, EXEC_COMMAND_NO_ENV_EXPAND));
+ assert_se(FLAGS_SET(flags, EXEC_COMMAND_NO_SETUID));
+ assert_se(FLAGS_SET(flags, EXEC_COMMAND_IGNORE_FAILURE));
+ assert_se(!FLAGS_SET(flags, EXEC_COMMAND_AMBIENT_MAGIC));
+ assert_se(!FLAGS_SET(flags, EXEC_COMMAND_FULLY_PRIVILEGED));
+
+ r = exec_command_flags_from_strv(invalid_strv, &flags);
+
+ assert_se(r == -EINVAL);
+}
+
+static void test_exec_command_flags_to_strv(void) {
+ _cleanup_strv_free_ char **opts = NULL, **empty_opts = NULL, **invalid_opts = NULL;
+ ExecCommandFlags flags = 0;
+ int r;
+
+ flags |= (EXEC_COMMAND_AMBIENT_MAGIC|EXEC_COMMAND_NO_ENV_EXPAND|EXEC_COMMAND_IGNORE_FAILURE);
+
+ r = exec_command_flags_to_strv(flags, &opts);
+
+ assert_se(r == 0);
+ assert_se(strv_equal(opts, STRV_MAKE("ignore-failure", "ambient", "no-env-expand")));
+
+ r = exec_command_flags_to_strv(0, &empty_opts);
+
+ assert_se(r == 0);
+ assert_se(strv_equal(empty_opts, STRV_MAKE_EMPTY));
+
+ flags = _EXEC_COMMAND_FLAGS_INVALID;
+
+ r = exec_command_flags_to_strv(flags, &invalid_opts);
+
+ assert_se(r == -EINVAL);
+}
+
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
test_stdout_gathering();
test_environment_gathering();
test_error_catching();
+ test_exec_command_flags_from_strv();
+ test_exec_command_flags_to_strv();
return 0;
}
assert_se(isempty(p));
p = original = "\"foobar\" \'waldo\'";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
assert_se(streq(t, "foobar"));
free(t);
assert_se(p == original + 9);
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
assert_se(streq(t, "waldo"));
free(t);
assert_se(isempty(p));
assert_se(isempty(p));
p = original = "\"";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) == -EINVAL);
assert_se(p == original + 1);
p = original = "\'";
assert_se(isempty(p));
p = original = "\'";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) == -EINVAL);
assert_se(p == original + 1);
p = original = "\'fooo";
assert_se(isempty(p));
p = original = "\'fooo";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) == -EINVAL);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) == -EINVAL);
assert_se(p == original + 5);
p = original = "\'fooo";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX) > 0);
assert_se(streq(t, "fooo"));
free(t);
assert_se(isempty(p));
p = original = "\"fooo";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX) > 0);
assert_se(streq(t, "fooo"));
free(t);
assert_se(isempty(p));
assert_se(isempty(p));
p = original = "yay\'foo\'bar";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
assert_se(streq(t, "yayfoobar"));
free(t);
assert_se(isempty(p));
free(t);
assert_se(p == original + 13);
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE) > 0);
assert_se(streq(t, "pi\360\237\222\251le"));
free(t);
assert_se(isempty(p));
assert_se(p == original + 5);
p = original = "\"foo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX) > 0);
assert_se(streq(t, "foo"));
free(t);
assert_se(isempty(p));
assert_se(isempty(p));
p = original = "\"foo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE_RELAX) == -EINVAL);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE_RELAX) == -EINVAL);
assert_se(p == original + 5);
p = original = "\"foo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
assert_se(streq(t, "foo\\"));
free(t);
assert_se(isempty(p));
p = original = "\"foo\\";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0);
assert_se(streq(t, "foo\\"));
free(t);
assert_se(isempty(p));
assert_se(isempty(p));
p = original = "-N ''";
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
assert_se(streq(t, "-N"));
free(t);
assert_se(p == original + 3);
- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_QUOTES) > 0);
+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE) > 0);
assert_se(streq(t, ""));
free(t);
assert_se(isempty(p));
assert_se(isempty(p));
p = original = "\"foobar\" \'waldo\'";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "foobar"));
free(t);
assert_se(p == original + 9);
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "waldo"));
free(t);
assert_se(isempty(p));
assert_se(isempty(p));
p = original = "\"";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE, NULL, "fake", 1, original) == -EINVAL);
assert_se(p == original + 1);
p = original = "\'";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE, NULL, "fake", 1, original) == -EINVAL);
assert_se(p == original + 1);
p = original = "\'fooo";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE, NULL, "fake", 1, original) == -EINVAL);
assert_se(p == original + 5);
p = original = "\'fooo";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "fooo"));
free(t);
assert_se(isempty(p));
free(t);
assert_se(p == original + 13);
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "pi\360\237\222\251le"));
free(t);
assert_se(isempty(p));
assert_se(isempty(p));
p = original = "\"foo\\";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE, NULL, "fake", 1, original) == -EINVAL);
assert_se(p == original + 5);
p = original = "\"foo\\";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "foo"));
free(t);
assert_se(isempty(p));
p = original = "\"foo\\";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) == -EINVAL);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE, NULL, "fake", 1, original) == -EINVAL);
assert_se(p == original + 5);
p = original = "\"foo\\";
- assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
+ assert_se(extract_first_word_and_warn(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_RELAX, NULL, "fake", 1, original) > 0);
assert_se(streq(t, "foo"));
free(t);
assert_se(isempty(p));
free(b);
p = original = "'foobar' wa\"ld\"i ";
- assert_se(extract_many_words(&p, NULL, EXTRACT_QUOTES, &a, &b, &c, NULL) == 2);
+ assert_se(extract_many_words(&p, NULL, EXTRACT_UNQUOTE, &a, &b, &c, NULL) == 2);
assert_se(isempty(p));
assert_se(streq_ptr(a, "foobar"));
assert_se(streq_ptr(b, "waldi"));
--- /dev/null
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "format-util.h"
+#include "macro.h"
+#include "string-util.h"
+
+static void test_format_bytes_one(uint64_t val, bool trailing_B, const char *iec_with_p, const char *iec_without_p,
+ const char *si_with_p, const char *si_without_p) {
+ char buf[FORMAT_BYTES_MAX];
+
+ assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_with_p));
+ assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_USE_IEC | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), iec_without_p));
+ assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, FORMAT_BYTES_BELOW_POINT | (trailing_B ? FORMAT_BYTES_TRAILING_B : 0)), si_with_p));
+ assert_se(streq_ptr(format_bytes_full(buf, sizeof buf, val, trailing_B ? FORMAT_BYTES_TRAILING_B : 0), si_without_p));
+}
+
+static void test_format_bytes(void) {
+ test_format_bytes_one(900, true, "900B", "900B", "900B", "900B");
+ test_format_bytes_one(900, false, "900", "900", "900", "900");
+ test_format_bytes_one(1023, true, "1023B", "1023B", "1.0K", "1K");
+ test_format_bytes_one(1023, false, "1023", "1023", "1.0K", "1K");
+ test_format_bytes_one(1024, true, "1.0K", "1K", "1.0K", "1K");
+ test_format_bytes_one(1024, false, "1.0K", "1K", "1.0K", "1K");
+ test_format_bytes_one(1100, true, "1.0K", "1K", "1.1K", "1K");
+ test_format_bytes_one(1500, true, "1.4K", "1K", "1.5K", "1K");
+ test_format_bytes_one(UINT64_C(3)*1024*1024, true, "3.0M", "3M", "3.1M", "3M");
+ test_format_bytes_one(UINT64_C(3)*1024*1024*1024, true, "3.0G", "3G", "3.2G", "3G");
+ test_format_bytes_one(UINT64_C(3)*1024*1024*1024*1024, true, "3.0T", "3T", "3.2T", "3T");
+ test_format_bytes_one(UINT64_C(3)*1024*1024*1024*1024*1024, true, "3.0P", "3P", "3.3P", "3P");
+ test_format_bytes_one(UINT64_C(3)*1024*1024*1024*1024*1024*1024, true, "3.0E", "3E", "3.4E", "3E");
+ test_format_bytes_one(UINT64_MAX, true, NULL, NULL, NULL, NULL);
+ test_format_bytes_one(UINT64_MAX, false, NULL, NULL, NULL, NULL);
+}
+
+int main(void) {
+ test_format_bytes();
+
+ return 0;
+}
"0::/user.slice/user-1002.slice/user@1002.service/gnome-terminal-server.service\n"
static void test_basic_parsing(void) {
- _cleanup_(journal_importer_cleanup) JournalImporter imp = {};
+ _cleanup_(journal_importer_cleanup) JournalImporter imp = JOURNAL_IMPORTER_INIT(-1);
_cleanup_free_ char *journal_data_path = NULL;
int r;
}
static void test_bad_input(void) {
- _cleanup_(journal_importer_cleanup) JournalImporter imp = {};
+ _cleanup_(journal_importer_cleanup) JournalImporter imp = JOURNAL_IMPORTER_INIT(-1);
_cleanup_free_ char *journal_data_path = NULL;
int r;
}
static void test_list(void) {
- struct udev_list list = {};
+ _cleanup_(udev_list_freep) struct udev_list *list = NULL;
struct udev_list_entry *e;
/* empty list */
- udev_list_init(&list, false);
- assert_se(!udev_list_get_entry(&list));
+ assert_se(list = udev_list_new(false));
+ assert_se(!udev_list_get_entry(list));
+ list = udev_list_free(list);
/* unique == false */
- udev_list_init(&list, false);
- assert_se(udev_list_entry_add(&list, "aaa", "hoge"));
- assert_se(udev_list_entry_add(&list, "aaa", "hogehoge"));
- assert_se(udev_list_entry_add(&list, "bbb", "foo"));
- e = udev_list_get_entry(&list);
+ assert_se(list = udev_list_new(false));
+ assert_se(udev_list_entry_add(list, "aaa", "hoge"));
+ assert_se(udev_list_entry_add(list, "aaa", "hogehoge"));
+ assert_se(udev_list_entry_add(list, "bbb", "foo"));
+ e = udev_list_get_entry(list);
assert_se(e);
assert_se(streq_ptr(udev_list_entry_get_name(e), "aaa"));
assert_se(streq_ptr(udev_list_entry_get_value(e), "hoge"));
assert_se(!udev_list_entry_get_by_name(e, "aaa"));
assert_se(!udev_list_entry_get_by_name(e, "bbb"));
assert_se(!udev_list_entry_get_by_name(e, "ccc"));
- udev_list_cleanup(&list);
+ list = udev_list_free(list);
/* unique == true */
- udev_list_init(&list, true);
- assert_se(udev_list_entry_add(&list, "aaa", "hoge"));
- assert_se(udev_list_entry_add(&list, "aaa", "hogehoge"));
- assert_se(udev_list_entry_add(&list, "bbb", "foo"));
- e = udev_list_get_entry(&list);
+ assert_se(list = udev_list_new(true));
+ assert_se(udev_list_entry_add(list, "aaa", "hoge"));
+ assert_se(udev_list_entry_add(list, "aaa", "hogehoge"));
+ assert_se(udev_list_entry_add(list, "bbb", "foo"));
+ e = udev_list_get_entry(list);
assert_se(e);
assert_se(streq_ptr(udev_list_entry_get_name(e), "aaa"));
assert_se(streq_ptr(udev_list_entry_get_value(e), "hogehoge"));
assert_se(streq_ptr(udev_list_entry_get_name(e), "aaa"));
assert_se(streq_ptr(udev_list_entry_get_value(e), "hogehoge"));
assert_se(!udev_list_entry_get_by_name(e, "ccc"));
- udev_list_cleanup(&list);
}
static int parse_args(int argc, char *argv[], const char **syspath, const char **subsystem) {
#include "format-util.h"
#include "log.h"
#include "process-util.h"
+#include "string-util.h"
#include "util.h"
assert_cc(LOG_REALM_REMOVE_LEVEL(LOG_REALM_PLUS_LEVEL(LOG_REALM_SYSTEMD, LOG_FTP | LOG_DEBUG))
#define X100(x) X10(X10(x))
#define X1000(x) X100(X10(x))
+static void test_file(void) {
+ log_info("__FILE__: %s", __FILE__);
+ log_info("RELATIVE_SOURCE_PATH: %s", RELATIVE_SOURCE_PATH);
+ log_info("PROJECT_FILE: %s", PROJECT_FILE);
+
+ assert(startswith(__FILE__, RELATIVE_SOURCE_PATH "/"));
+}
+
static void test_log_struct(void) {
log_struct(LOG_INFO,
"MESSAGE=Waldo PID="PID_FMT" (no errno)", getpid_cached(),
int main(int argc, char* argv[]) {
int target;
+ test_file();
+
for (target = 0; target < _LOG_TARGET_MAX; target++) {
log_set_target(target);
log_open();
_cleanup_free_ char *s = NULL;
const char *t;
- assert_se(s = prefix_root(r, p));
- assert_se(streq_ptr(s, expected));
+ assert_se(s = path_join(r, p));
+ assert_se(path_equal_ptr(s, expected));
t = prefix_roota(r, p);
assert_se(t);
- assert_se(streq_ptr(t, expected));
+ assert_se(path_equal_ptr(t, expected));
}
static void test_prefix_root(void) {
test_prefix_root_one("///", "/foo", "/foo");
test_prefix_root_one("/", "////foo", "/foo");
test_prefix_root_one(NULL, "////foo", "/foo");
+ test_prefix_root_one("/", "foo", "/foo");
+ test_prefix_root_one("", "foo", "foo");
+ test_prefix_root_one(NULL, "foo", "foo");
test_prefix_root_one("/foo", "/bar", "/foo/bar");
test_prefix_root_one("/foo", "bar", "/foo/bar");
int r;
r = path_extract_filename(input, &k);
- log_info("%s → %s/%s [expected: %s/%s]", strnull(input), strnull(k), strerror(-r), strnull(output), strerror(-ret));
+ log_info("%s → %s/%s [expected: %s/%s]", strnull(input), strnull(k), strerror_safe(r), strnull(output), strerror_safe(ret));
assert_se(streq_ptr(k, output));
assert_se(r == ret);
}
#include <errno.h>
+#include "format-util.h"
#include "log.h"
-#include "parse-util.h"
#include "procfs-util.h"
int main(int argc, char *argv[]) {
#include <sys/types.h>
#include <unistd.h>
+#include "errno-util.h"
#include "fd-util.h"
#include "log.h"
#include "memory-util.h"
log_info("/= running system =/");
r = can_sleep("suspend");
- log_info("Suspend configured and possible: %s", r >= 0 ? yes_no(r) : strerror(-r));
+ log_info("Suspend configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r));
r = can_sleep("hibernate");
- log_info("Hibernation configured and possible: %s", r >= 0 ? yes_no(r) : strerror(-r));
+ log_info("Hibernation configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r));
r = can_sleep("hybrid-sleep");
- log_info("Hybrid-sleep configured and possible: %s", r >= 0 ? yes_no(r) : strerror(-r));
+ log_info("Hybrid-sleep configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r));
r = can_sleep("suspend-then-hibernate");
- log_info("Suspend-then-Hibernate configured and possible: %s", r >= 0 ? yes_no(r) : strerror(-r));
+ log_info("Suspend-then-Hibernate configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r));
}
int main(int argc, char* argv[]) {
#include "utf8.h"
#include "util.h"
-static void test_string_erase(void) {
- char *x;
-
- x = strdupa("");
- assert_se(streq(string_erase(x), ""));
-
- x = strdupa("1");
- assert_se(streq(string_erase(x), ""));
-
- x = strdupa("123456789");
- assert_se(streq(string_erase(x), ""));
-
- assert_se(x[1] == '\0');
- assert_se(x[2] == '\0');
- assert_se(x[3] == '\0');
- assert_se(x[4] == '\0');
- assert_se(x[5] == '\0');
- assert_se(x[6] == '\0');
- assert_se(x[7] == '\0');
- assert_se(x[8] == '\0');
- assert_se(x[9] == '\0');
-}
-
static void test_free_and_strndup_one(char **t, const char *src, size_t l, const char *expected, bool change) {
int r;
for (;;) {
_cleanup_free_ char *word = NULL;
- r = extract_first_word(&test, &word, NULL, EXTRACT_QUOTES);
+ r = extract_first_word(&test, &word, NULL, EXTRACT_UNQUOTE);
if (r == 0) {
assert_se(!trailing);
break;
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
- test_string_erase();
test_free_and_strndup();
test_ascii_strcasecmp_n();
test_ascii_strcasecmp_nn();
char **t;
int r;
- r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_QUOTES);
+ r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
assert_se(r == (int) strv_length(list));
assert_se(s);
j = strv_join(s, " | ");
char **s = NULL;
int r;
- r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_QUOTES);
+ r = strv_split_extract(&s, quoted, WHITESPACE, EXTRACT_UNQUOTE);
assert_se(s == NULL);
assert_se(r == -EINVAL);
}
static void test_strv_split(void) {
- _cleanup_strv_free_ char **l = NULL;
+ _cleanup_(strv_free_erasep) char **l = NULL;
const char str[] = "one,two,three";
l = strv_split(str, ",");
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_multiple));
- strv_free(l);
+ strv_free_erase(l);
l = strv_split(" one two\t three", WHITESPACE);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_multiple));
- strv_free(l);
+ strv_free_erase(l);
/* Setting NULL for separator is equivalent to WHITESPACE */
l = strv_split(" one two\t three", NULL);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_multiple));
- strv_free(l);
+ strv_free_erase(l);
l = strv_split_full(" one two\t three", NULL, 0);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_multiple));
- strv_free(l);
+ strv_free_erase(l);
l = strv_split_full(" 'one' \" two\t three \" ' four five'", NULL, SPLIT_QUOTES);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted));
- strv_free(l);
+ strv_free_erase(l);
/* missing last quote ignores the last element. */
l = strv_split_full(" 'one' \" two\t three \" ' four five' ' ignored element ", NULL, SPLIT_QUOTES);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted));
- strv_free(l);
+ strv_free_erase(l);
/* missing last quote, but the last element is _not_ ignored with SPLIT_RELAX. */
l = strv_split_full(" 'one' \" two\t three \" ' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted));
- strv_free(l);
+ strv_free_erase(l);
/* missing separator between */
l = strv_split_full(" 'one' \" two\t three \"' four five'", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
assert_se(strv_equal(l, (char**) input_table_quoted));
- strv_free(l);
+ strv_free_erase(l);
l = strv_split_full(" 'one' \" two\t three \"' four five", NULL, SPLIT_QUOTES | SPLIT_RELAX);
assert_se(l);
/* SPDX-License-Identifier: LGPL-2.1+ */
+#include <stdio.h>
#include <string.h>
#include "string-util.h"
assert_se(space_left == 10);
}
+static void test_sd_event_code_migration(void) {
+ char b[100 * DECIMAL_STR_MAX(unsigned) + 1];
+ char c[100 * DECIMAL_STR_MAX(unsigned) + 1], *p;
+ unsigned i;
+ size_t l;
+ int o;
+
+ for (i = o = 0; i < 100; i++)
+ o += snprintf(&b[o], sizeof(b) - o, "%u ", i);
+
+ p = c;
+ l = sizeof(c);
+ for (i = 0; i < 100; i++)
+ l = strpcpyf(&p, l, "%u ", i);
+
+ assert_se(streq(b, c));
+}
+
int main(int argc, char *argv[]) {
test_strpcpy();
test_strpcpyf();
test_strscpy();
test_strscpyl();
+ test_sd_event_code_migration();
+
return 0;
}
return !!set_get(unix_sockets, (char*) fn);
}
-static int dir_is_mount_point(DIR *d, const char *subdir) {
-
- int mount_id_parent, mount_id;
- int r_p, r;
-
- r_p = name_to_handle_at_loop(dirfd(d), ".", NULL, &mount_id_parent, 0);
- if (r_p < 0)
- r_p = -errno;
-
- r = name_to_handle_at_loop(dirfd(d), subdir, NULL, &mount_id, 0);
- if (r < 0)
- r = -errno;
-
- /* got no handle; make no assumptions, return error */
- if (r_p < 0 && r < 0)
- return r_p;
-
- /* got both handles; if they differ, it is a mount point */
- if (r_p >= 0 && r >= 0)
- return mount_id_parent != mount_id;
-
- /* got only one handle; assume different mount points if one
- * of both queries was not supported by the filesystem */
- if (IN_SET(r_p, -ENOSYS, -EOPNOTSUPP) || IN_SET(r, -ENOSYS, -EOPNOTSUPP))
- return true;
-
- /* return error */
- if (r_p < 0)
- return r_p;
- return r;
-}
-
static DIR* xopendirat_nomod(int dirfd, const char *path) {
DIR *dir;
/* Try to detect bind mounts of the same filesystem instance; they
* do not differ in device major/minors. This type of query is not
* supported on all kernels or filesystem types though. */
- if (S_ISDIR(s.st_mode) && dir_is_mount_point(d, dent->d_name) > 0) {
- log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.",
- p, dent->d_name);
- continue;
+ if (S_ISDIR(s.st_mode)) {
+ int q;
+
+ q = fd_is_mount_point(dirfd(d), dent->d_name, 0);
+ if (q < 0)
+ log_debug_errno(q, "Failed to determine whether \"%s/%s\" is a mount point, ignoring: %m", p, dent->d_name);
+ else if (q > 0) {
+ log_debug("Ignoring \"%s/%s\": different mount of the same filesystem.", p, dent->d_name);
+ continue;
+ }
}
- sub_path = strjoin(p, "/", dent->d_name);
+ sub_path = path_join(p, dent->d_name);
if (!sub_path) {
r = log_oom();
goto finish;
continue;
}
- if (mountpoint && S_ISREG(s.st_mode))
- if (s.st_uid == 0 && STR_IN_SET(dent->d_name,
- ".journal",
- "aquota.user",
- "aquota.group")) {
- log_debug("Skipping \"%s\".", sub_path);
- continue;
- }
+ if (mountpoint &&
+ S_ISREG(s.st_mode) &&
+ s.st_uid == 0 &&
+ STR_IN_SET(dent->d_name,
+ ".journal",
+ "aquota.user",
+ "aquota.group")) {
+ log_debug("Skipping \"%s\".", sub_path);
+ continue;
+ }
/* Ignore sockets that are listed in /proc/net/unix */
if (S_ISSOCK(s.st_mode) && unix_socket_alive(sub_path)) {
for (;;) {
_cleanup_free_ char *name = NULL, *value = NULL, *xattr = NULL;
- r = extract_first_word(&p, &xattr, NULL, EXTRACT_QUOTES|EXTRACT_CUNESCAPE);
+ r = extract_first_word(&p, &xattr, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE);
if (r < 0)
log_warning_errno(r, "Failed to parse extended attribute '%s', ignoring: %m", p);
if (r <= 0)
if (isempty(k)) /* Don't complain about other paths than /var/run, and not about /var/run itself either. */
return 0;
- n = strjoin("/run/", k);
+ n = path_join("/run", k);
if (!n)
return log_oom();
/* Also log about this briefly. We do so at LOG_NOTICE level, as we fixed up the situation automatically, hence
* there's no immediate need for action by the user. However, in the interest of making things less confusing
* to the user, let's still inform the user that these snippets should really be updated. */
-
- log_notice("[%s:%u] Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.", fname, line, *path, n);
+ log_syntax(NULL, LOG_NOTICE, fname, line, 0, "Line references path below legacy directory /var/run/, updating %s → %s; please update the tmpfiles.d/ drop-in file accordingly.", *path, n);
free_and_replace(*path, n);
r = extract_many_words(
&buffer,
NULL,
- EXTRACT_QUOTES,
+ EXTRACT_UNQUOTE,
&action,
&path,
&mode,
if (arg_root) {
char *p;
- p = prefix_root(arg_root, i.path);
+ p = path_join(arg_root, i.path);
if (!p)
return log_oom();
free_and_replace(i.path, p);
}
static int send_passwords(const char *socket_name, char **passwords) {
- _cleanup_free_ char *packet = NULL;
+ _cleanup_(erase_and_freep) char *packet = NULL;
_cleanup_close_ int socket_fd = -1;
union sockaddr_union sa = {};
size_t packet_length = 1;
char **p, *d;
ssize_t n;
- int r, salen;
+ int salen;
assert(socket_name);
d = stpcpy(d, *p) + 1;
socket_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
- if (socket_fd < 0) {
- r = log_debug_errno(errno, "socket(): %m");
- goto finish;
- }
+ if (socket_fd < 0)
+ return log_debug_errno(errno, "socket(): %m");
n = sendto(socket_fd, packet, packet_length, MSG_NOSIGNAL, &sa.sa, salen);
- if (n < 0) {
- r = log_debug_errno(errno, "sendto(): %m");
- goto finish;
- }
+ if (n < 0)
+ return log_debug_errno(errno, "sendto(): %m");
- r = (int) n;
-
-finish:
- explicit_bzero_safe(packet, packet_length);
- return r;
+ return (int) n;
}
static int parse_password(const char *filename, char **wall) {
udev-builtin-usb_id.c
net/link-config.c
net/link-config.h
- net/ethtool-util.c
- net/ethtool-util.h
net/naming-scheme.c
net/naming-scheme.h
'''.split()
%includes
%%
Match.MACAddress, config_parse_hwaddrs, 0, offsetof(link_config, match_mac)
-Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name)
-Match.Path, config_parse_strv, 0, offsetof(link_config, match_path)
-Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver)
-Match.Type, config_parse_strv, 0, offsetof(link_config, match_type)
+Match.OriginalName, config_parse_match_ifnames, 0, offsetof(link_config, match_name)
+Match.Path, config_parse_match_strv, 0, offsetof(link_config, match_path)
+Match.Driver, config_parse_match_strv, 0, offsetof(link_config, match_driver)
+Match.Type, config_parse_match_strv, 0, offsetof(link_config, match_type)
+Match.Property, config_parse_match_property, 0, offsetof(link_config, match_property)
Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, conditions)
Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, conditions)
Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, conditions)
Link.UDPSegmentationOffload, config_parse_warn_compat, DISABLED_LEGACY, 0
Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GRO])
Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_LRO])
-Link.RxChannels, config_parse_channel, 0, 0
-Link.TxChannels, config_parse_channel, 0, 0
-Link.OtherChannels, config_parse_channel, 0, 0
-Link.CombinedChannels, config_parse_channel, 0, 0
-Link.Advertise, config_parse_advertise, 0, 0
+Link.RxChannels, config_parse_channel, 0, offsetof(link_config, channels)
+Link.TxChannels, config_parse_channel, 0, offsetof(link_config, channels)
+Link.OtherChannels, config_parse_channel, 0, offsetof(link_config, channels)
+Link.CombinedChannels, config_parse_channel, 0, offsetof(link_config, channels)
+Link.Advertise, config_parse_advertise, 0, offsetof(link_config, advertise)
strv_free(link->match_driver);
strv_free(link->match_type);
strv_free(link->match_name);
+ strv_free(link->match_property);
condition_free_list(link->conditions);
free(link->description);
if (set_isempty(link->match_mac) && strv_isempty(link->match_path) &&
strv_isempty(link->match_driver) && strv_isempty(link->match_type) &&
- strv_isempty(link->match_name) && !link->conditions)
+ strv_isempty(link->match_name) && strv_isempty(link->match_property) && !link->conditions)
log_warning("%s: No valid settings found in the [Match] section. "
"The file will match all interfaces. "
"If that is intended, please add OriginalName=* in the [Match] section.",
assert(ret);
LIST_FOREACH(links, link, ctx->links) {
- const char *address = NULL, *id_path = NULL, *id_net_driver = NULL, *devtype = NULL, *sysname = NULL;
-
- (void) sd_device_get_sysattr_value(device, "address", &address);
- (void) sd_device_get_property_value(device, "ID_PATH", &id_path);
- (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &id_net_driver);
- (void) sd_device_get_devtype(device, &devtype);
- (void) sd_device_get_sysname(device, &sysname);
-
if (net_match_config(link->match_mac, link->match_path, link->match_driver,
- link->match_type, link->match_name,
- address ? ether_aton(address) : NULL,
- id_path,
- id_net_driver,
- devtype,
- sysname)) {
+ link->match_type, link->match_name, link->match_property,
+ device, NULL, NULL)) {
if (link->match_name) {
unsigned name_assign_type = NET_NAME_UNKNOWN;
(void) link_unsigned_attribute(device, "name_assign_type", &name_assign_type);
if (name_assign_type == NET_NAME_ENUM && !strv_contains(link->match_name, "*")) {
- log_warning("Config file %s applies to device based on potentially unpredictable interface name '%s'",
- link->filename, sysname);
+ log_device_warning(device, "Config file %s applies to device based on potentially unpredictable interface name",
+ link->filename);
*ret = link;
return 0;
} else if (name_assign_type == NET_NAME_RENAMED) {
- log_warning("Config file %s matches device based on renamed interface name '%s', ignoring",
- link->filename, sysname);
+ log_device_warning(device, "Config file %s matches device based on renamed interface name, ignoring",
+ link->filename);
continue;
}
}
- log_debug("Config file %s applies to device %s",
- link->filename, sysname);
+ log_device_debug(device, "Config file %s is applied", link->filename);
*ret = link;
return 0;
} else {
uint64_t result;
- r = net_get_unique_predictable_data(device, &result);
+ r = net_get_unique_predictable_data(device,
+ naming_scheme_has(NAMING_STABLE_VIRTUAL_MACS),
+ &result);
if (r < 0)
return log_device_warning_errno(device, r, "Could not generate persistent MAC: %m");
+ log_device_debug(device, "Using generated persistent MAC address");
assert_cc(ETH_ALEN <= sizeof(result));
memcpy(mac->ether_addr_octet, &result, ETH_ALEN);
}
if (r < 0)
return r;
- r = ethtool_set_glinksettings(&ctx->ethtool_fd, old_name, config);
+ 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)
char **match_driver;
char **match_type;
char **match_name;
+ char **match_property;
LIST_HEAD(Condition, conditions);
char *description;
size_t speed;
Duplex duplex;
int autonegotiation;
- uint32_t advertise[2];
+ uint32_t advertise[N_ADVERTISE];
WakeOnLan wol;
NetDevPort port;
int features[_NET_DEV_FEAT_MAX];
{ "v238", NAMING_V238 },
{ "v239", NAMING_V239 },
{ "v240", NAMING_V240 },
+ { "v241", NAMING_V241 },
{ "v243", NAMING_V243 },
/* … add more schemes here, as the logic to name devices is updated … */
};
* OS versions, but not fully stabilize them. */
typedef enum NamingSchemeFlags {
/* First, the individual features */
- NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a40008b8299529c978ed8e11de8f6*/
- NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6eab35d1cb9fa73889892702c27be09 */
- NAMING_INFINIBAND = 1 << 2, /* Use "ib" prefix for infiniband, see 938d30aa98df887797c9e05074a562ddacdcdf5e */
- NAMING_ZERO_ACPI_INDEX = 1 << 3, /* Use zero acpi_index field, see d81186ef4f6a888a70f20a1e73a812d6acb9e22f */
- NAMING_ALLOW_RERENAMES = 1 << 4, /* Allow re-renaming of devices, see #9006 */
- NAMING_NETDEVSIM = 1 << 5, /* Generate names for netdevsim devices, see eaa9d507d85509c8bf727356e3884ec54b0fc646 */
- NAMING_LABEL_NOPREFIX = 1 << 6, /* Don't prepend ID_NET_LABEL_ONBOARD with interface type prefix */
+ NAMING_SR_IOV_V = 1 << 0, /* Use "v" suffix for SR-IOV, see 609948c7043a */
+ NAMING_NPAR_ARI = 1 << 1, /* Use NPAR "ARI", see 6bc04997b6ea */
+ NAMING_INFINIBAND = 1 << 2, /* Use "ib" prefix for infiniband, see 938d30aa98df */
+ NAMING_ZERO_ACPI_INDEX = 1 << 3, /* Use zero acpi_index field, see d81186ef4f6a */
+ NAMING_ALLOW_RERENAMES = 1 << 4, /* Allow re-renaming of devices, see #9006 */
+ NAMING_STABLE_VIRTUAL_MACS = 1 << 5, /* Use device name to generate MAC, see 6d3646406560 */
+ NAMING_NETDEVSIM = 1 << 6, /* Generate names for netdevsim devices, see eaa9d507d855 */
+ NAMING_LABEL_NOPREFIX = 1 << 7, /* Don't prepend ID_NET_LABEL_ONBOARD with interface type prefix */
/* And now the masks that combine the features above */
NAMING_V238 = 0,
NAMING_V239 = NAMING_V238 | NAMING_SR_IOV_V | NAMING_NPAR_ARI,
NAMING_V240 = NAMING_V239 | NAMING_INFINIBAND | NAMING_ZERO_ACPI_INDEX | NAMING_ALLOW_RERENAMES,
- NAMING_V243 = NAMING_V240 | NAMING_NETDEVSIM | NAMING_LABEL_NOPREFIX,
+ NAMING_V241 = NAMING_V240 | NAMING_STABLE_VIRTUAL_MACS,
+ NAMING_V243 = NAMING_V241 | NAMING_NETDEVSIM | NAMING_LABEL_NOPREFIX,
_NAMING_SCHEME_FLAGS_INVALID = -1,
} NamingSchemeFlags;
if (!startswith(dent->d_name, "virtfn"))
continue;
- virtfn_link_file = strjoin(physfn_pci_syspath, "/", dent->d_name);
+ virtfn_link_file = path_join(physfn_pci_syspath, dent->d_name);
if (!virtfn_link_file)
return -ENOMEM;
if (r < 0)
return r;
- (void) sd_event_source_set_description(uctrl->event_source, "udev-ctrl-wait-io");
+ (void) sd_event_source_set_description(source_io, "udev-ctrl-wait-io");
if (timeout != USEC_INFINITY) {
usec_t usec;
assert(*str);
assert(ret_type);
- if (p[0] == '$') {
+ if (*p == '$') {
p++;
- if (p[0] == '$') {
+ if (*p == '$') {
*str = p;
return 0;
}
for (i = 0; i < ELEMENTSOF(map); i++)
if ((q = startswith(p, map[i].name)))
break;
- } else if (p[0] == '%') {
+ } else if (*p == '%') {
p++;
- if (p[0] == '%') {
+ if (*p == '%') {
*str = p;
return 0;
}
for (i = 0; i < ELEMENTSOF(map); i++)
- if (p[0] == map[i].fmt) {
+ if (*p == map[i].fmt) {
q = p + 1;
break;
}
/* When 'strict' flag is set, then '$' and '%' must be escaped. */
return strict ? -EINVAL : 0;
- if (q[0] == '{') {
+ if (*q == '{') {
const char *start, *end;
size_t len;
strnscpy(ret_attr, UTIL_PATH_SIZE, start, len);
q = end + 1;
} else
- ret_attr[0] = '\0';
+ *ret_attr = '\0';
*str = q;
*ret_type = map[i].type;
p = skip_leading_chars(event->program_result, NULL);
for (i = 1; i < index; i++) {
- while (p[0] != '\0' && !strchr(WHITESPACE, p[0]))
+ while (*p && !strchr(WHITESPACE, *p))
p++;
p = skip_leading_chars(p, NULL);
if (*p == '\0')
if (has_plus)
l = strpcpy(&s, l, start);
else {
- while (p[0] != '\0' && !strchr(WHITESPACE, p[0]))
+ while (*p && !strchr(WHITESPACE, *p))
p++;
l = strnpcpy(&s, l, start, p - start);
}
assert(dest);
assert(size > 0);
- while (s[0] != '\0') {
+ while (*s) {
FormatSubstitutionType type;
char attr[UTIL_PATH_SIZE];
ssize_t subst_len;
}
assert(size >= 1);
- dest[0] = '\0';
+ *dest = '\0';
return size;
}
-int udev_check_format(const char *s) {
+int udev_check_format(const char *value, size_t *offset, const char **hint) {
FormatSubstitutionType type;
+ const char *s = value;
char attr[UTIL_PATH_SIZE];
int r;
- while (s[0] != '\0') {
+ while (*s) {
r = get_subst_type(&s, true, &type, attr);
- if (r < 0)
+ if (r < 0) {
+ if (offset)
+ *offset = s - value;
+ if (hint)
+ *hint = "invalid substitution type";
return r;
- if (r == 0) {
+ } else if (r == 0) {
s++;
continue;
}
- if (IN_SET(type, FORMAT_SUBST_ATTR, FORMAT_SUBST_ENV) && isempty(attr))
+ if (IN_SET(type, FORMAT_SUBST_ATTR, FORMAT_SUBST_ENV) && isempty(attr)) {
+ if (offset)
+ *offset = s - value;
+ if (hint)
+ *hint = "attribute value missing";
return -EINVAL;
+ }
if (type == FORMAT_SUBST_RESULT && !isempty(attr)) {
unsigned i;
r = safe_atou_optional_plus(attr, &i);
- if (r < 0)
+ if (r < 0) {
+ if (offset)
+ *offset = s - value;
+ if (hint)
+ *hint = "attribute value not a valid number";
return r;
+ }
}
}
static int update_devnode(UdevEvent *event) {
sd_device *dev = event->dev;
- bool apply;
int r;
r = sd_device_get_devnum(dev, NULL);
if (!uid_is_valid(event->uid)) {
r = device_get_devnode_uid(dev, &event->uid);
- if (r == -ENOENT)
- event->uid = 0;
- else if (r < 0)
+ if (r < 0 && r != -ENOENT)
return log_device_error_errno(dev, r, "Failed to get devnode UID: %m");
}
if (!gid_is_valid(event->gid)) {
r = device_get_devnode_gid(dev, &event->gid);
- if (r == -ENOENT)
- event->gid = 0;
- else if (r < 0)
+ if (r < 0 && r != -ENOENT)
return log_device_error_errno(dev, r, "Failed to get devnode GID: %m");
}
r = device_get_devnode_mode(dev, &event->mode);
if (r < 0 && r != -ENOENT)
return log_device_error_errno(dev, r, "Failed to get devnode mode: %m");
- if (r == -ENOENT) {
- if (event->gid > 0)
- /* default 0660 if a group is assigned */
- event->mode = 0660;
- else
- /* default 0600 */
- event->mode = 0600;
- }
}
+ if (event->mode == MODE_INVALID && gid_is_valid(event->gid) && event->gid > 0)
+ /* If group is set, but mode is not set, "upgrade" mode for the group. */
+ event->mode = 0660;
+
+ bool apply_mac = device_for_action(dev, DEVICE_ACTION_ADD);
- apply = device_for_action(dev, DEVICE_ACTION_ADD) ||
- uid_is_valid(event->uid) ||
- gid_is_valid(event->gid) ||
- event->mode != MODE_INVALID;
- return udev_node_add(dev, apply, event->mode, event->uid, event->gid, event->seclabel_list);
+ return udev_node_add(dev, apply_mac, event->mode, event->uid, event->gid, event->seclabel_list);
}
static void event_execute_rules_on_remove(
ssize_t udev_event_apply_format(UdevEvent *event,
const char *src, char *dest, size_t size,
bool replace_whitespace);
-int udev_check_format(const char *s);
+int udev_check_format(const char *value, size_t *offset, const char **hint);
int udev_event_spawn(UdevEvent *event,
usec_t timeout_usec,
bool accept_failure,
#include "string-util.h"
#include "strxcpyx.h"
#include "udev-node.h"
+#include "user-util.h"
static int node_symlink(sd_device *dev, const char *node, const char *slink) {
_cleanup_free_ char *slink_dirname = NULL, *target = NULL;
return 0;
}
-static int node_permissions_apply(sd_device *dev, bool apply,
+static int node_permissions_apply(sd_device *dev, bool apply_mac,
mode_t mode, uid_t uid, gid_t gid,
OrderedHashmap *seclabel_list) {
const char *devnode, *subsystem, *id_filename = NULL;
struct stat stats;
dev_t devnum;
- int r = 0;
+ bool apply_mode, apply_uid, apply_gid;
+ int r;
assert(dev);
mode |= S_IFCHR;
if (lstat(devnode, &stats) < 0)
- return log_device_debug_errno(dev, errno, "cannot stat() node '%s' (%m)", devnode);
+ return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
- if (((stats.st_mode & S_IFMT) != (mode & S_IFMT)) || (stats.st_rdev != devnum))
- return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST), "Found node '%s' with non-matching devnum %s, skip handling",
+ if ((mode != MODE_INVALID && (stats.st_mode & S_IFMT) != (mode & S_IFMT)) || stats.st_rdev != devnum)
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EEXIST),
+ "Found node '%s' with non-matching devnum %s, skip handling",
devnode, id_filename);
- if (apply) {
+ apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
+ apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
+ apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
+
+ if (apply_mode || apply_uid || apply_gid || apply_mac) {
bool selinux = false, smack = false;
const char *name, *label;
Iterator i;
- if ((stats.st_mode & 0777) != (mode & 0777) || stats.st_uid != uid || stats.st_gid != gid) {
+ if (apply_mode || apply_uid || apply_gid) {
log_device_debug(dev, "Setting permissions %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid);
r = chmod_and_chown(devnode, mode, uid, gid);
if (r < 0)
- log_device_warning_errno(dev, r, "Failed to set owner/mode of %s to uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o: %m", devnode, uid, gid, mode);
+ log_device_warning_errno(dev, r, "Failed to set owner/mode of %s to uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o: %m",
+ devnode, uid, gid, mode);
} else
log_device_debug(dev, "Preserve permissions of %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid);
#include "fs-util.h"
#include "glob-util.h"
#include "libudev-util.h"
+#include "list.h"
#include "mkdir.h"
#include "nulstr-util.h"
#include "parse-util.h"
#define RULES_DIRS (const char* const*) CONF_PATHS_STRV("udev/rules.d")
+
+typedef enum {
+ OP_MATCH, /* == */
+ OP_NOMATCH, /* != */
+ OP_ADD, /* += */
+ OP_REMOVE, /* -= */
+ OP_ASSIGN, /* = */
+ OP_ASSIGN_FINAL, /* := */
+ _OP_TYPE_MAX,
+ _OP_TYPE_INVALID = -1
+} UdevRuleOperatorType;
+
+typedef enum {
+ MATCH_TYPE_EMPTY, /* empty string */
+ MATCH_TYPE_PLAIN, /* no special characters */
+ MATCH_TYPE_GLOB, /* shell globs ?,*,[] */
+ MATCH_TYPE_SUBSYSTEM, /* "subsystem", "bus", or "class" */
+ _MATCH_TYPE_MAX,
+ _MATCH_TYPE_INVALID = -1
+} UdevRuleMatchType;
+
+typedef enum {
+ SUBST_TYPE_PLAIN, /* no substitution */
+ SUBST_TYPE_FORMAT, /* % or $ */
+ SUBST_TYPE_SUBSYS, /* "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
+ _SUBST_TYPE_MAX,
+ _SUBST_TYPE_INVALID = -1
+} UdevRuleSubstituteType;
+
+typedef enum {
+ /* lvalues which take match or nomatch operator */
+ TK_M_ACTION, /* string, device_get_action() */
+ TK_M_DEVPATH, /* path, sd_device_get_devpath() */
+ TK_M_KERNEL, /* string, sd_device_get_sysname() */
+ TK_M_DEVLINK, /* strv, sd_device_get_devlink_first(), sd_device_get_devlink_next() */
+ TK_M_NAME, /* string, name of network interface */
+ TK_M_ENV, /* string, device property, takes key through attribute */
+ TK_M_TAG, /* strv, sd_device_get_tag_first(), sd_device_get_tag_next() */
+ TK_M_SUBSYSTEM, /* string, sd_device_get_subsystem() */
+ TK_M_DRIVER, /* string, sd_device_get_driver() */
+ TK_M_ATTR, /* string, takes filename through attribute, sd_device_get_sysattr_value(), util_resolve_subsys_kernel(), etc. */
+ TK_M_SYSCTL, /* string, takes kernel parameter through attribute */
+
+ /* matches parent paramters */
+ TK_M_PARENTS_KERNEL, /* string */
+ TK_M_PARENTS_SUBSYSTEM, /* string */
+ TK_M_PARENTS_DRIVER, /* string */
+ TK_M_PARENTS_ATTR, /* string */
+ TK_M_PARENTS_TAG, /* strv */
+
+ TK_M_TEST, /* path, optionally mode_t can be specified by attribute, test the existence of a file */
+ TK_M_PROGRAM, /* string, execute a program */
+ TK_M_IMPORT_FILE, /* path */
+ TK_M_IMPORT_PROGRAM, /* string, import properties from the result of program */
+ TK_M_IMPORT_BUILTIN, /* string, import properties from the result of built-in command */
+ TK_M_IMPORT_DB, /* string, import properties from database */
+ TK_M_IMPORT_CMDLINE, /* string, kernel command line */
+ TK_M_IMPORT_PARENT, /* string, parent property */
+ TK_M_RESULT, /* string, result of TK_M_PROGRAM */
+
+#define _TK_M_MAX (TK_M_RESULT + 1)
+#define _TK_A_MIN _TK_M_MAX
+
+ /* lvalues which take one of assign operators */
+ TK_A_OPTIONS_STRING_ESCAPE_NONE, /* no argument */
+ TK_A_OPTIONS_STRING_ESCAPE_REPLACE, /* no argument */
+ TK_A_OPTIONS_DB_PERSIST, /* no argument */
+ TK_A_OPTIONS_INOTIFY_WATCH, /* boolean */
+ TK_A_OPTIONS_DEVLINK_PRIORITY, /* int */
+ TK_A_OWNER, /* user name */
+ TK_A_GROUP, /* group name */
+ TK_A_MODE, /* mode string */
+ TK_A_OWNER_ID, /* uid_t */
+ TK_A_GROUP_ID, /* gid_t */
+ TK_A_MODE_ID, /* mode_t */
+ TK_A_TAG, /* string */
+ TK_A_OPTIONS_STATIC_NODE, /* device path, /dev/... */
+ TK_A_SECLABEL, /* string with attribute */
+ TK_A_ENV, /* string with attribute */
+ TK_A_NAME, /* ifname */
+ TK_A_DEVLINK, /* string */
+ TK_A_ATTR, /* string with attribute */
+ TK_A_SYSCTL, /* string with attribute */
+ TK_A_RUN_BUILTIN, /* string */
+ TK_A_RUN_PROGRAM, /* string */
+
+ _TK_TYPE_MAX,
+ _TK_TYPE_INVALID = -1,
+} UdevRuleTokenType;
+
+typedef enum {
+ LINE_HAS_NAME = 1 << 0, /* has NAME= */
+ LINE_HAS_DEVLINK = 1 << 1, /* has SYMLINK=, OWNER=, GROUP= or MODE= */
+ LINE_HAS_STATIC_NODE = 1 << 2, /* has OPTIONS=static_node */
+ LINE_HAS_GOTO = 1 << 3, /* has GOTO= */
+ LINE_HAS_LABEL = 1 << 4, /* has LABEL= */
+ LINE_UPDATE_SOMETHING = 1 << 5, /* has other TK_A_* or TK_M_IMPORT tokens */
+} UdevRuleLineType;
+
+typedef struct UdevRuleFile UdevRuleFile;
+typedef struct UdevRuleLine UdevRuleLine;
+typedef struct UdevRuleToken UdevRuleToken;
+
+struct UdevRuleToken {
+ UdevRuleTokenType type:8;
+ UdevRuleOperatorType op:8;
+ UdevRuleMatchType match_type:8;
+ UdevRuleSubstituteType attr_subst_type:7;
+ bool attr_match_remove_trailing_whitespace:1;
+ const char *value;
+ void *data;
+ LIST_FIELDS(UdevRuleToken, tokens);
+};
+
+struct UdevRuleLine {
+ char *line;
+ unsigned line_number;
+ UdevRuleLineType type;
+
+ const char *label;
+ const char *goto_label;
+ UdevRuleLine *goto_line;
+
+ UdevRuleFile *rule_file;
+ UdevRuleToken *current_token;
+ LIST_HEAD(UdevRuleToken, tokens);
+ LIST_FIELDS(UdevRuleLine, rule_lines);
+};
+
+struct UdevRuleFile {
+ char *filename;
+ UdevRuleLine *current_line;
+ LIST_HEAD(UdevRuleLine, rule_lines);
+ LIST_FIELDS(UdevRuleFile, rule_files);
+};
+
+struct UdevRules {
+ usec_t dirs_ts_usec;
+ ResolveNameTiming resolve_name_timing;
+ Hashmap *known_users;
+ Hashmap *known_groups;
+ UdevRuleFile *current_file;
+ LIST_HEAD(UdevRuleFile, rule_files);
+};
+
+
+/*** Logging helpers ***/
+
+#define log_rule_full(device, rules, level, error, fmt, ...) \
+ ({ \
+ UdevRules *_r = (rules); \
+ UdevRuleFile *_f = _r ? _r->current_file : NULL; \
+ UdevRuleLine *_l = _f ? _f->current_line : NULL; \
+ const char *_n = _f ? _f->filename : NULL; \
+ \
+ log_device_full(device, level, error, "%s:%u " fmt, \
+ strna(_n), _l ? _l->line_number : 0, \
+ ##__VA_ARGS__); \
+ })
+
+#define log_rule_debug(device, rules, ...) log_rule_full(device, rules, LOG_DEBUG, 0, ##__VA_ARGS__)
+#define log_rule_info(device, rules, ...) log_rule_full(device, rules, LOG_INFO, 0, ##__VA_ARGS__)
+#define log_rule_notice(device, rules, ...) log_rule_full(device, rules, LOG_NOTICE, 0, ##__VA_ARGS__)
+#define log_rule_warning(device, rules, ...) log_rule_full(device, rules, LOG_WARNING, 0, ##__VA_ARGS__)
+#define log_rule_error(device, rules, ...) log_rule_full(device, rules, LOG_ERR, 0, ##__VA_ARGS__)
+
+#define log_rule_debug_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_DEBUG, error, ##__VA_ARGS__)
+#define log_rule_info_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_INFO, error, ##__VA_ARGS__)
+#define log_rule_notice_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_NOTICE, error, ##__VA_ARGS__)
+#define log_rule_warning_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_WARNING, error, ##__VA_ARGS__)
+#define log_rule_error_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_ERR, error, ##__VA_ARGS__)
+
+#define log_token_full(rules, ...) log_rule_full(NULL, rules, ##__VA_ARGS__)
+
+#define log_token_debug(rules, ...) log_token_full(rules, LOG_DEBUG, 0, ##__VA_ARGS__)
+#define log_token_info(rules, ...) log_token_full(rules, LOG_INFO, 0, ##__VA_ARGS__)
+#define log_token_notice(rules, ...) log_token_full(rules, LOG_NOTICE, 0, ##__VA_ARGS__)
+#define log_token_warning(rules, ...) log_token_full(rules, LOG_WARNING, 0, ##__VA_ARGS__)
+#define log_token_error(rules, ...) log_token_full(rules, LOG_ERR, 0, ##__VA_ARGS__)
+
+#define log_token_debug_errno(rules, error, ...) log_token_full(rules, LOG_DEBUG, error, ##__VA_ARGS__)
+#define log_token_info_errno(rules, error, ...) log_token_full(rules, LOG_INFO, error, ##__VA_ARGS__)
+#define log_token_notice_errno(rules, error, ...) log_token_full(rules, LOG_NOTICE, error, ##__VA_ARGS__)
+#define log_token_warning_errno(rules, error, ...) log_token_full(rules, LOG_WARNING, error, ##__VA_ARGS__)
+#define log_token_error_errno(rules, error, ...) log_token_full(rules, LOG_ERR, error, ##__VA_ARGS__)
+
+#define _log_token_invalid(rules, key, type) \
+ log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL), \
+ "Invalid %s for %s.", type, key)
+
+#define log_token_invalid_op(rules, key) _log_token_invalid(rules, key, "operator")
+#define log_token_invalid_attr(rules, key) _log_token_invalid(rules, key, "attribute")
+
+#define log_token_invalid_attr_format(rules, key, attr, offset, hint) \
+ log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL), \
+ "Invalid attribute \"%s\" for %s (char %zu: %s), ignoring, but please fix it.", \
+ attr, key, offset, hint)
+#define log_token_invalid_value(rules, key, value, offset, hint) \
+ log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL), \
+ "Invalid value \"%s\" for %s (char %zu: %s), ignoring, but please fix it.", \
+ value, key, offset, hint)
+
+static void log_unknown_owner(sd_device *dev, UdevRules *rules, int error, const char *entity, const char *name) {
+ if (IN_SET(abs(error), ENOENT, ESRCH))
+ log_rule_error(dev, rules, "Unknown %s '%s', ignoring", entity, name);
+ else
+ log_rule_error_errno(dev, rules, error, "Failed to resolve %s '%s', ignoring: %m", entity, name);
+}
+
+/*** Other functions ***/
+
static void udev_rule_token_free(UdevRuleToken *token) {
free(token);
}
return mfree(rules);
}
-static void log_unknown_owner(sd_device *dev, UdevRules *rules, int error, const char *entity, const char *name) {
- if (IN_SET(abs(error), ENOENT, ESRCH))
- log_rule_error(dev, rules, "Unknown %s '%s', ignoring", entity, name);
- else
- log_rule_error_errno(dev, rules, error, "Failed to resolve %s '%s', ignoring: %m", entity, name);
-}
-
static int rule_resolve_user(UdevRules *rules, const char *name, uid_t *ret) {
_cleanup_free_ char *n = NULL;
uid_t uid;
SET_FLAG(rule_line->type, LINE_HAS_DEVLINK, true);
else if (token->type >= _TK_A_MIN ||
- IN_SET(token->type,
+ IN_SET(token->type, TK_M_PROGRAM,
TK_M_IMPORT_FILE, TK_M_IMPORT_PROGRAM, TK_M_IMPORT_BUILTIN,
TK_M_IMPORT_DB, TK_M_IMPORT_CMDLINE, TK_M_IMPORT_PARENT))
SET_FLAG(rule_line->type, LINE_UPDATE_SOMETHING, true);
return 0;
}
+static void check_value_format_and_warn(UdevRules *rules, const char *key, const char *value, bool nonempty) {
+ size_t offset;
+ const char *hint;
+
+ if (nonempty && isempty(value))
+ log_token_invalid_value(rules, key, value, (size_t) 0, "empty value");
+ else if (udev_check_format(value, &offset, &hint) < 0)
+ log_token_invalid_value(rules, key, value, offset + 1, hint);
+}
+
+static int check_attr_format_and_warn(UdevRules *rules, const char *key, const char *value) {
+ size_t offset;
+ const char *hint;
+
+ if (isempty(value))
+ return log_token_invalid_attr(rules, key);
+ if (udev_check_format(value, &offset, &hint) < 0)
+ log_token_invalid_attr_format(rules, key, value, offset + 1, hint);
+ return 0;
+}
+
static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOperatorType op, char *value) {
bool is_match = IN_SET(op, OP_MATCH, OP_NOMATCH);
UdevRuleLine *rule_line;
return log_token_invalid_op(rules, key);
if (!is_match) {
- if (udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
-
+ check_value_format_and_warn(rules, key, value, false);
r = rule_line_add_token(rule_line, TK_A_DEVLINK, op, value, NULL);
} else
r = rule_line_add_token(rule_line, TK_M_DEVLINK, op, value, NULL);
if (isempty(value))
return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),
"Ignoring NAME=\"\", as udev will not delete any device nodes.");
- if (udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
+ check_value_format_and_warn(rules, key, value, false);
r = rule_line_add_token(rule_line, TK_A_NAME, op, value, NULL);
} else
return log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL),
"Invalid ENV attribute. '%s' cannot be set.", attr);
- if (udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
+ check_value_format_and_warn(rules, key, value, false);
+
r = rule_line_add_token(rule_line, TK_A_ENV, op, value, attr);
} else
r = rule_line_add_token(rule_line, TK_M_ENV, op, value, attr);
}
if (!is_match) {
- if (isempty(value) || udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
+ check_value_format_and_warn(rules, key, value, true);
+
r = rule_line_add_token(rule_line, TK_A_TAG, op, value, NULL);
} else
r = rule_line_add_token(rule_line, TK_M_TAG, op, value, NULL);
r = rule_line_add_token(rule_line, TK_M_DRIVER, op, value, NULL);
} else if (streq(key, "ATTR")) {
- if (isempty(attr))
- return log_token_invalid_attr(rules, key);
- if (udev_check_format(attr) < 0)
- log_token_invalid_attr_format(rules, key, attr);
+ r = check_attr_format_and_warn(rules, key, attr);
+ if (r < 0)
+ return r;
if (op == OP_REMOVE)
return log_token_invalid_op(rules, key);
if (IN_SET(op, OP_ADD, OP_ASSIGN_FINAL)) {
}
if (!is_match) {
- if (udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
-
+ check_value_format_and_warn(rules, key, value, false);
r = rule_line_add_token(rule_line, TK_A_ATTR, op, value, attr);
} else
r = rule_line_add_token(rule_line, TK_M_ATTR, op, value, attr);
} else if (streq(key, "SYSCTL")) {
- if (isempty(attr))
- return log_token_invalid_attr(rules, key);
- if (udev_check_format(attr) < 0)
- log_token_invalid_attr_format(rules, key, attr);
+ r = check_attr_format_and_warn(rules, key, attr);
+ if (r < 0)
+ return r;
if (op == OP_REMOVE)
return log_token_invalid_op(rules, key);
if (IN_SET(op, OP_ADD, OP_ASSIGN_FINAL)) {
}
if (!is_match) {
- if (udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
-
+ check_value_format_and_warn(rules, key, value, false);
r = rule_line_add_token(rule_line, TK_A_SYSCTL, op, value, attr);
} else
r = rule_line_add_token(rule_line, TK_M_SYSCTL, op, value, attr);
r = rule_line_add_token(rule_line, TK_M_PARENTS_DRIVER, op, value, NULL);
} else if (streq(key, "ATTRS")) {
- if (isempty(attr))
- return log_token_invalid_attr(rules, key);
- if (udev_check_format(attr) < 0)
- log_token_invalid_attr_format(rules, key, attr);
+ r = check_attr_format_and_warn(rules, key, attr);
+ if (r < 0)
+ return r;
if (!is_match)
return log_token_invalid_op(rules, key);
if (r < 0)
return log_token_error_errno(rules, r, "Failed to parse mode '%s': %m", attr);
}
- if (isempty(value) || udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
+ check_value_format_and_warn(rules, key, value, true);
if (!is_match)
return log_token_invalid_op(rules, key);
} else if (streq(key, "PROGRAM")) {
if (attr)
return log_token_invalid_attr(rules, key);
- if (isempty(value) || udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
+ check_value_format_and_warn(rules, key, value, true);
if (op == OP_REMOVE)
return log_token_invalid_op(rules, key);
if (!is_match) {
} else if (streq(key, "IMPORT")) {
if (isempty(attr))
return log_token_invalid_attr(rules, key);
- if (isempty(value) || udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
+ check_value_format_and_warn(rules, key, value, true);
if (op == OP_REMOVE)
return log_token_invalid_op(rules, key);
if (!is_match) {
r = rule_line_add_token(rule_line, TK_A_OWNER_ID, op, NULL, UID_TO_PTR(uid));
} else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER) {
- if (isempty(value) || udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
+ check_value_format_and_warn(rules, key, value, true);
r = rule_line_add_token(rule_line, TK_A_OWNER, op, value, NULL);
} else {
log_token_debug(rules, "Resolving user name is disabled, ignoring %s=%s", key, value);
r = rule_line_add_token(rule_line, TK_A_GROUP_ID, op, NULL, GID_TO_PTR(gid));
} else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER) {
- if (isempty(value) || udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
+ check_value_format_and_warn(rules, key, value, true);
r = rule_line_add_token(rule_line, TK_A_GROUP, op, value, NULL);
} else {
log_token_debug(rules, "Resolving group name is disabled, ignoring %s=%s", key, value);
if (parse_mode(value, &mode) >= 0)
r = rule_line_add_token(rule_line, TK_A_MODE_ID, op, NULL, MODE_TO_PTR(mode));
else {
- if (isempty(value) || udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
+ check_value_format_and_warn(rules, key, value, true);
r = rule_line_add_token(rule_line, TK_A_MODE, op, value, NULL);
}
} else if (streq(key, "SECLABEL")) {
if (isempty(attr))
return log_token_invalid_attr(rules, key);
- if (isempty(value) || udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
+ check_value_format_and_warn(rules, key, value, true);
if (is_match || op == OP_REMOVE)
return log_token_invalid_op(rules, key);
if (op == OP_ASSIGN_FINAL) {
} else if (streq(key, "RUN")) {
if (is_match || op == OP_REMOVE)
return log_token_invalid_op(rules, key);
- if (isempty(value) || udev_check_format(value) < 0)
- log_token_invalid_value(rules, key, value);
+ check_value_format_and_warn(rules, key, value, true);
if (!attr || streq(attr, "program"))
r = rule_line_add_token(rule_line, TK_A_RUN_PROGRAM, op, value, NULL);
else if (streq(attr, "builtin")) {
/* SPDX-License-Identifier: GPL-2.0+ */
#pragma once
-#include "device-util.h"
#include "hashmap.h"
-#include "list.h"
#include "time-util.h"
#include "udev-util.h"
typedef struct UdevRules UdevRules;
-typedef struct UdevRuleFile UdevRuleFile;
-typedef struct UdevRuleLine UdevRuleLine;
-typedef struct UdevRuleToken UdevRuleToken;
typedef struct UdevEvent UdevEvent;
-typedef enum {
- OP_MATCH, /* == */
- OP_NOMATCH, /* != */
- OP_ADD, /* += */
- OP_REMOVE, /* -= */
- OP_ASSIGN, /* = */
- OP_ASSIGN_FINAL, /* := */
- _OP_TYPE_MAX,
- _OP_TYPE_INVALID = -1
-} UdevRuleOperatorType;
-
-typedef enum {
- MATCH_TYPE_EMPTY, /* empty string */
- MATCH_TYPE_PLAIN, /* no special characters */
- MATCH_TYPE_GLOB, /* shell globs ?,*,[] */
- MATCH_TYPE_SUBSYSTEM, /* "subsystem", "bus", or "class" */
- _MATCH_TYPE_MAX,
- _MATCH_TYPE_INVALID = -1
-} UdevRuleMatchType;
-
-typedef enum {
- SUBST_TYPE_PLAIN, /* no substitution */
- SUBST_TYPE_FORMAT, /* % or $ */
- SUBST_TYPE_SUBSYS, /* "[<SUBSYSTEM>/<KERNEL>]<attribute>" format */
- _SUBST_TYPE_MAX,
- _SUBST_TYPE_INVALID = -1
-} UdevRuleSubstituteType;
-
typedef enum {
ESCAPE_UNSET,
ESCAPE_NONE, /* OPTIONS="string_escape=none" */
_ESCAPE_TYPE_INVALID = -1
} UdevRuleEscapeType;
-typedef enum {
- /* lvalues which take match or nomatch operator */
- TK_M_ACTION, /* string, device_get_action() */
- TK_M_DEVPATH, /* path, sd_device_get_devpath() */
- TK_M_KERNEL, /* string, sd_device_get_sysname() */
- TK_M_DEVLINK, /* strv, sd_device_get_devlink_first(), sd_device_get_devlink_next() */
- TK_M_NAME, /* string, name of network interface */
- TK_M_ENV, /* string, device property, takes key through attribute */
- TK_M_TAG, /* strv, sd_device_get_tag_first(), sd_device_get_tag_next() */
- TK_M_SUBSYSTEM, /* string, sd_device_get_subsystem() */
- TK_M_DRIVER, /* string, sd_device_get_driver() */
- TK_M_ATTR, /* string, takes filename through attribute, sd_device_get_sysattr_value(), util_resolve_subsys_kernel(), etc. */
- TK_M_SYSCTL, /* string, takes kernel parameter through attribute */
-
- /* matches parent paramters */
- TK_M_PARENTS_KERNEL, /* string */
- TK_M_PARENTS_SUBSYSTEM, /* string */
- TK_M_PARENTS_DRIVER, /* string */
- TK_M_PARENTS_ATTR, /* string */
- TK_M_PARENTS_TAG, /* strv */
-
- TK_M_TEST, /* path, optionally mode_t can be specified by attribute, test the existence of a file */
- TK_M_PROGRAM, /* string, execute a program */
- TK_M_IMPORT_FILE, /* path */
- TK_M_IMPORT_PROGRAM, /* string, import properties from the result of program */
- TK_M_IMPORT_BUILTIN, /* string, import properties from the result of built-in command */
- TK_M_IMPORT_DB, /* string, import properties from database */
- TK_M_IMPORT_CMDLINE, /* string, kernel command line */
- TK_M_IMPORT_PARENT, /* string, parent property */
- TK_M_RESULT, /* string, result of TK_M_PROGRAM */
-
-#define _TK_M_MAX (TK_M_RESULT + 1)
-#define _TK_A_MIN _TK_M_MAX
-
- /* lvalues which take one of assign operators */
- TK_A_OPTIONS_STRING_ESCAPE_NONE, /* no argument */
- TK_A_OPTIONS_STRING_ESCAPE_REPLACE, /* no argument */
- TK_A_OPTIONS_DB_PERSIST, /* no argument */
- TK_A_OPTIONS_INOTIFY_WATCH, /* boolean */
- TK_A_OPTIONS_DEVLINK_PRIORITY, /* int */
- TK_A_OWNER, /* user name */
- TK_A_GROUP, /* group name */
- TK_A_MODE, /* mode string */
- TK_A_OWNER_ID, /* uid_t */
- TK_A_GROUP_ID, /* gid_t */
- TK_A_MODE_ID, /* mode_t */
- TK_A_TAG, /* string */
- TK_A_OPTIONS_STATIC_NODE, /* device path, /dev/... */
- TK_A_SECLABEL, /* string with attribute */
- TK_A_ENV, /* string with attribute */
- TK_A_NAME, /* ifname */
- TK_A_DEVLINK, /* string */
- TK_A_ATTR, /* string with attribute */
- TK_A_SYSCTL, /* string with attribute */
- TK_A_RUN_BUILTIN, /* string */
- TK_A_RUN_PROGRAM, /* string */
-
- _TK_TYPE_MAX,
- _TK_TYPE_INVALID = -1,
-} UdevRuleTokenType;
-
-typedef enum {
- LINE_HAS_NAME = 1 << 0, /* has NAME= */
- LINE_HAS_DEVLINK = 1 << 1, /* has SYMLINK=, OWNER=, GROUP= or MODE= */
- LINE_HAS_STATIC_NODE = 1 << 2, /* has OPTIONS=static_node */
- LINE_HAS_GOTO = 1 << 3, /* has GOTO= */
- LINE_HAS_LABEL = 1 << 4, /* has LABEL= */
- LINE_UPDATE_SOMETHING = 1 << 5, /* has other TK_A_* or TK_M_IMPORT tokens */
-} UdevRuleLineType;
-
-struct UdevRuleToken {
- UdevRuleTokenType type:8;
- UdevRuleOperatorType op:8;
- UdevRuleMatchType match_type:8;
- UdevRuleSubstituteType attr_subst_type:7;
- bool attr_match_remove_trailing_whitespace:1;
- const char *value;
- void *data;
- LIST_FIELDS(UdevRuleToken, tokens);
-};
-
-struct UdevRuleLine {
- char *line;
- unsigned line_number;
- UdevRuleLineType type;
-
- const char *label;
- const char *goto_label;
- UdevRuleLine *goto_line;
-
- UdevRuleFile *rule_file;
- UdevRuleToken *current_token;
- LIST_HEAD(UdevRuleToken, tokens);
- LIST_FIELDS(UdevRuleLine, rule_lines);
-};
-
-struct UdevRuleFile {
- char *filename;
- UdevRuleLine *current_line;
- LIST_HEAD(UdevRuleLine, rule_lines);
- LIST_FIELDS(UdevRuleFile, rule_files);
-};
-
-struct UdevRules {
- usec_t dirs_ts_usec;
- ResolveNameTiming resolve_name_timing;
- Hashmap *known_users;
- Hashmap *known_groups;
- UdevRuleFile *current_file;
- LIST_HEAD(UdevRuleFile, rule_files);
-};
-
int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing);
UdevRules *udev_rules_free(UdevRules *rules);
DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRules*, udev_rules_free);
usec_t timeout_usec,
Hashmap *properties_list);
int udev_rules_apply_static_dev_perms(UdevRules *rules);
-
-#define log_rule_full(device, rules, level, error, fmt, ...) \
- ({ \
- UdevRules *_r = (rules); \
- UdevRuleFile *_f = _r ? _r->current_file : NULL; \
- UdevRuleLine *_l = _f ? _f->current_line : NULL; \
- const char *_n = _f ? _f->filename : NULL; \
- \
- log_device_full(device, level, error, "%s:%u " fmt, \
- strna(_n), _l ? _l->line_number : 0, \
- ##__VA_ARGS__); \
- })
-
-#define log_rule_debug(device, rules, ...) log_rule_full(device, rules, LOG_DEBUG, 0, ##__VA_ARGS__)
-#define log_rule_info(device, rules, ...) log_rule_full(device, rules, LOG_INFO, 0, ##__VA_ARGS__)
-#define log_rule_notice(device, rules, ...) log_rule_full(device, rules, LOG_NOTICE, 0, ##__VA_ARGS__)
-#define log_rule_warning(device, rules, ...) log_rule_full(device, rules, LOG_WARNING, 0, ##__VA_ARGS__)
-#define log_rule_error(device, rules, ...) log_rule_full(device, rules, LOG_ERR, 0, ##__VA_ARGS__)
-
-#define log_rule_debug_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_DEBUG, error, ##__VA_ARGS__)
-#define log_rule_info_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_INFO, error, ##__VA_ARGS__)
-#define log_rule_notice_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_NOTICE, error, ##__VA_ARGS__)
-#define log_rule_warning_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_WARNING, error, ##__VA_ARGS__)
-#define log_rule_error_errno(device, rules, error, ...) log_rule_full(device, rules, LOG_ERR, error, ##__VA_ARGS__)
-
-#define log_token_full(rules, ...) log_rule_full(NULL, rules, ##__VA_ARGS__)
-
-#define log_token_debug(rules, ...) log_token_full(rules, LOG_DEBUG, 0, ##__VA_ARGS__)
-#define log_token_info(rules, ...) log_token_full(rules, LOG_INFO, 0, ##__VA_ARGS__)
-#define log_token_notice(rules, ...) log_token_full(rules, LOG_NOTICE, 0, ##__VA_ARGS__)
-#define log_token_warning(rules, ...) log_token_full(rules, LOG_WARNING, 0, ##__VA_ARGS__)
-#define log_token_error(rules, ...) log_token_full(rules, LOG_ERR, 0, ##__VA_ARGS__)
-
-#define log_token_debug_errno(rules, error, ...) log_token_full(rules, LOG_DEBUG, error, ##__VA_ARGS__)
-#define log_token_info_errno(rules, error, ...) log_token_full(rules, LOG_INFO, error, ##__VA_ARGS__)
-#define log_token_notice_errno(rules, error, ...) log_token_full(rules, LOG_NOTICE, error, ##__VA_ARGS__)
-#define log_token_warning_errno(rules, error, ...) log_token_full(rules, LOG_WARNING, error, ##__VA_ARGS__)
-#define log_token_error_errno(rules, error, ...) log_token_full(rules, LOG_ERR, error, ##__VA_ARGS__)
-
-#define _log_token_invalid(rules, key, type) \
- log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL), \
- "Invalid %s for %s.", type, key)
-
-#define log_token_invalid_op(rules, key) _log_token_invalid(rules, key, "operator")
-#define log_token_invalid_attr(rules, key) _log_token_invalid(rules, key, "attribute")
-
-#define log_token_invalid_attr_format(rules, key, attr) \
- log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL), \
- "Invalid attribute \"%s\" for %s, ignoring, but please fix it.", \
- attr, key)
-#define log_token_invalid_value(rules, key, value) \
- log_token_error_errno(rules, SYNTHETIC_ERRNO(EINVAL), \
- "Invalid value \"%s\" for %s, ignoring, but please fix it.", \
- value, key)
{}
};
- r = must_be_root();
- if (r < 0)
- return r;
-
if (running_in_chroot() > 0) {
log_info("Running in chroot, ignoring request.");
return 0;
return log_error_errno(r, "Unknown device \"%s\": %m", *p);
if (arg_wait_for_initialization_timeout > 0) {
- r = device_wait_for_initialization(device, NULL, arg_wait_for_initialization_timeout, NULL);
+ sd_device *d;
+
+ r = device_wait_for_initialization(device, NULL, arg_wait_for_initialization_timeout, &d);
if (r < 0)
return r;
+
+ sd_device_unref(device);
+ device = d;
}
if (action == ACTION_QUERY)
"Test an event run.\n\n"
" -h --help Show this help\n"
" -V --version Show package version\n"
- " -a --action=ACTION Set action string\n"
+ " -a --action=ACTION|help Set action string\n"
" -N --resolve-names=early|late|never When to resolve names\n"
, program_invocation_short_name);
case 'a': {
DeviceAction a;
+ if (streq(optarg, "help")) {
+ dump_device_action_table();
+ return 0;
+ }
+
a = device_action_from_string(optarg);
if (a < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_set) {
sd_device *d;
- int r;
+ int r, ret = 0;
FOREACH_DEVICE_AND_SUBSYSTEM(e, d) {
_cleanup_free_ char *filename = NULL;
r = write_string_file(filename, action, WRITE_STRING_FILE_DISABLE_BUFFER);
if (r < 0) {
- log_debug_errno(r, "Failed to write '%s' to '%s', ignoring: %m", action, filename);
+ log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
+ "Failed to write '%s' to '%s': %m", action, filename);
+ if (ret == 0 && r != -ENOENT)
+ ret = r;
continue;
}
}
}
- return 0;
+ return ret;
}
static int device_monitor_handler(sd_device_monitor *m, sd_device *dev, void *userdata) {
" -t --type= Type of events to trigger\n"
" devices sysfs devices (default)\n"
" subsystems sysfs subsystems and drivers\n"
- " -c --action=ACTION Event action value, default is \"change\"\n"
+ " -c --action=ACTION|help Event action value, default is \"change\"\n"
" -s --subsystem-match=SUBSYSTEM Trigger devices from a matching subsystem\n"
" -S --subsystem-nomatch=SUBSYSTEM Exclude devices from a matching subsystem\n"
" -a --attr-match=FILE[=VALUE] Trigger devices with a matching attribute\n"
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg);
break;
case 'c':
+ if (streq(optarg, "help")) {
+ dump_device_action_table();
+ return 0;
+ }
if (device_action_from_string(optarg) < 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown action '%s'", optarg);
}
}
- if (!arg_dry_run || ping) {
- r = must_be_root();
- if (r < 0)
- return r;
- }
-
if (ping) {
_cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL;
}
eq++;
- if (!isempty(eq)) {
+ if (isempty(eq)) {
log_debug("Received udev control message (ENV), unsetting '%s'", key);
r = hashmap_put(manager->properties, key, NULL);
int r;
log_set_target(LOG_TARGET_AUTO);
+ log_open();
udev_parse_config_full(&arg_children_max, &arg_exec_delay_usec, &arg_event_timeout_usec, &arg_resolve_name_timing);
log_parse_environment();
- log_open();
+ log_open(); /* Done again to update after reading configuration. */
r = parse_argv(argc, argv);
if (r <= 0)
#include "main-func.h"
#include "mkdir.h"
#include "parse-util.h"
+#include "path-util.h"
#include "proc-cmdline.h"
#include "specifier.h"
#include "string-util.h"
if (!arg_data_what) {
memcpy(&root_uuid, m, sizeof(root_uuid));
- arg_data_what = strjoin("/dev/disk/by-partuuid/", id128_to_uuid_string(root_uuid, ids));
+ arg_data_what = path_join("/dev/disk/by-partuuid", id128_to_uuid_string(root_uuid, ids));
if (!arg_data_what)
return log_oom();
}
if (!arg_hash_what) {
memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid));
- arg_hash_what = strjoin("/dev/disk/by-partuuid/", id128_to_uuid_string(verity_uuid, ids));
+ arg_hash_what = path_join("/dev/disk/by-partuuid", id128_to_uuid_string(verity_uuid, ids));
if (!arg_hash_what)
return log_oom();
}
# and systemd-coredump(8) and core(5) for the explanation of the
# setting below.
-kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t %c %h %e
+kernel.core_pattern=|@rootlibexecdir@/systemd-coredump %P %u %g %s %t %c %h
EOF
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
ddebug "umount $TESTDIR/root"
cat >>$initdir/etc/fstab <<EOF
/dev/mapper/varcrypt /var ext4 defaults 0 1
EOF
- ) || return 1
+ )
ddebug "umount $TESTDIR/root/var"
umount $TESTDIR/root/var
cp test-jobs.sh $initdir/
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
ddebug "umount $TESTDIR/root"
cp test-journal.sh $initdir/
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
ddebug "umount $TESTDIR/root"
cp test-rlimits.sh $initdir/
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
ddebug "umount $TESTDIR/root"
dracut_install -o sesearch
dracut_install runcon
dracut_install checkmodule semodule semodule_package m4 make /usr/libexec/selinux/hll/pp load_policy sefcontext_compile
- ) || return 1
+ )
# mask some services that we do not want to run in these tests
ln -s /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
cp test-segfault.sh $initdir/
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
ddebug "umount $TESTDIR/root"
EOF
setup_testsuite
- ) || return 1
+ )
ln -s /etc/systemd/system/-.mount $initdir/etc/systemd/system/root.mount
mkdir -p $initdir/etc/systemd/system/local-fs.target.wants
EOF
setup_testsuite
- ) || return 1
+ )
# mask some services that we do not want to run in these tests
ln -s /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
EOF
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
# mask some services that we do not want to run in these tests
chmod 0755 $initdir/test-fail-on-restart.sh
setup_testsuite
- ) || return 1
+ )
ddebug "umount $TESTDIR/root"
umount $TESTDIR/root
chmod 0755 $initdir/test-socket-group.sh
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
chmod 0755 $initdir/test-nspawn.sh
setup_testsuite
- ) || return 1
+ )
ddebug "umount $TESTDIR/root"
umount $TESTDIR/root
chmod +x $initdir/test-machine-id-setup.sh
setup_testsuite
- ) || return 1
+ )
# mask some services that we do not want to run in these tests
ln -s /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
cp $BUILD_DIR/src/shared/libsystemd-shared-*.so ${initdir}/usr/lib
setup_testsuite
- ) || return 1
+ )
# mask some services that we do not want to run in these tests
ln -s /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
ln -s /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
ddebug "umount $TESTDIR/root"
umount $TESTDIR/root
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
# mask some services that we do not want to run in these tests
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
ddebug "umount $TESTDIR/root"
umount $TESTDIR/root
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
ddebug "umount $TESTDIR/root"
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
ddebug "umount $TESTDIR/root"
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
# mask some services that we do not want to run in these tests
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
ddebug "umount $TESTDIR/root"
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
ddebug "umount $TESTDIR/root"
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
ddebug "umount $TESTDIR/root"
EOF
setup_testsuite
- ) || return 1
+ )
setup_nspawn_root
# mask some services that we do not want to run in these tests
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
ddebug "umount $TESTDIR/root"
umount $TESTDIR/root
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
ddebug "umount $TESTDIR/root"
umount $TESTDIR/root
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
ddebug "umount $TESTDIR/root"
umount $TESTDIR/root
cp testsuite.sh $initdir/
setup_testsuite
- ) || return 1
+ )
ddebug "umount $TESTDIR/root"
umount $TESTDIR/root
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -e
+TEST_DESCRIPTION="test CleanUnit"
+
+. $TEST_BASE_DIR/test-functions
+
+test_setup() {
+ create_empty_image
+ mkdir -p $TESTDIR/root
+ mount ${LOOPDEV}p1 $TESTDIR/root
+
+ (
+ LOG_LEVEL=5
+ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+ setup_basic_environment
+
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/bin/bash -x /testsuite.sh
+Type=oneshot
+StandardOutput=tty
+StandardError=tty
+EOF
+ cp testsuite.sh $initdir/
+
+ setup_testsuite
+ ) || return 1
+ setup_nspawn_root
+
+ ddebug "umount $TESTDIR/root"
+ umount $TESTDIR/root
+}
+
+do_test "$@"
--- /dev/null
+#!/bin/bash
+# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
+# ex: ts=8 sw=4 sts=4 et filetype=sh
+set -ex
+set -o pipefail
+
+cat > /etc/systemd/system/testservice.service <<EOF
+[Service]
+ConfigurationDirectory=testservice
+RuntimeDirectory=testservice
+StateDirectory=testservice
+CacheDirectory=testservice
+LogsDirectory=testservice
+RuntimeDirectoryPreserve=yes
+ExecStart=/bin/sleep infinity
+Type=exec
+EOF
+
+systemctl daemon-reload
+
+! test -e /etc/testservice
+! test -e /run/testservice
+! test -e /var/lib/testservice
+! test -e /var/cache/testservice
+! test -e /var/log/testservice
+
+systemctl start testservice
+
+test -d /etc/testservice
+test -d /run/testservice
+test -d /var/lib/testservice
+test -d /var/cache/testservice
+test -d /var/log/testservice
+
+! systemctl clean testservice
+
+systemctl stop testservice
+
+test -d /etc/testservice
+test -d /run/testservice
+test -d /var/lib/testservice
+test -d /var/cache/testservice
+test -d /var/log/testservice
+
+systemctl clean testservice --what=configuration
+
+! test -e /etc/testservice
+test -d /run/testservice
+test -d /var/lib/testservice
+test -d /var/cache/testservice
+test -d /var/log/testservice
+
+systemctl clean testservice
+
+! test -e /etc/testservice
+! test -e /run/testservice
+test -d /var/lib/testservice
+! test -e /var/cache/testservice
+test -d /var/log/testservice
+
+systemctl clean testservice --what=logs
+
+! test -e /etc/testservice
+! test -e /run/testservice
+test -d /var/lib/testservice
+! test -e /var/cache/testservice
+! test -e /var/log/testservice
+
+systemctl clean testservice --what=all
+
+! test -e /etc/testservice
+! test -e /run/testservice
+! test -e /var/lib/testservice
+! test -e /var/cache/testservice
+! test -e /var/log/testservice
+
+echo OK > /testok
+
+exit 0
--- /dev/null
+../TEST-01-BASIC/Makefile
\ No newline at end of file
--- /dev/null
+#!/bin/bash
+set -e
+TEST_DESCRIPTION="test migrating state directory from DynamicUser=1 to DynamicUser=0 and back"
+
+. $TEST_BASE_DIR/test-functions
+
+test_setup() {
+ create_empty_image
+ mkdir -p $TESTDIR/root
+ mount ${LOOPDEV}p1 $TESTDIR/root
+
+ (
+ LOG_LEVEL=5
+ eval $(udevadm info --export --query=env --name=${LOOPDEV}p2)
+
+ setup_basic_environment
+
+ # mask some services that we do not want to run in these tests
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-hwdb-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-journal-catalog-update.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-networkd.socket
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-resolved.service
+ ln -fs /dev/null $initdir/etc/systemd/system/systemd-machined.service
+
+ # setup the testsuite service
+ cat >$initdir/etc/systemd/system/testsuite.service <<EOF
+[Unit]
+Description=Testsuite service
+
+[Service]
+ExecStart=/bin/bash -x /testsuite.sh
+Type=oneshot
+StandardOutput=tty
+StandardError=tty
+NotifyAccess=all
+EOF
+ cp testsuite.sh $initdir/
+
+ setup_testsuite
+ )
+ setup_nspawn_root
+
+ ddebug "umount $TESTDIR/root"
+ umount $TESTDIR/root
+}
+
+do_test "$@"
--- /dev/null
+#!/bin/bash
+set -ex
+set -o pipefail
+
+systemd-analyze log-level debug
+systemd-analyze log-target console
+
+# Set everything up without DynamicUser=1
+
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz touch /var/lib/zzz/test
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
+! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+
+test -d /var/lib/zzz
+! test -L /var/lib/zzz
+! test -e /var/lib/private/zzz
+test -f /var/lib/zzz/test
+! test -f /var/lib/zzz/test-missing
+
+# Convert to DynamicUser=1
+
+systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test
+! systemd-run --wait -p DynamicUser=1 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+
+test -L /var/lib/zzz
+test -d /var/lib/private/zzz
+
+test -f /var/lib/zzz/test
+! test -f /var/lib/zzz/test-missing
+
+# Convert back
+
+systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test
+! systemd-run --wait -p DynamicUser=0 -p StateDirectory=zzz test -f /var/lib/zzz/test-missing
+
+test -d /var/lib/zzz
+! test -L /var/lib/zzz
+! test -e /var/lib/private/zzz
+test -f /var/lib/zzz/test
+! test -f /var/lib/zzz/test-missing
+
+systemd-analyze log-level info
+
+echo OK > /testok
+
+exit 0
--- /dev/null
+[Unit]
+Description=A conjugate
+Requires=a.service
+After=a.service
+Before=a.service
+
+[Service]
+ExecStart=/bin/true
Path=
Driver=
Type=
+Property=
Host=
Virtualization=
KernelCommandLine=
--- /dev/null
+[NetDev]
+Name=xfrm99
+Kind=xfrm
+
+[Xfrm]
+InterfaceId=7
+Independent=false
Local=
TOS=
Independent=
+AssignToLoopback=
Key=
InputKey=
Encapsulation=
Key=
KeyFile=
Activate=
+[Xfrm]
+Independent=
+InterfaceId=
--- /dev/null
+[Match]
+Name=eth0
+
+[Network]
+Xfrm=xfrm99
Architecture=
Path=
Name=
+Property=
Virtualization=
KernelCommandLine=
Host=
RouteTable=
BlackList=
SendRelease=
+MaxAttempts=
+[DHCPv4]
+UseDNS=
+UseDomains=
+UseRoutes=
+IAID=
+UserClass=
+UseNTP=
+UseMTU=
+UseDomainName=
+RouteMetric=
+SendHostname=
+Anonymize=
+VendorClassIdentifier=
+Hostname=
+DUIDType=
+UseHostname=
+CriticalConnection=
+DUIDRawData=
+RequestBroadcast=
+ClientIdentifier=
+ListenPort=
+UseTimezone=
+RouteTable=
+BlackList=
+SendRelease=
+MaxAttempts=
+[DHCPv6]
+UseNTP=
+UseDNS=
+RapidCommit=
+ForceDHCPv6PDOtherInformation=
[Route]
Destination=
Protocol=
IPv6HopLimit=
IPForward=
IPv6Token=
+Xfrm=
Description=
VXLAN=
L2TP=
BindCarrier=
VRF=
IgnoreCarrierLoss=
+KeepConfiguration=
[IPv6Prefix]
Prefix=
OnLink=
DefaultLeaseTimeSec=
EmitTimezone=
DNS=
-MaxAttempts=
-[DHCPv4]
-UseHostname=
-UseMTU=
-UseDomainName=
-CriticalConnection=
-UseDNS=
--- /dev/null
+[IPv6AcceptRA]
+BlackList=70:: 70::
\ No newline at end of file
rd.udev.exec_delay=
rd.udev.log_priority=
resume=
+resumeflags=
root=
rootflags=
rootfstype=
--- /dev/null
+[Unit]
+Description=I
+Conflicts=a.service d.service
+Wants=b.service
+After=b.service
+
+[Service]
+ExecStart=/bin/true
test_data_files = '''
a.service
+ a-conj.service
b.service
basic.target
c.service
hello-after-sleep.target
hello.service
hwdb/10-bad.hwdb
+ i.service
journal-data/journal-1.txt
journal-data/journal-2.txt
nomem.slice
[Unit]
Description=Test for basic execution
+ConditionKernelVersion=">=3.0"
+ConditionKernelVersion=">=2.0" "<=60" "!=1.4"
+ConditionKernelVersion=" >= 2.0" " <= 60 " "!= 1.4"
+ConditionKernelVersion=" >= 2.0" " * " "*.*"
[Service]
ExecStart=touch /tmp/a ; /bin/sh -c 'touch /tmp/b' ; touch /tmp/c
set -x
-DEFAULT_ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
-DEFAULT_UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1
+DEFAULT_ASAN_OPTIONS=${ASAN_OPTIONS:-strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1}
+DEFAULT_UBSAN_OPTIONS=${UBSAN_OPTIONS:-print_stacktrace=1:print_summary=1:halt_on_error=1}
DEFAULT_ENVIRONMENT="ASAN_OPTIONS=\$DEFAULT_ASAN_OPTIONS UBSAN_OPTIONS=\$DEFAULT_UBSAN_OPTIONS"
# As right now bash is the PID 1, we can't expect PATH to have a sane value.
# some {rc,halt}.local scripts and programs are okay to not exist, the rest should
# also, plymouth is pulled in by rescue.service, but even there the exit code
# is ignored; as it's not present on some distros, don't fail if it doesn't exist
+ dinfo "Attempting to install $i"
inst $i || [ "${i%.local}" != "$i" ] || [ "${i%systemd-update-done}" != "$i" ] || [ "/bin/plymouth" == "$i" ]
done
)
# Install binary executable, and all shared library dependencies, if any.
inst_binary() {
local _bin _target
+
+ # In certain cases we might attempt to install a binary which is already
+ # present in the test image, yet it's missing from the host system.
+ # In such cases, let's check if the binary indeed exists in the image
+ # before doing any other chcecks. If it does, immediately return with
+ # success.
+ [[ $# -eq 1 && -e $initdir/$1 ]] && return 0
+
_bin=$(find_binary "$1") || return 1
_target=${2:-$_bin}
[[ -e $initdir/$_target ]] && return 0
--- /dev/null
+[Match]
+Name=dummy98
+Property=INTERFACE=hoge
+
+[Network]
+IPv6AcceptRA=false
--- /dev/null
+[Match]
+Name=dummy98
+Property=INTERFACE=dummy98
+
+[Network]
+IPv6AcceptRA=false
+Address=10.1.2.3/16
+++ /dev/null
-[Match]
-Name=test1
-
-[Network]
-Bond=bond199
-PrimarySlave=true
--- /dev/null
+[Match]
+Name=dummy98
+
+[Network]
+KeepConfiguration=static
--- /dev/null
+[Match]
+Name=bridge99
+
+[Network]
+LinkLocalAddressing=yes
+IPv6AcceptRA=no
+ConfigureWithoutCarrier=yes
--- /dev/null
+[NetDev]
+Name=gretun96
+Kind=gre
+
+[Tunnel]
+Local=any
+Remote=any
+Key=106
+SerializeTunneledPackets=false
--- /dev/null
+[NetDev]
+Name=ip6gretun96
+Kind=ip6gre
+
+[Tunnel]
+Local=any
+Remote=any
--- /dev/null
+[NetDev]
+Name=ipiptun96
+Kind=ipip
+MTUBytes=1480
+
+[Tunnel]
+Local=any
+Remote=any
--- /dev/null
+[NetDev]
+Name=ipiptun99
+Kind=ipip
+MTUBytes=1480
+
+[Tunnel]
+Local=192.168.223.238
+Remote=192.169.224.239
+Independent=true
+AssignToLoopback=yes
[Match]
Name=dummy98
+[Network]
+IPv6AcceptRA=no
+
[Neighbor]
Address=192.168.10.1
MACAddress=00:00:5e:00:02:65
--- /dev/null
+[NetDev]
+Name=sittun96
+Kind=sit
+
+[Tunnel]
+Local=any
+Remote=any
[Network]
IPv6AcceptRA=no
Address=10.2.3.4/16
+
+# This should be ignored when ipv6 is disabled
+Gateway=2607:5300:0203:39ff:ff:ff:ff:ff
+
+[Address]
+# This should be ignored when ipv6 is disabled
+Address=2607:5300:0203:3906::/64
+
+[Route]
+# This should be ignored when ipv6 is disabled
+Destination=2607:5300:0203:39ff:ff:ff:ff:ff
+Scope=link
--- /dev/null
+[Match]
+Name=*tun96
+
+[Network]
+IPv6AcceptRA=no
+Address=2001:db8:0:f102::19/64
+Address=10.3.2.6/16
+LinkLocalAddressing=yes
--- /dev/null
+[NetDev]
+Name=vtitun96
+Kind=vti
+
+[Tunnel]
+Local=any
+Remote=any
--- /dev/null
+[NetDev]
+Kind=xfrm
+Name=xfrm99
+
+[Xfrm]
+Independent=yes
--- /dev/null
+[NetDev]
+Kind=xfrm
+Name=xfrm99
--- /dev/null
+[Match]
+Name=bridge99
+
+[Network]
+IPv6AcceptRA=false
+
+[BridgeVLAN]
+VLAN=4060-4094
--- /dev/null
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+Bridge=bridge99
+
+[BridgeVLAN]
+VLAN=4064-4094
Gateway=192.168.0.1
IPv6AcceptRA=no
IgnoreCarrierLoss=true
+ConfigureWithoutCarrier=true
[RoutingPolicyRule]
To=8.8.8.8
[Network]
DHCP=ipv4
IPv6AcceptRA=false
-
-[DHCP]
-CriticalConnection=true
+KeepConfiguration=dhcp-on-stop
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+KeepConfiguration=dhcp
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+
+[DHCP]
+UseDNS=yes
+
+[IPv6AcceptRA]
+UseDNS=yes
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+
+[DHCPv4]
+UseDNS=yes
+
+[DHCPv6]
+UseDNS=no
+
+[IPv6AcceptRA]
+UseDNS=no
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+DHCP=yes
+
+[DHCP]
+UseDNS=no
+
+[IPv6AcceptRA]
+UseDNS=no
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+DHCP=yes
+
+[DHCP]
+UseDNS=yes
+
+[IPv6AcceptRA]
+UseDNS=no
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=no
+
+[Route]
+Destination=192.168.5.0/24
+
+[Route]
+Destination=192.168.6.0/24
+
+[Route]
+Destination=192.168.7.0/24
+
+[DHCP]
+UseRoutes=no
--- /dev/null
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=no
+Address=192.168.5.250
Tunnel=gretun99
Tunnel=gretun98
Tunnel=gretun97
+Tunnel=gretun96
Tunnel=ip6gretun99
Tunnel=ip6gretun98
Tunnel=ip6gretun97
+Tunnel=ip6gretun96
Tunnel=ipiptun99
Tunnel=ipiptun98
Tunnel=ipiptun97
+Tunnel=ipiptun96
Name=geneve99
Name=ipiptun99
Name=nlmon99
+Name=xfrm99
[Network]
LinkLocalAddressing=yes
Tunnel=sittun99
Tunnel=sittun98
Tunnel=sittun97
+Tunnel=sittun96
Tunnel=vtitun99
Tunnel=vtitun98
Tunnel=vtitun97
+Tunnel=vtitun96
--- /dev/null
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Xfrm=xfrm99
import re
import shutil
import signal
-import socket
import subprocess
import sys
import time
dnsmasq_log_file='/run/networkd-ci/test-dnsmasq-log-file'
networkd_bin='/usr/lib/systemd/systemd-networkd'
+resolved_bin='/usr/lib/systemd/systemd-resolved'
wait_online_bin='/usr/lib/systemd/systemd-networkd-wait-online'
networkctl_bin='/usr/bin/networkctl'
+resolvectl_bin='/usr/bin/resolvectl'
+timedatectl_bin='/usr/bin/timedatectl'
use_valgrind=False
-enable_debug=False
+enable_debug=True
env = {}
asan_options=None
lsan_options=None
ubsan_options=None
+def check_output(*command, **kwargs):
+ # This replaces both check_output and check_call (output can be ignored)
+ command = command[0].split() + list(command[1:])
+ return subprocess.check_output(command, universal_newlines=True, **kwargs).rstrip()
+
+def call(*command, **kwargs):
+ command = command[0].split() + list(command[1:])
+ return subprocess.call(command, universal_newlines=True, **kwargs)
+
+def run(*command, **kwargs):
+ command = command[0].split() + list(command[1:])
+ return subprocess.run(command, universal_newlines=True, **kwargs)
+
def is_module_available(module_name):
- lsmod_output = subprocess.check_output('lsmod', universal_newlines=True)
- module_re = re.compile(r'^{0}\b'.format(re.escape(module_name)), re.MULTILINE)
- return module_re.search(lsmod_output) or not subprocess.call(["modprobe", module_name])
+ lsmod_output = check_output('lsmod')
+ module_re = re.compile(rf'^{re.escape(module_name)}\b', re.MULTILINE)
+ return module_re.search(lsmod_output) or not call('modprobe', module_name)
def expectedFailureIfModuleIsNotAvailable(module_name):
def f(func):
def expectedFailureIfERSPANModuleIsNotAvailable():
def f(func):
- rc = subprocess.call(['ip', 'link', 'add', 'dev', 'erspan99', 'type', 'erspan', 'seq', 'key', '30', 'local', '192.168.1.4', 'remote', '192.168.1.1', 'erspan_ver', '1', 'erspan', '123'])
+ rc = call('ip link add dev erspan99 type erspan seq key 30 local 192.168.1.4 remote 192.168.1.1 erspan_ver 1 erspan 123')
if rc == 0:
- subprocess.call(['ip', 'link', 'del', 'erspan99'])
+ call('ip link del erspan99')
return func
else:
return unittest.expectedFailure(func)
def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
def f(func):
- rc = subprocess.call(['ip', 'rule', 'add', 'from', '192.168.100.19', 'sport', '1123-1150', 'dport', '3224-3290', 'table', '7'])
+ rc = call('ip rule add from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
if rc == 0:
- subprocess.call(['ip', 'rule', 'del', 'from', '192.168.100.19', 'sport', '1123-1150', 'dport', '3224-3290', 'table', '7'])
+ call('ip rule del from 192.168.100.19 sport 1123-1150 dport 3224-3290 table 7')
return func
else:
return unittest.expectedFailure(func)
def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable():
def f(func):
- rc = subprocess.call(['ip', 'rule', 'add', 'not', 'from', '192.168.100.19', 'ipproto', 'tcp', 'table', '7'])
+ rc = call('ip rule add not from 192.168.100.19 ipproto tcp table 7')
if rc == 0:
- subprocess.call(['ip', 'rule', 'del', 'not', 'from', '192.168.100.19', 'ipproto', 'tcp', 'table', '7'])
+ call('ip rule del not from 192.168.100.19 ipproto tcp table 7')
return func
else:
return unittest.expectedFailure(func)
return f
-def expectedFailureIfEthtoolDoesNotSupportDriver():
+def expectedFailureIfLinkFileFieldIsNotSet():
def f(func):
support = False
- rc = subprocess.call(['ip', 'link', 'add', 'name', 'dummy99', 'type', 'dummy'])
+ rc = call('ip link add name dummy99 type dummy')
if rc == 0:
- ret = subprocess.run(['udevadm', 'info', '-w10s', '/sys/class/net/dummy99'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
- if ret.returncode == 0 and 'E: ID_NET_DRIVER=dummy' in ret.stdout.rstrip():
+ ret = run('udevadm info -w10s /sys/class/net/dummy99', stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ if ret.returncode == 0 and 'E: ID_NET_LINK_FILE=' in ret.stdout.rstrip():
support = True
- subprocess.call(['ip', 'link', 'del', 'dummy99'])
+ call('ip link del dummy99')
if support:
return func
shutil.rmtree(networkd_ci_path)
copytree(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'conf'), networkd_ci_path)
- subprocess.check_call('systemctl stop systemd-networkd.socket', shell=True)
- subprocess.check_call('systemctl stop systemd-networkd.service', shell=True)
+ check_output('systemctl stop systemd-networkd.socket')
+ check_output('systemctl stop systemd-networkd.service')
+ check_output('systemctl stop systemd-resolved.service')
drop_in = [
'[Service]',
with open('/run/systemd/system/systemd-networkd.service.d/00-override.conf', mode='w') as f:
f.write('\n'.join(drop_in))
- subprocess.check_call('systemctl daemon-reload', shell=True)
- output = subprocess.check_output(['systemctl', 'cat', 'systemd-networkd.service'], universal_newlines=True).rstrip()
- print(output)
+ drop_in = [
+ '[Service]',
+ 'Restart=no',
+ 'ExecStart=',
+ ]
+ if use_valgrind:
+ drop_in += ['ExecStart=!!valgrind --track-origins=yes --leak-check=full --show-leak-kinds=all ' + resolved_bin]
+ else:
+ drop_in += ['ExecStart=!!' + resolved_bin]
+ if enable_debug:
+ drop_in += ['Environment=SYSTEMD_LOG_LEVEL=debug']
+ if asan_options:
+ drop_in += ['Environment=ASAN_OPTIONS="' + asan_options + '"']
+ if lsan_options:
+ drop_in += ['Environment=LSAN_OPTIONS="' + lsan_options + '"']
+ if ubsan_options:
+ drop_in += ['Environment=UBSAN_OPTIONS="' + ubsan_options + '"']
+ if asan_options or lsan_options or ubsan_options:
+ drop_in += ['SystemCallFilter=']
+ if use_valgrind or asan_options or lsan_options or ubsan_options:
+ drop_in += ['MemoryDenyWriteExecute=no']
+
+ os.makedirs('/run/systemd/system/systemd-resolved.service.d', exist_ok=True)
+ with open('/run/systemd/system/systemd-resolved.service.d/00-override.conf', mode='w') as f:
+ f.write('\n'.join(drop_in))
+
+ check_output('systemctl daemon-reload')
+ print(check_output('systemctl cat systemd-networkd.service'))
+ print(check_output('systemctl cat systemd-resolved.service'))
+ check_output('systemctl restart systemd-resolved')
def tearDownModule():
shutil.rmtree(networkd_ci_path)
- subprocess.check_call('systemctl stop systemd-networkd.service', shell=True)
+ check_output('systemctl stop systemd-networkd.service')
+ check_output('systemctl stop systemd-resolved.service')
shutil.rmtree('/run/systemd/system/systemd-networkd.service.d')
- subprocess.check_call('systemctl daemon-reload', shell=True)
+ shutil.rmtree('/run/systemd/system/systemd-resolved.service.d')
+ check_output('systemctl daemon-reload')
+
+ check_output('systemctl start systemd-networkd.socket')
+ check_output('systemctl start systemd-resolved.service')
+
+def read_link_attr(link, dev, attribute):
+ with open(os.path.join(os.path.join(os.path.join('/sys/class/net/', link), dev), attribute)) as f:
+ return f.readline().strip()
+
+def read_bridge_port_attr(bridge, link, attribute):
+ path_bridge = os.path.join('/sys/devices/virtual/net', bridge)
+ path_port = 'lower_' + link + '/brport'
+ path = os.path.join(path_bridge, path_port)
+
+ with open(os.path.join(path, attribute)) as f:
+ return f.readline().strip()
+
+def link_exists(link):
+ return os.path.exists(os.path.join('/sys/class/net', link))
+
+def remove_links(links):
+ for link in links:
+ if link_exists(link):
+ call('ip link del dev', link)
+ time.sleep(1)
+
+def remove_fou_ports(ports):
+ for port in ports:
+ call('ip fou del port', port, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+
+def remove_routing_policy_rule_tables(tables):
+ for table in tables:
+ rc = 0
+ while rc == 0:
+ rc = call('ip rule del table', table, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+
+def remove_routes(routes):
+ for route_type, addr in routes:
+ call('ip route del', route_type, addr, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+
+def remove_l2tp_tunnels(tunnel_ids):
+ output = check_output('ip l2tp show tunnel')
+ for tid in tunnel_ids:
+ words='Tunnel ' + tid + ', encap'
+ if words in output:
+ call('ip l2tp del tunnel tid', tid)
+ time.sleep(1)
+
+def read_ipv6_sysctl_attr(link, attribute):
+ with open(os.path.join(os.path.join(network_sysctl_ipv6_path, link), attribute)) as f:
+ return f.readline().strip()
+
+def read_ipv4_sysctl_attr(link, attribute):
+ with open(os.path.join(os.path.join(network_sysctl_ipv4_path, link), attribute)) as f:
+ return f.readline().strip()
+
+def copy_unit_to_networkd_unit_path(*units):
+ print()
+ for unit in units:
+ shutil.copy(os.path.join(networkd_ci_path, unit), network_unit_file_path)
+ if (os.path.exists(os.path.join(networkd_ci_path, unit + '.d'))):
+ copytree(os.path.join(networkd_ci_path, unit + '.d'), os.path.join(network_unit_file_path, unit + '.d'))
+
+def remove_unit_from_networkd_path(units):
+ for unit in units:
+ if (os.path.exists(os.path.join(network_unit_file_path, unit))):
+ os.remove(os.path.join(network_unit_file_path, unit))
+ if (os.path.exists(os.path.join(network_unit_file_path, unit + '.d'))):
+ shutil.rmtree(os.path.join(network_unit_file_path, unit + '.d'))
+
+def warn_about_firewalld():
+ rc = call('systemctl -q is-active firewalld.service')
+ if rc == 0:
+ print('\nWARNING: firewalld.service is active. The test may fail.')
+
+def start_dnsmasq(additional_options='', ipv4_range='192.168.5.10,192.168.5.200', ipv6_range='2600::10,2600::20', lease_time='1h'):
+ warn_about_firewalld()
+ dnsmasq_command = f'dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range={ipv6_range},{lease_time} --dhcp-range={ipv4_range},{lease_time} -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --dhcp-option=33,192.168.5.4,192.168.5.5 --port=0 ' + additional_options
+ check_output(dnsmasq_command)
+
+def stop_dnsmasq(pid_file):
+ if os.path.exists(pid_file):
+ with open(pid_file, 'r') as f:
+ pid = f.read().rstrip(' \t\r\n\0')
+ os.kill(int(pid), signal.SIGTERM)
+
+ os.remove(pid_file)
+
+def search_words_in_dnsmasq_log(words, show_all=False):
+ if os.path.exists(dnsmasq_log_file):
+ with open (dnsmasq_log_file) as in_file:
+ contents = in_file.read()
+ if show_all:
+ print(contents)
+ for line in contents.splitlines():
+ if words in line:
+ in_file.close()
+ print("%s, %s" % (words, line))
+ return True
+ return False
+
+def remove_lease_file():
+ if os.path.exists(os.path.join(networkd_ci_path, 'lease')):
+ os.remove(os.path.join(networkd_ci_path, 'lease'))
+
+def remove_log_file():
+ if os.path.exists(dnsmasq_log_file):
+ os.remove(dnsmasq_log_file)
+
+def remove_networkd_state_files():
+ if os.path.exists(os.path.join(networkd_runtime_directory, 'state')):
+ os.remove(os.path.join(networkd_runtime_directory, 'state'))
+
+def stop_networkd(show_logs=True, remove_state_files=True):
+ if show_logs:
+ invocation_id = check_output('systemctl show systemd-networkd -p InvocationID --value')
+ check_output('systemctl stop systemd-networkd')
+ if show_logs:
+ print(check_output('journalctl _SYSTEMD_INVOCATION_ID=' + invocation_id))
+ if remove_state_files:
+ remove_networkd_state_files()
+
+def start_networkd(sleep_sec=0):
+ check_output('systemctl start systemd-networkd')
+ if sleep_sec > 0:
+ time.sleep(sleep_sec)
+
+def restart_networkd(sleep_sec=0, show_logs=True, remove_state_files=True):
+ stop_networkd(show_logs, remove_state_files)
+ start_networkd(sleep_sec)
+
+def wait_online(links_with_operstate, timeout='20s', bool_any=False):
+ args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate]
+ if bool_any:
+ args += ['--any']
+ try:
+ check_output(*args, env=env)
+ except subprocess.CalledProcessError:
+ for link in links_with_operstate:
+ output = check_output(*networkctl_cmd, 'status', link.split(':')[0], env=env)
+ print(output)
+ raise
- subprocess.check_call('systemctl start systemd-networkd.socket', shell=True)
- subprocess.check_call('systemctl start systemd-networkd.service', shell=True)
+def get_operstate(link, show_status=True, setup_state='configured'):
+ output = check_output(*networkctl_cmd, 'status', link, env=env)
+ if show_status:
+ print(output)
+ for line in output.splitlines():
+ if 'State:' in line and (not setup_state or setup_state in line):
+ return line.split()[1]
+ return None
class Utilities():
- def read_link_attr(self, link, dev, attribute):
- with open(os.path.join(os.path.join(os.path.join('/sys/class/net/', link), dev), attribute)) as f:
- return f.readline().strip()
-
- def read_bridge_port_attr(self, bridge, link, attribute):
-
- path_bridge = os.path.join('/sys/devices/virtual/net', bridge)
- path_port = 'lower_' + link + '/brport'
- path = os.path.join(path_bridge, path_port)
-
- with open(os.path.join(path, attribute)) as f:
- return f.readline().strip()
-
- def link_exists(self, link):
- return os.path.exists(os.path.join('/sys/class/net', link))
-
def check_link_exists(self, link):
- self.assertTrue(self.link_exists(link))
-
- def remove_links(self, links):
- for link in links:
- if self.link_exists(link):
- subprocess.call(['ip', 'link', 'del', 'dev', link])
- time.sleep(1)
-
- def remove_fou_ports(self, ports):
- for port in ports:
- subprocess.call(['ip', 'fou', 'del', 'port', port])
-
- def remove_routing_policy_rule_tables(self, tables):
- for table in tables:
- subprocess.call(['ip', 'rule', 'del', 'table', table])
-
- def remove_routes(self, routes):
- for route_type, addr in routes:
- subprocess.call(['ip', 'route', 'del', route_type, addr])
-
- def l2tp_tunnel_remove(self, tunnel_ids):
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel'], universal_newlines=True).rstrip()
- for tid in tunnel_ids:
- words='Tunnel ' + tid + ', encap'
- if words in output:
- subprocess.call(['ip', 'l2tp', 'del', 'tunnel', 'tid', tid])
- time.sleep(1)
-
- def read_ipv6_sysctl_attr(self, link, attribute):
- with open(os.path.join(os.path.join(network_sysctl_ipv6_path, link), attribute)) as f:
- return f.readline().strip()
-
- def read_ipv4_sysctl_attr(self, link, attribute):
- with open(os.path.join(os.path.join(network_sysctl_ipv4_path, link), attribute)) as f:
- return f.readline().strip()
-
- def copy_unit_to_networkd_unit_path(self, *units):
- print()
- for unit in units:
- shutil.copy(os.path.join(networkd_ci_path, unit), network_unit_file_path)
- if (os.path.exists(os.path.join(networkd_ci_path, unit + '.d'))):
- copytree(os.path.join(networkd_ci_path, unit + '.d'), os.path.join(network_unit_file_path, unit + '.d'))
-
- def remove_unit_from_networkd_path(self, units):
- for unit in units:
- if (os.path.exists(os.path.join(network_unit_file_path, unit))):
- os.remove(os.path.join(network_unit_file_path, unit))
- if (os.path.exists(os.path.join(network_unit_file_path, unit + '.d'))):
- shutil.rmtree(os.path.join(network_unit_file_path, unit + '.d'))
-
- def warn_about_firewalld(self):
- rc = subprocess.call(['systemctl', '-q', 'is-active', 'firewalld.service'])
- if rc == 0:
- print('\nWARNING: firewalld.service is active. The test may fail.')
-
- def start_dnsmasq(self, additional_options='', ipv4_range='192.168.5.10,192.168.5.200', ipv6_range='2600::10,2600::20', lease_time='1h'):
- self.warn_about_firewalld()
- dnsmasq_command = f'dnsmasq -8 /var/run/networkd-ci/test-dnsmasq-log-file --log-queries=extra --log-dhcp --pid-file=/var/run/networkd-ci/test-test-dnsmasq.pid --conf-file=/dev/null --interface=veth-peer --enable-ra --dhcp-range={ipv6_range},{lease_time} --dhcp-range={ipv4_range},{lease_time} -R --dhcp-leasefile=/var/run/networkd-ci/lease --dhcp-option=26,1492 --dhcp-option=option:router,192.168.5.1 --dhcp-option=33,192.168.5.4,192.168.5.5 --port=0 ' + additional_options
- subprocess.check_call(dnsmasq_command, shell=True)
-
- def stop_dnsmasq(self, pid_file):
- if os.path.exists(pid_file):
- with open(pid_file, 'r') as f:
- pid = f.read().rstrip(' \t\r\n\0')
- os.kill(int(pid), signal.SIGTERM)
-
- os.remove(pid_file)
-
- def search_words_in_dnsmasq_log(self, words, show_all=False):
- if os.path.exists(dnsmasq_log_file):
- with open (dnsmasq_log_file) as in_file:
- contents = in_file.read()
- if show_all:
- print(contents)
- for line in contents.splitlines():
- if words in line:
- in_file.close()
- print("%s, %s" % (words, line))
- return True
- return False
-
- def remove_lease_file(self):
- if os.path.exists(os.path.join(networkd_ci_path, 'lease')):
- os.remove(os.path.join(networkd_ci_path, 'lease'))
-
- def remove_log_file(self):
- if os.path.exists(dnsmasq_log_file):
- os.remove(dnsmasq_log_file)
-
- def start_networkd(self, sleep_sec=5, remove_state_files=True):
- if (remove_state_files and
- os.path.exists(os.path.join(networkd_runtime_directory, 'state'))):
- subprocess.check_call('systemctl stop systemd-networkd', shell=True)
- os.remove(os.path.join(networkd_runtime_directory, 'state'))
- subprocess.check_call('systemctl start systemd-networkd', shell=True)
- else:
- subprocess.check_call('systemctl restart systemd-networkd', shell=True)
- if sleep_sec > 0:
- time.sleep(sleep_sec)
-
- def wait_online(self, links_with_operstate, timeout='20s', bool_any=False):
- args = wait_online_cmd + [f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate]
- if bool_any:
- args += ['--any']
- try:
- subprocess.check_call(args, env=env)
- except subprocess.CalledProcessError:
- for link in links_with_operstate:
- output = subprocess.check_output(networkctl_cmd + ['status', link.split(':')[0]], universal_newlines=True, env=env).rstrip()
- print(output)
- raise
-
- def get_operstate(self, link, show_status=True, setup_state='configured'):
- output = subprocess.check_output(networkctl_cmd + ['status', link], universal_newlines=True, env=env).rstrip()
- if show_status:
- print(output)
- for line in output.splitlines():
- if 'State:' in line and (not setup_state or setup_state in line):
- return line.split()[1]
- return None
+ self.assertTrue(link_exists(link))
def check_operstate(self, link, expected, show_status=True, setup_state='configured'):
- self.assertRegex(self.get_operstate(link, show_status, setup_state), expected)
+ self.assertRegex(get_operstate(link, show_status, setup_state), expected)
def wait_address(self, link, address_regex, scope='global', ipv='', timeout_sec=100):
for i in range(timeout_sec):
if i > 0:
time.sleep(1)
- output = subprocess.check_output(['ip', ipv, 'address', 'show', 'dev', link, 'scope', scope], universal_newlines=True).rstrip()
+ output = check_output(f'ip {ipv} address show dev {link} scope {scope}')
if re.search(address_regex, output):
break
else:
]
def setUp(self):
- self.remove_links(self.links)
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
def tearDown(self):
- self.remove_links(self.links)
- self.remove_unit_from_networkd_path(self.units)
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
def test_glob(self):
- self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
+ start_networkd()
- self.wait_online(['test1:degraded'])
+ wait_online(['test1:degraded'])
- output = subprocess.check_output(networkctl_cmd + ['list'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'list', env=env)
self.assertRegex(output, '1 lo ')
self.assertRegex(output, 'test1')
- output = subprocess.check_output(networkctl_cmd + ['list', 'test1'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'list', 'test1', env=env)
self.assertNotRegex(output, '1 lo ')
self.assertRegex(output, 'test1')
- output = subprocess.check_output(networkctl_cmd + ['list', 'te*'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'list', 'te*', env=env)
self.assertNotRegex(output, '1 lo ')
self.assertRegex(output, 'test1')
- output = subprocess.check_output(networkctl_cmd + ['status', 'te*'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'te*', env=env)
self.assertNotRegex(output, '1: lo ')
self.assertRegex(output, 'test1')
- output = subprocess.check_output(networkctl_cmd + ['status', 'tes[a-z][0-9]'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'tes[a-z][0-9]', env=env)
self.assertNotRegex(output, '1: lo ')
self.assertRegex(output, 'test1')
def test_mtu(self):
- self.copy_unit_to_networkd_unit_path('11-dummy-mtu.netdev', '11-dummy.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('11-dummy-mtu.netdev', '11-dummy.network')
+ start_networkd()
- self.wait_online(['test1:degraded'])
+ wait_online(['test1:degraded'])
- output = subprocess.check_output(networkctl_cmd + ['status', 'test1'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'test1', env=env)
self.assertRegex(output, 'MTU: 1600')
- @expectedFailureIfEthtoolDoesNotSupportDriver()
- def test_udev_driver(self):
- self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network',
- '25-veth.netdev', 'netdev-link-local-addressing-yes.network')
- self.start_networkd(0)
+ def test_type(self):
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
+ start_networkd()
+ wait_online(['test1:degraded'])
- self.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
+ output = check_output(*networkctl_cmd, 'status', 'test1')
+ print(output)
+ self.assertRegex(output, 'Type: ether')
- output = subprocess.check_output(networkctl_cmd + ['status', 'test1'], universal_newlines=True, env=env).rstrip()
- self.assertRegex(output, 'Driver: dummy')
+ output = check_output(*networkctl_cmd, 'status', 'lo')
+ print(output)
+ self.assertRegex(output, 'Type: loopback')
+
+ @expectedFailureIfLinkFileFieldIsNotSet()
+ def test_udev_link_file(self):
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network')
+ start_networkd()
+ wait_online(['test1:degraded'])
- output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip()
- self.assertRegex(output, 'Driver: veth')
+ output = check_output(*networkctl_cmd, 'status', 'test1')
+ print(output)
+ self.assertRegex(output, r'Link File: (?:/usr)/lib/systemd/network/99-default.link')
+ self.assertRegex(output, r'Network File: /run/systemd/network/11-dummy.network')
- output = subprocess.check_output(networkctl_cmd + ['status', 'veth-peer'], universal_newlines=True, env=env).rstrip()
- self.assertRegex(output, 'Driver: veth')
+ output = check_output(*networkctl_cmd, 'status', 'lo')
+ print(output)
+ self.assertRegex(output, r'Link File: (?:/usr)/lib/systemd/network/99-default.link')
+ self.assertRegex(output, r'Network File: n/a')
def test_delete_links(self):
- self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network',
- '25-veth.netdev', 'netdev-link-local-addressing-yes.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network',
+ '25-veth.netdev', 'netdev-link-local-addressing-yes.network')
+ start_networkd()
- self.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
+ wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded'])
- subprocess.check_call(networkctl_cmd + ['delete', 'test1', 'veth99'])
- self.assertFalse(self.link_exists('test1'))
- self.assertFalse(self.link_exists('veth99'))
- self.assertFalse(self.link_exists('veth-peer'))
+ check_output(*networkctl_cmd, 'delete', 'test1', 'veth99')
+ self.assertFalse(link_exists('test1'))
+ self.assertFalse(link_exists('veth99'))
+ self.assertFalse(link_exists('veth-peer'))
class NetworkdNetDevTests(unittest.TestCase, Utilities):
- links =[
+ links_remove_earlier = [
+ 'xfrm99',
+ ]
+
+ links = [
'6rdtun99',
'bond99',
'bridge99',
'gretun99',
'ip6gretap98',
'ip6gretap99',
+ 'ip6gretun96',
'ip6gretun97',
'ip6gretun98',
'ip6gretun99',
'vti6tun97',
'vti6tun98',
'vti6tun99',
+ 'vtitun96',
'vtitun97',
'vtitun98',
'vtitun99',
'vxcan99',
'vxlan99',
'wg98',
- 'wg99']
+ 'wg99',
+ ]
units = [
'10-dropin-test.netdev',
'11-dummy.netdev',
'11-dummy.network',
'12-dummy.netdev',
+ '13-not-match-udev-property.network',
+ '14-match-udev-property.network',
'15-name-conflict-test.netdev',
'21-macvlan.netdev',
'21-macvtap.netdev',
'25-bond.netdev',
'25-bond-balanced-tlb.netdev',
'25-bridge.netdev',
+ '25-bridge-configure-without-carrier.network',
'25-bridge.network',
'25-erspan-tunnel-local-any.netdev',
'25-erspan-tunnel.netdev',
'25-geneve.netdev',
'25-gretap-tunnel-local-any.netdev',
'25-gretap-tunnel.netdev',
+ '25-gre-tunnel-any-any.netdev',
'25-gre-tunnel-local-any.netdev',
'25-gre-tunnel-remote-any.netdev',
'25-gre-tunnel.netdev',
'25-ip6gretap-tunnel-local-any.netdev',
'25-ip6gretap-tunnel.netdev',
+ '25-ip6gre-tunnel-any-any.netdev',
'25-ip6gre-tunnel-local-any.netdev',
'25-ip6gre-tunnel-remote-any.netdev',
'25-ip6gre-tunnel.netdev',
- '25-ip6tnl-tunnel-remote-any.netdev',
+ '25-ip6tnl-tunnel-any-any.netdev',
'25-ip6tnl-tunnel-local-any.netdev',
+ '25-ip6tnl-tunnel-remote-any.netdev',
'25-ip6tnl-tunnel.netdev',
+ '25-ipip-tunnel-any-any.netdev',
'25-ipip-tunnel-independent.netdev',
+ '25-ipip-tunnel-independent-loopback.netdev',
'25-ipip-tunnel-local-any.netdev',
'25-ipip-tunnel-remote-any.netdev',
'25-ipip-tunnel.netdev',
'25-macsec.netdev',
'25-macsec.network',
'25-nlmon.netdev',
+ '25-sit-tunnel-any-any.netdev',
'25-sit-tunnel-local-any.netdev',
'25-sit-tunnel-remote-any.netdev',
'25-sit-tunnel.netdev',
'25-vcan.netdev',
'25-veth.netdev',
'25-vrf.netdev',
+ '25-vti6-tunnel-any-any.netdev',
'25-vti6-tunnel-local-any.netdev',
'25-vti6-tunnel-remote-any.netdev',
'25-vti6-tunnel.netdev',
+ '25-vti-tunnel-any-any.netdev',
'25-vti-tunnel-local-any.netdev',
'25-vti-tunnel-remote-any.netdev',
'25-vti-tunnel.netdev',
'25-wireguard-private-key.txt',
'25-wireguard.netdev',
'25-wireguard.network',
+ '25-xfrm.netdev',
+ '25-xfrm-independent.netdev',
'6rd.network',
'erspan.network',
'gre.network',
'vti6.network',
'vti.network',
'vxlan-test1.network',
- 'vxlan.network']
+ 'vxlan.network',
+ 'xfrm.network',
+ ]
fou_ports = [
'55555',
'55556']
def setUp(self):
- self.remove_fou_ports(self.fou_ports)
- self.remove_links(self.links)
+ remove_fou_ports(self.fou_ports)
+ remove_links(self.links_remove_earlier)
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
def tearDown(self):
- self.remove_fou_ports(self.fou_ports)
- self.remove_links(self.links)
- self.remove_unit_from_networkd_path(self.units)
+ remove_fou_ports(self.fou_ports)
+ remove_links(self.links_remove_earlier)
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
def test_dropin_and_name_conflict(self):
- self.copy_unit_to_networkd_unit_path('10-dropin-test.netdev', '15-name-conflict-test.netdev')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('10-dropin-test.netdev', '15-name-conflict-test.netdev')
+ start_networkd()
- self.wait_online(['dropin-test:off'])
+ wait_online(['dropin-test:off'])
- output = subprocess.check_output(['ip', 'link', 'show', 'dropin-test'], universal_newlines=True).rstrip()
+ output = check_output('ip link show dropin-test')
print(output)
self.assertRegex(output, '00:50:56:c0:00:28')
+ def test_match_udev_property(self):
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', '13-not-match-udev-property.network', '14-match-udev-property.network')
+ start_networkd()
+ wait_online(['dummy98:routable'])
+
+ output = check_output('networkctl status dummy98')
+ print(output)
+ self.assertRegex(output, 'Network File: /run/systemd/network/14-match-udev-property')
+
def test_wait_online_any(self):
- self.copy_unit_to_networkd_unit_path('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network')
+ start_networkd()
- self.wait_online(['bridge99', 'test1:degraded'], bool_any=True)
+ wait_online(['bridge99', 'test1:degraded'], bool_any=True)
self.check_operstate('bridge99', '(?:off|no-carrier)', setup_state='configuring')
self.check_operstate('test1', 'degraded')
def test_bridge(self):
- self.copy_unit_to_networkd_unit_path('25-bridge.netdev')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-bridge.netdev', '25-bridge-configure-without-carrier.network')
+ start_networkd()
- self.wait_online(['bridge99:off'])
+ wait_online(['bridge99:no-carrier'])
tick = os.sysconf('SC_CLK_TCK')
- self.assertEqual(9, round(float(self.read_link_attr('bridge99', 'bridge', 'hello_time')) / tick))
- self.assertEqual(9, round(float(self.read_link_attr('bridge99', 'bridge', 'max_age')) / tick))
- self.assertEqual(9, round(float(self.read_link_attr('bridge99', 'bridge','forward_delay')) / tick))
- self.assertEqual(9, round(float(self.read_link_attr('bridge99', 'bridge','ageing_time')) / tick))
- self.assertEqual(9, int(self.read_link_attr('bridge99', 'bridge','priority')))
- self.assertEqual(1, int(self.read_link_attr('bridge99', 'bridge','multicast_querier')))
- self.assertEqual(1, int(self.read_link_attr('bridge99', 'bridge','multicast_snooping')))
- self.assertEqual(1, int(self.read_link_attr('bridge99', 'bridge','stp_state')))
+ self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'hello_time')) / tick))
+ self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge', 'max_age')) / tick))
+ self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge','forward_delay')) / tick))
+ self.assertEqual(9, round(float(read_link_attr('bridge99', 'bridge','ageing_time')) / tick))
+ self.assertEqual(9, int(read_link_attr('bridge99', 'bridge','priority')))
+ self.assertEqual(1, int(read_link_attr('bridge99', 'bridge','multicast_querier')))
+ self.assertEqual(1, int(read_link_attr('bridge99', 'bridge','multicast_snooping')))
+ self.assertEqual(1, int(read_link_attr('bridge99', 'bridge','stp_state')))
def test_bond(self):
- self.copy_unit_to_networkd_unit_path('25-bond.netdev', '25-bond-balanced-tlb.netdev')
- self.start_networkd(0)
-
- self.wait_online(['bond99:off', 'bond98:off'])
-
- self.assertEqual('802.3ad 4', self.read_link_attr('bond99', 'bonding', 'mode'))
- self.assertEqual('layer3+4 1', self.read_link_attr('bond99', 'bonding', 'xmit_hash_policy'))
- self.assertEqual('1000', self.read_link_attr('bond99', 'bonding', 'miimon'))
- self.assertEqual('fast 1', self.read_link_attr('bond99', 'bonding', 'lacp_rate'))
- self.assertEqual('2000', self.read_link_attr('bond99', 'bonding', 'updelay'))
- self.assertEqual('2000', self.read_link_attr('bond99', 'bonding', 'downdelay'))
- self.assertEqual('4', self.read_link_attr('bond99', 'bonding', 'resend_igmp'))
- self.assertEqual('1', self.read_link_attr('bond99', 'bonding', 'min_links'))
- self.assertEqual('1218', self.read_link_attr('bond99', 'bonding', 'ad_actor_sys_prio'))
- self.assertEqual('811', self.read_link_attr('bond99', 'bonding', 'ad_user_port_key'))
- self.assertEqual('00:11:22:33:44:55', self.read_link_attr('bond99', 'bonding', 'ad_actor_system'))
-
- self.assertEqual('balance-tlb 5', self.read_link_attr('bond98', 'bonding', 'mode'))
- self.assertEqual('1', self.read_link_attr('bond98', 'bonding', 'tlb_dynamic_lb'))
+ copy_unit_to_networkd_unit_path('25-bond.netdev', '25-bond-balanced-tlb.netdev')
+ start_networkd()
+
+ wait_online(['bond99:off', 'bond98:off'])
+
+ self.assertEqual('802.3ad 4', read_link_attr('bond99', 'bonding', 'mode'))
+ self.assertEqual('layer3+4 1', read_link_attr('bond99', 'bonding', 'xmit_hash_policy'))
+ self.assertEqual('1000', read_link_attr('bond99', 'bonding', 'miimon'))
+ self.assertEqual('fast 1', read_link_attr('bond99', 'bonding', 'lacp_rate'))
+ self.assertEqual('2000', read_link_attr('bond99', 'bonding', 'updelay'))
+ self.assertEqual('2000', read_link_attr('bond99', 'bonding', 'downdelay'))
+ self.assertEqual('4', read_link_attr('bond99', 'bonding', 'resend_igmp'))
+ self.assertEqual('1', read_link_attr('bond99', 'bonding', 'min_links'))
+ self.assertEqual('1218', read_link_attr('bond99', 'bonding', 'ad_actor_sys_prio'))
+ self.assertEqual('811', read_link_attr('bond99', 'bonding', 'ad_user_port_key'))
+ self.assertEqual('00:11:22:33:44:55', read_link_attr('bond99', 'bonding', 'ad_actor_system'))
+
+ self.assertEqual('balance-tlb 5', read_link_attr('bond98', 'bonding', 'mode'))
+ self.assertEqual('1', read_link_attr('bond98', 'bonding', 'tlb_dynamic_lb'))
def test_vlan(self):
- self.copy_unit_to_networkd_unit_path('21-vlan.netdev', '11-dummy.netdev',
- '21-vlan.network', '21-vlan-test1.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('21-vlan.netdev', '11-dummy.netdev',
+ '21-vlan.network', '21-vlan-test1.network')
+ start_networkd()
- self.wait_online(['test1:degraded', 'vlan99:routable'])
+ wait_online(['test1:degraded', 'vlan99:routable'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show test1')
print(output)
self.assertRegex(output, ' mtu 2000 ')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vlan99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show vlan99')
print(output)
self.assertRegex(output, ' mtu 2000 ')
self.assertRegex(output, 'REORDER_HDR')
self.assertRegex(output, 'MVRP')
self.assertRegex(output, ' id 99 ')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'test1'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev test1')
print(output)
self.assertRegex(output, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1')
self.assertRegex(output, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'vlan99'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev vlan99')
print(output)
self.assertRegex(output, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99')
with self.subTest(mode=mode):
if mode != 'private':
self.tearDown()
- self.copy_unit_to_networkd_unit_path('21-macvtap.netdev', 'netdev-link-local-addressing-yes.network',
- '11-dummy.netdev', 'macvtap.network')
+ copy_unit_to_networkd_unit_path('21-macvtap.netdev', 'netdev-link-local-addressing-yes.network',
+ '11-dummy.netdev', 'macvtap.network')
with open(os.path.join(network_unit_file_path, '21-macvtap.netdev'), mode='a') as f:
f.write('[MACVTAP]\nMode=' + mode)
- self.start_networkd(0)
+ start_networkd()
- self.wait_online(['macvtap99:degraded', 'test1:degraded'])
+ wait_online(['macvtap99:degraded', 'test1:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'macvtap99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show macvtap99')
print(output)
self.assertRegex(output, 'macvtap mode ' + mode + ' ')
with self.subTest(mode=mode):
if mode != 'private':
self.tearDown()
- self.copy_unit_to_networkd_unit_path('21-macvlan.netdev', 'netdev-link-local-addressing-yes.network',
- '11-dummy.netdev', 'macvlan.network')
+ copy_unit_to_networkd_unit_path('21-macvlan.netdev', 'netdev-link-local-addressing-yes.network',
+ '11-dummy.netdev', 'macvlan.network')
with open(os.path.join(network_unit_file_path, '21-macvlan.netdev'), mode='a') as f:
f.write('[MACVLAN]\nMode=' + mode)
- self.start_networkd(0)
+ start_networkd()
- self.wait_online(['macvlan99:degraded', 'test1:degraded'])
+ wait_online(['macvlan99:degraded', 'test1:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show test1')
print(output)
self.assertRegex(output, ' mtu 2000 ')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'macvlan99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show macvlan99')
print(output)
self.assertRegex(output, ' mtu 2000 ')
self.assertRegex(output, 'macvlan mode ' + mode + ' ')
with self.subTest(mode=mode, flag=flag):
if mode != 'L2':
self.tearDown()
- self.copy_unit_to_networkd_unit_path('25-ipvlan.netdev', 'netdev-link-local-addressing-yes.network',
- '11-dummy.netdev', 'ipvlan.network')
+ copy_unit_to_networkd_unit_path('25-ipvlan.netdev', 'netdev-link-local-addressing-yes.network',
+ '11-dummy.netdev', 'ipvlan.network')
with open(os.path.join(network_unit_file_path, '25-ipvlan.netdev'), mode='a') as f:
f.write('[IPVLAN]\nMode=' + mode + '\nFlags=' + flag)
- self.start_networkd(0)
- self.wait_online(['ipvlan99:degraded', 'test1:degraded'])
+ start_networkd()
+ wait_online(['ipvlan99:degraded', 'test1:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipvlan99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ipvlan99')
print(output)
self.assertRegex(output, 'ipvlan *mode ' + mode.lower() + ' ' + flag)
with self.subTest(mode=mode, flag=flag):
if mode != 'L2':
self.tearDown()
- self.copy_unit_to_networkd_unit_path('25-ipvtap.netdev', 'netdev-link-local-addressing-yes.network',
- '11-dummy.netdev', 'ipvtap.network')
+ copy_unit_to_networkd_unit_path('25-ipvtap.netdev', 'netdev-link-local-addressing-yes.network',
+ '11-dummy.netdev', 'ipvtap.network')
with open(os.path.join(network_unit_file_path, '25-ipvtap.netdev'), mode='a') as f:
f.write('[IPVTAP]\nMode=' + mode + '\nFlags=' + flag)
- self.start_networkd(0)
- self.wait_online(['ipvtap99:degraded', 'test1:degraded'])
+ start_networkd()
+ wait_online(['ipvtap99:degraded', 'test1:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipvtap99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ipvtap99')
print(output)
self.assertRegex(output, 'ipvtap *mode ' + mode.lower() + ' ' + flag)
def test_veth(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'netdev-link-local-addressing-yes.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'netdev-link-local-addressing-yes.network')
+ start_networkd()
- self.wait_online(['veth99:degraded', 'veth-peer:degraded'])
+ wait_online(['veth99:degraded', 'veth-peer:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show veth99')
print(output)
self.assertRegex(output, 'link/ether 12:34:56:78:9a:bc')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'veth-peer'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show veth-peer')
print(output)
self.assertRegex(output, 'link/ether 12:34:56:78:9a:bd')
def test_tun(self):
- self.copy_unit_to_networkd_unit_path('25-tun.netdev')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-tun.netdev')
+ start_networkd()
- self.wait_online(['tun99:off'])
+ wait_online(['tun99:off'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'tun99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show tun99')
print(output)
# Old ip command does not support IFF_ flags
self.assertRegex(output, 'tun (?:type tun pi on vnet_hdr on multi_queue|addrgenmode) ')
def test_tap(self):
- self.copy_unit_to_networkd_unit_path('25-tap.netdev')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-tap.netdev')
+ start_networkd()
- self.wait_online(['tap99:off'])
+ wait_online(['tap99:off'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'tap99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show tap99')
print(output)
# Old ip command does not support IFF_ flags
self.assertRegex(output, 'tun (?:type tap pi on vnet_hdr on multi_queue|addrgenmode) ')
@expectedFailureIfModuleIsNotAvailable('vrf')
def test_vrf(self):
- self.copy_unit_to_networkd_unit_path('25-vrf.netdev', 'netdev-link-local-addressing-yes.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-vrf.netdev', 'netdev-link-local-addressing-yes.network')
+ start_networkd()
- self.wait_online(['vrf99:carrier'])
+ wait_online(['vrf99:carrier'])
@expectedFailureIfModuleIsNotAvailable('vcan')
def test_vcan(self):
- self.copy_unit_to_networkd_unit_path('25-vcan.netdev', 'netdev-link-local-addressing-yes.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-vcan.netdev', 'netdev-link-local-addressing-yes.network')
+ start_networkd()
- self.wait_online(['vcan99:carrier'])
+ wait_online(['vcan99:carrier'])
@expectedFailureIfModuleIsNotAvailable('vxcan')
def test_vxcan(self):
- self.copy_unit_to_networkd_unit_path('25-vxcan.netdev', 'netdev-link-local-addressing-yes.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-vxcan.netdev', 'netdev-link-local-addressing-yes.network')
+ start_networkd()
- self.wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
+ wait_online(['vxcan99:carrier', 'vxcan-peer:carrier'])
@expectedFailureIfModuleIsNotAvailable('wireguard')
def test_wireguard(self):
- self.copy_unit_to_networkd_unit_path('25-wireguard.netdev', '25-wireguard.network',
- '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
- '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt')
- self.start_networkd(0)
- self.wait_online(['wg99:carrier', 'wg98:routable'])
+ copy_unit_to_networkd_unit_path('25-wireguard.netdev', '25-wireguard.network',
+ '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network',
+ '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt')
+ start_networkd()
+ wait_online(['wg99:carrier', 'wg98:routable'])
if shutil.which('wg'):
- subprocess.call('wg')
+ call('wg')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'listen-port'], universal_newlines=True).rstrip()
+ output = check_output('wg show wg99 listen-port')
self.assertRegex(output, '51820')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'fwmark'], universal_newlines=True).rstrip()
+ output = check_output('wg show wg99 fwmark')
self.assertRegex(output, '0x4d2')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'allowed-ips'], universal_newlines=True).rstrip()
- self.assertRegex(output, 'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48')
- self.assertRegex(output, 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'persistent-keepalive'], universal_newlines=True).rstrip()
- self.assertRegex(output, 'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t20')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'endpoints'], universal_newlines=True).rstrip()
- self.assertRegex(output, 'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t192.168.27.3:51820')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'private-key'], universal_newlines=True).rstrip()
- self.assertRegex(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS\+UENyPncC4bF46ong=')
- output = subprocess.check_output(['wg', 'show', 'wg99', 'preshared-keys'], universal_newlines=True).rstrip()
- self.assertRegex(output, 'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA= IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=')
- self.assertRegex(output, 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= cPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=')
-
- output = subprocess.check_output(['wg', 'show', 'wg98', 'private-key'], universal_newlines=True).rstrip()
- self.assertRegex(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr\+WHtZLZ90FU=')
+ output = check_output('wg show wg99 allowed-ips')
+ self.assertRegex(output, r'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48')
+ self.assertRegex(output, r'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128')
+ output = check_output('wg show wg99 persistent-keepalive')
+ self.assertRegex(output, r'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t20')
+ output = check_output('wg show wg99 endpoints')
+ self.assertRegex(output, r'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA=\t192.168.27.3:51820')
+ output = check_output('wg show wg99 private-key')
+ self.assertRegex(output, r'EEGlnEPYJV//kbvvIqxKkQwOiS\+UENyPncC4bF46ong=')
+ output = check_output('wg show wg99 preshared-keys')
+ self.assertRegex(output, r'RDf\+LSpeEre7YEIKaxg\+wbpsNV7du\+ktR99uBEtIiCA= IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=')
+ self.assertRegex(output, r'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= cPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=')
+
+ output = check_output('wg show wg98 private-key')
+ self.assertRegex(output, r'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr\+WHtZLZ90FU=')
def test_geneve(self):
- self.copy_unit_to_networkd_unit_path('25-geneve.netdev', 'netdev-link-local-addressing-yes.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-geneve.netdev', 'netdev-link-local-addressing-yes.network')
+ start_networkd()
- self.wait_online(['geneve99:degraded'])
+ wait_online(['geneve99:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'geneve99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show geneve99')
print(output)
self.assertRegex(output, '192.168.22.1')
self.assertRegex(output, '6082')
self.assertRegex(output, 'udp6zerocsumrx')
def test_ipip_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ipip.network',
- '25-ipip-tunnel.netdev', '25-tunnel.network',
- '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
- '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
- self.start_networkd(0)
- self.wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'dummy98:degraded'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ipip.network',
+ '25-ipip-tunnel.netdev', '25-tunnel.network',
+ '25-ipip-tunnel-local-any.netdev', '25-tunnel-local-any.network',
+ '25-ipip-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
+ '25-ipip-tunnel-any-any.netdev', '25-tunnel-any-any.network')
+ start_networkd()
+ wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97:routable', 'ipiptun96:routable', 'dummy98:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ipiptun99')
print(output)
self.assertRegex(output, 'ipip (?:ipip |)remote 192.169.224.239 local 192.168.223.238 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ipiptun98')
print(output)
self.assertRegex(output, 'ipip (?:ipip |)remote 192.169.224.239 local any dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun97'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ipiptun97')
print(output)
self.assertRegex(output, 'ipip (?:ipip |)remote any local 192.168.223.238 dev dummy98')
+ output = check_output('ip -d link show ipiptun96')
+ print(output)
+ self.assertRegex(output, 'ipip (?:ipip |)remote any local any dev dummy98')
def test_gre_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'gretun.network',
- '25-gre-tunnel.netdev', '25-tunnel.network',
- '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
- '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
- self.start_networkd(0)
- self.wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'dummy98:degraded'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'gretun.network',
+ '25-gre-tunnel.netdev', '25-tunnel.network',
+ '25-gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
+ '25-gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
+ '25-gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
+ start_networkd()
+ wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97:routable', 'gretun96:routable', 'dummy98:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show gretun99')
print(output)
self.assertRegex(output, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98')
self.assertRegex(output, 'ikey 1.2.3.103')
self.assertRegex(output, 'okey 1.2.4.103')
self.assertRegex(output, 'iseq')
self.assertRegex(output, 'oseq')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show gretun98')
print(output)
self.assertRegex(output, 'gre remote 10.65.223.239 local any dev dummy98')
self.assertRegex(output, 'ikey 0.0.0.104')
self.assertRegex(output, 'okey 0.0.0.104')
self.assertNotRegex(output, 'iseq')
self.assertNotRegex(output, 'oseq')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun97'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show gretun97')
print(output)
self.assertRegex(output, 'gre remote any local 10.65.223.238 dev dummy98')
self.assertRegex(output, 'ikey 0.0.0.105')
self.assertRegex(output, 'okey 0.0.0.105')
self.assertNotRegex(output, 'iseq')
self.assertNotRegex(output, 'oseq')
+ output = check_output('ip -d link show gretun96')
+ print(output)
+ self.assertRegex(output, 'gre remote any local any dev dummy98')
+ self.assertRegex(output, 'ikey 0.0.0.106')
+ self.assertRegex(output, 'okey 0.0.0.106')
+ self.assertNotRegex(output, 'iseq')
+ self.assertNotRegex(output, 'oseq')
def test_ip6gre_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6gretun.network',
- '25-ip6gre-tunnel.netdev', '25-tunnel.network',
- '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
- '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
- self.start_networkd()
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6gretun.network',
+ '25-ip6gre-tunnel.netdev', '25-tunnel.network',
+ '25-ip6gre-tunnel-local-any.netdev', '25-tunnel-local-any.network',
+ '25-ip6gre-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
+ '25-ip6gre-tunnel-any-any.netdev', '25-tunnel-any-any.network')
+ start_networkd(5)
# Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here.
self.check_link_exists('ip6gretun99')
self.check_link_exists('ip6gretun98')
self.check_link_exists('ip6gretun97')
+ self.check_link_exists('ip6gretun96')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ip6gretun99')
print(output)
self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ip6gretun98')
print(output)
self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun97'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ip6gretun97')
print(output)
self.assertRegex(output, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98')
+ output = check_output('ip -d link show ip6gretun96')
+ print(output)
+ self.assertRegex(output, 'ip6gre remote any local any dev dummy98')
def test_gretap_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'gretap.network',
- '25-gretap-tunnel.netdev', '25-tunnel.network',
- '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
- self.start_networkd(0)
- self.wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'gretap.network',
+ '25-gretap-tunnel.netdev', '25-tunnel.network',
+ '25-gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
+ start_networkd()
+ wait_online(['gretap99:routable', 'gretap98:routable', 'dummy98:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show gretap99')
print(output)
self.assertRegex(output, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98')
self.assertRegex(output, 'ikey 0.0.0.106')
self.assertRegex(output, 'okey 0.0.0.106')
self.assertRegex(output, 'iseq')
self.assertRegex(output, 'oseq')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show gretap98')
print(output)
self.assertRegex(output, 'gretap remote 10.65.223.239 local any dev dummy98')
self.assertRegex(output, 'ikey 0.0.0.107')
self.assertRegex(output, 'oseq')
def test_ip6gretap_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6gretap.network',
- '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
- '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
- self.start_networkd(0)
- self.wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6gretap.network',
+ '25-ip6gretap-tunnel.netdev', '25-tunnel.network',
+ '25-ip6gretap-tunnel-local-any.netdev', '25-tunnel-local-any.network')
+ start_networkd()
+ wait_online(['ip6gretap99:routable', 'ip6gretap98:routable', 'dummy98:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretap99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ip6gretap99')
print(output)
self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretap98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ip6gretap98')
print(output)
self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98')
def test_vti_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'vti.network',
- '25-vti-tunnel.netdev', '25-tunnel.network',
- '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
- '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
- self.start_networkd(0)
- self.wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'dummy98:degraded'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'vti.network',
+ '25-vti-tunnel.netdev', '25-tunnel.network',
+ '25-vti-tunnel-local-any.netdev', '25-tunnel-local-any.network',
+ '25-vti-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
+ '25-vti-tunnel-any-any.netdev', '25-tunnel-any-any.network')
+ start_networkd()
+ wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97:routable', 'vtitun96:routable', 'dummy98:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show vtitun99')
print(output)
self.assertRegex(output, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show vtitun98')
print(output)
self.assertRegex(output, 'vti remote 10.65.223.239 local any dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun97'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show vtitun97')
print(output)
self.assertRegex(output, 'vti remote any local 10.65.223.238 dev dummy98')
+ output = check_output('ip -d link show vtitun96')
+ print(output)
+ self.assertRegex(output, 'vti remote any local any dev dummy98')
def test_vti6_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'vti6.network',
- '25-vti6-tunnel.netdev', '25-tunnel.network',
- '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
- '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
- self.start_networkd(0)
- self.wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'vti6.network',
+ '25-vti6-tunnel.netdev', '25-tunnel.network',
+ '25-vti6-tunnel-local-any.netdev', '25-tunnel-local-any.network',
+ '25-vti6-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
+ start_networkd()
+ wait_online(['vti6tun99:routable', 'vti6tun98:routable', 'vti6tun97:routable', 'dummy98:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show vti6tun99')
print(output)
self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show vti6tun98')
print(output)
self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local (?:any|::) dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun97'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show vti6tun97')
print(output)
self.assertRegex(output, 'vti6 remote (?:any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
def test_ip6tnl_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6tnl.network',
- '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
- '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
- '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
- self.start_networkd(0)
- self.wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', 'dummy98:degraded'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'ip6tnl.network',
+ '25-ip6tnl-tunnel.netdev', '25-tunnel.network',
+ '25-ip6tnl-tunnel-local-any.netdev', '25-tunnel-local-any.network',
+ '25-ip6tnl-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
+ start_networkd()
+ wait_online(['ip6tnl99:routable', 'ip6tnl98:routable', 'ip6tnl97:routable', 'dummy98:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ip6tnl99')
print(output)
self.assertRegex(output, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ip6tnl98')
print(output)
self.assertRegex(output, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (?:any|::) dev dummy98')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl97'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ip6tnl97')
print(output)
self.assertRegex(output, 'ip6tnl ip6ip6 remote (?:any|::) local 2a00:ffde:4567:edde::4987 dev dummy98')
def test_sit_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'sit.network',
- '25-sit-tunnel.netdev', '25-tunnel.network',
- '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
- '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network')
- self.start_networkd(0)
- self.wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'dummy98:degraded'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'sit.network',
+ '25-sit-tunnel.netdev', '25-tunnel.network',
+ '25-sit-tunnel-local-any.netdev', '25-tunnel-local-any.network',
+ '25-sit-tunnel-remote-any.netdev', '25-tunnel-remote-any.network',
+ '25-sit-tunnel-any-any.netdev', '25-tunnel-any-any.network')
+ start_networkd()
+ wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97:routable', 'sittun96:routable', 'dummy98:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show sittun99')
print(output)
self.assertRegex(output, "sit (?:ip6ip |)remote 10.65.223.239 local 10.65.223.238 dev dummy98")
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show sittun98')
print(output)
self.assertRegex(output, "sit (?:ip6ip |)remote 10.65.223.239 local any dev dummy98")
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun97'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show sittun97')
print(output)
self.assertRegex(output, "sit (?:ip6ip |)remote any local 10.65.223.238 dev dummy98")
+ output = check_output('ip -d link show sittun96')
+ print(output)
+ self.assertRegex(output, "sit (?:ip6ip |)remote any local any dev dummy98")
def test_isatap_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'isatap.network',
- '25-isatap-tunnel.netdev', '25-tunnel.network')
- self.start_networkd(0)
- self.wait_online(['isataptun99:routable', 'dummy98:degraded'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'isatap.network',
+ '25-isatap-tunnel.netdev', '25-tunnel.network')
+ start_networkd()
+ wait_online(['isataptun99:routable', 'dummy98:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'isataptun99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show isataptun99')
print(output)
self.assertRegex(output, "isatap ")
def test_6rd_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '6rd.network',
- '25-6rd-tunnel.netdev', '25-tunnel.network')
- self.start_networkd(0)
- self.wait_online(['sittun99:routable', 'dummy98:degraded'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', '6rd.network',
+ '25-6rd-tunnel.netdev', '25-tunnel.network')
+ start_networkd()
+ wait_online(['sittun99:routable', 'dummy98:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show sittun99')
print(output)
self.assertRegex(output, '6rd-prefix 2602::/24')
@expectedFailureIfERSPANModuleIsNotAvailable()
def test_erspan_tunnel(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'erspan.network',
- '25-erspan-tunnel.netdev', '25-tunnel.network',
- '25-erspan-tunnel-local-any.netdev', '25-tunnel-local-any.network')
- self.start_networkd(0)
- self.wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'erspan.network',
+ '25-erspan-tunnel.netdev', '25-tunnel.network',
+ '25-erspan-tunnel-local-any.netdev', '25-tunnel-local-any.network')
+ start_networkd()
+ wait_online(['erspan99:routable', 'erspan98:routable', 'dummy98:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'erspan99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show erspan99')
print(output)
self.assertRegex(output, 'erspan remote 172.16.1.100 local 172.16.1.200')
self.assertRegex(output, 'ikey 0.0.0.101')
self.assertRegex(output, 'okey 0.0.0.101')
self.assertRegex(output, 'iseq')
self.assertRegex(output, 'oseq')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'erspan98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show erspan98')
print(output)
self.assertRegex(output, 'erspan remote 172.16.1.100 local any')
self.assertRegex(output, '102')
self.assertRegex(output, 'oseq')
def test_tunnel_independent(self):
- self.copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent.netdev', 'netdev-link-local-addressing-yes.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent.netdev', 'netdev-link-local-addressing-yes.network')
+ start_networkd()
+
+ wait_online(['ipiptun99:carrier'])
- self.wait_online(['ipiptun99:carrier'])
+ def test_tunnel_independent_loopback(self):
+ copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent-loopback.netdev', 'netdev-link-local-addressing-yes.network')
+ start_networkd()
+
+ wait_online(['ipiptun99:carrier'])
+
+ @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
+ def test_xfrm(self):
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', 'xfrm.network',
+ '25-xfrm.netdev', 'netdev-link-local-addressing-yes.network')
+ start_networkd()
+
+ wait_online(['xfrm99:degraded', 'dummy98:degraded'])
+
+ output = check_output('ip link show dev xfrm99')
+ print(output)
+
+ @expectedFailureIfModuleIsNotAvailable('xfrm_interface')
+ def test_xfrm_independent(self):
+ copy_unit_to_networkd_unit_path('25-xfrm-independent.netdev', 'netdev-link-local-addressing-yes.network')
+ start_networkd()
+
+ wait_online(['xfrm99:degraded'])
@expectedFailureIfModuleIsNotAvailable('fou')
def test_fou(self):
# Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated.
self.assertTrue(is_module_available('fou'))
- self.copy_unit_to_networkd_unit_path('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
- '25-fou-ipip.netdev', '25-fou-sit.netdev',
- '25-fou-gre.netdev', '25-fou-gretap.netdev')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev',
+ '25-fou-ipip.netdev', '25-fou-sit.netdev',
+ '25-fou-gre.netdev', '25-fou-gretap.netdev')
+ start_networkd()
- self.wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'])
+ wait_online(['ipiptun96:off', 'sittun96:off', 'gretun96:off', 'gretap96:off'])
- output = subprocess.check_output(['ip', 'fou', 'show'], universal_newlines=True).rstrip()
+ output = check_output('ip fou show')
print(output)
self.assertRegex(output, 'port 55555 ipproto 4')
self.assertRegex(output, 'port 55556 ipproto 47')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun96'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show ipiptun96')
print(output)
self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun96'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show sittun96')
print(output)
self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun96'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show gretun96')
print(output)
self.assertRegex(output, 'encap fou encap-sport 1001 encap-dport 55556')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap96'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show gretap96')
print(output)
self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55556')
def test_vxlan(self):
- self.copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network',
- '11-dummy.netdev', 'vxlan-test1.network')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network',
+ '11-dummy.netdev', 'vxlan-test1.network')
+ start_networkd()
- self.wait_online(['test1:degraded', 'vxlan99:degraded'])
+ wait_online(['test1:degraded', 'vxlan99:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vxlan99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show vxlan99')
print(output)
self.assertRegex(output, '999')
self.assertRegex(output, '5555')
self.assertRegex(output, 'remcsumrx')
self.assertRegex(output, 'gbp')
- output = subprocess.check_output(['bridge', 'fdb', 'show', 'dev', 'vxlan99'], universal_newlines=True).rstrip()
+ output = check_output('bridge fdb show dev vxlan99')
print(output)
self.assertRegex(output, '00:11:22:33:44:55 dst 10.0.0.5 self permanent')
self.assertRegex(output, '00:11:22:33:44:66 dst 10.0.0.6 self permanent')
self.assertRegex(output, '00:11:22:33:44:77 dst 10.0.0.7 self permanent')
def test_macsec(self):
- self.copy_unit_to_networkd_unit_path('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
- 'macsec.network', '12-dummy.netdev')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-macsec.netdev', '25-macsec.network', '25-macsec.key',
+ 'macsec.network', '12-dummy.netdev')
+ start_networkd()
- self.wait_online(['dummy98:degraded', 'macsec99:routable'])
+ wait_online(['dummy98:degraded', 'macsec99:routable'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'macsec99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show macsec99')
print(output)
self.assertRegex(output, 'macsec99@dummy98')
self.assertRegex(output, 'macsec sci [0-9a-f]*000b')
self.assertRegex(output, 'encrypt on')
- output = subprocess.check_output(['ip', 'macsec', 'show', 'macsec99'], universal_newlines=True).rstrip()
+ output = check_output('ip macsec show macsec99')
print(output)
self.assertRegex(output, 'encrypt on')
self.assertRegex(output, 'TXSC: [0-9a-f]*000b on SA 1')
self.assertRegex(output, '0: PN [0-9]*, state off, key 02030400000000000000000000000000')
def test_nlmon(self):
- self.copy_unit_to_networkd_unit_path('25-nlmon.netdev', 'netdev-link-local-addressing-yes.network')
- self.start_networkd()
+ copy_unit_to_networkd_unit_path('25-nlmon.netdev', 'netdev-link-local-addressing-yes.network')
+ start_networkd()
- self.wait_online(['nlmon99:carrier'])
+ wait_online(['nlmon99:carrier'])
class NetworkdL2TPTests(unittest.TestCase, Utilities):
l2tp_tunnel_ids = [ '10' ]
def setUp(self):
- self.l2tp_tunnel_remove(self.l2tp_tunnel_ids)
- self.remove_links(self.links)
+ remove_l2tp_tunnels(self.l2tp_tunnel_ids)
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
def tearDown(self):
- self.l2tp_tunnel_remove(self.l2tp_tunnel_ids)
- self.remove_links(self.links)
- self.remove_unit_from_networkd_path(self.units)
+ remove_l2tp_tunnels(self.l2tp_tunnel_ids)
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
@expectedFailureIfModuleIsNotAvailable('l2tp_eth')
def test_l2tp_udp(self):
- self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network', '25-l2tp-udp.netdev')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network', '25-l2tp-udp.netdev')
+ start_networkd()
- self.wait_online(['test1:routable', 'l2tp-ses1:off', 'l2tp-ses2:off'])
+ wait_online(['test1:routable', 'l2tp-ses1:off', 'l2tp-ses2:off'])
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel', 'tunnel_id', '10'], universal_newlines=True).rstrip()
+ output = check_output('ip l2tp show tunnel tunnel_id 10')
print(output)
self.assertRegex(output, "Tunnel 10, encap UDP")
self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
self.assertRegex(output, "UDP source / dest ports: 3000/4000")
self.assertRegex(output, "UDP checksum: enabled")
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '15'], universal_newlines=True).rstrip()
+ output = check_output('ip l2tp show session tid 10 session_id 15')
print(output)
self.assertRegex(output, "Session 15 in tunnel 10")
self.assertRegex(output, "Peer session 16, tunnel 11")
self.assertRegex(output, "interface name: l2tp-ses1")
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '17'], universal_newlines=True).rstrip()
+ output = check_output('ip l2tp show session tid 10 session_id 17')
print(output)
self.assertRegex(output, "Session 17 in tunnel 10")
self.assertRegex(output, "Peer session 18, tunnel 11")
@expectedFailureIfModuleIsNotAvailable('l2tp_ip')
def test_l2tp_ip(self):
- self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network', '25-l2tp-ip.netdev')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network', '25-l2tp-ip.netdev')
+ start_networkd()
- self.wait_online(['test1:routable', 'l2tp-ses3:off', 'l2tp-ses4:off'])
+ wait_online(['test1:routable', 'l2tp-ses3:off', 'l2tp-ses4:off'])
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel', 'tunnel_id', '10'], universal_newlines=True).rstrip()
+ output = check_output('ip l2tp show tunnel tunnel_id 10')
print(output)
self.assertRegex(output, "Tunnel 10, encap IP")
self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101")
self.assertRegex(output, "Peer tunnel 12")
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '25'], universal_newlines=True).rstrip()
+ output = check_output('ip l2tp show session tid 10 session_id 25')
print(output)
self.assertRegex(output, "Session 25 in tunnel 10")
self.assertRegex(output, "Peer session 26, tunnel 12")
self.assertRegex(output, "interface name: l2tp-ses3")
- output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '27'], universal_newlines=True).rstrip()
+ output = check_output('ip l2tp show session tid 10 session_id 27')
print(output)
self.assertRegex(output, "Session 27 in tunnel 10")
self.assertRegex(output, "Peer session 28, tunnel 12")
'11-dummy.netdev',
'12-dummy.netdev',
'23-active-slave.network',
+ '24-keep-configuration-static.network',
'24-search-domain.network',
'25-address-link-section.network',
'25-address-preferred-lifetime-zero-ipv6.network',
routes = [['blackhole', '202.54.1.2'], ['unreachable', '202.54.1.3'], ['prohibit', '202.54.1.4']]
def setUp(self):
- self.remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
- self.remove_routes(self.routes)
- self.remove_links(self.links)
+ remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
+ remove_routes(self.routes)
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
def tearDown(self):
- self.remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
- self.remove_routes(self.routes)
- self.remove_links(self.links)
- self.remove_unit_from_networkd_path(self.units)
+ remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
+ remove_routes(self.routes)
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
def test_address_static(self):
- self.copy_unit_to_networkd_unit_path('25-address-static.network', '12-dummy.netdev')
- self.start_networkd(0)
+ copy_unit_to_networkd_unit_path('25-address-static.network', '12-dummy.netdev')
+ start_networkd()
- self.wait_online(['dummy98:routable'])
+ wait_online(['dummy98:routable'])
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev dummy98')
print(output)
self.assertRegex(output, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98')
self.assertRegex(output, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98')
self.assertNotRegex(output, '10.10.0.1/16')
self.assertNotRegex(output, '10.10.0.2/16')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '32'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev dummy98 label 32')
self.assertRegex(output, 'inet 10.3.2.3/16 brd 10.3.255.255 scope global 32')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '33'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev dummy98 label 33')
self.assertRegex(output, 'inet 10.4.2.3 peer 10.4.2.4/16 scope global 33')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '34'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev dummy98 label 34')
self.assertRegex(output, 'inet 192.168.[0-9]*.1/24 brd 192.168.[0-9]*.255 scope global 34')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '35'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev dummy98 label 35')
self.assertRegex(output, 'inet 172.[0-9]*.0.1/16 brd 172.[0-9]*.255.255 scope global 35')
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 address show dev dummy98')
print(output)
self.assertRegex(output, 'inet6 2001:db8:0:f101::15/64 scope global')
self.assertRegex(output, 'inet6 2001:db8:0:f101::16/64 scope global')
self.assertRegex(output, 'inet6 fd[0-9a-f:]*1/64 scope global')
def test_address_preferred_lifetime_zero_ipv6(self):
- self.copy_unit_to_networkd_unit_path('25-address-preferred-lifetime-zero-ipv6.network', '12-dummy.netdev')
- self.start_networkd()
+ copy_unit_to_networkd_unit_path('25-address-preferred-lifetime-zero-ipv6.network', '12-dummy.netdev')
+ start_networkd(5)
self.check_link_exists('dummy98')
-
self.check_operstate('dummy98', 'routable', setup_state='configuring')
- output = subprocess.check_output(['ip', 'address', 'show', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip address show dummy98')
print(output)
self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope link deprecated dummy98')
self.assertRegex(output, 'inet6 2001:db8:0:f101::1/64 scope global')
def test_configure_without_carrier(self):
- self.copy_unit_to_networkd_unit_path('configure-without-carrier.network', '11-dummy.netdev')
- self.start_networkd()
-
- self.check_link_exists('test1')
+ copy_unit_to_networkd_unit_path('configure-without-carrier.network', '11-dummy.netdev')
+ start_networkd()
+ wait_online(['test1:routable'])
- output = subprocess.check_output(networkctl_cmd + ['status', 'test1'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'test1')
print(output)
self.assertRegex(output, '192.168.0.15')
self.assertRegex(output, '192.168.0.1')
self.assertRegex(output, 'routable')
def test_routing_policy_rule(self):
- self.copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev')
-
- self.start_networkd()
-
- self.check_link_exists('test1')
+ copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev')
+ start_networkd()
+ wait_online(['test1:degraded'])
- output = subprocess.check_output(['ip', 'rule'], universal_newlines=True).rstrip()
+ output = check_output('ip rule')
print(output)
self.assertRegex(output, '111')
self.assertRegex(output, 'from 192.168.100.18')
self.assertRegex(output, 'lookup 7')
def test_routing_policy_rule_issue_11280(self):
- self.copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev',
- 'routing-policy-rule-dummy98.network', '12-dummy.netdev')
+ copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev',
+ 'routing-policy-rule-dummy98.network', '12-dummy.netdev')
for trial in range(3):
# Remove state files only first time
- self.start_networkd(remove_state_files=(trial == 0))
+ start_networkd()
+ wait_online(['test1:degraded', 'dummy98:degraded'])
+ time.sleep(1)
- self.check_link_exists('test1')
- self.check_link_exists('dummy98')
-
- output = subprocess.check_output(['ip', 'rule', 'list', 'table', '7'], universal_newlines=True).rstrip()
+ output = check_output('ip rule list table 7')
print(output)
self.assertRegex(output, '111: from 192.168.100.18 tos (?:0x08|throughput) iif test1 oif test1 lookup 7')
- output = subprocess.check_output(['ip', 'rule', 'list', 'table', '8'], universal_newlines=True).rstrip()
+ output = check_output('ip rule list table 8')
print(output)
self.assertRegex(output, '112: from 192.168.101.18 tos (?:0x08|throughput) iif dummy98 oif dummy98 lookup 8')
+ stop_networkd(remove_state_files=False)
+
@expectedFailureIfRoutingPolicyPortRangeIsNotAvailable()
def test_routing_policy_rule_port_range(self):
- self.copy_unit_to_networkd_unit_path('25-fibrule-port-range.network', '11-dummy.netdev')
-
- self.start_networkd()
+ copy_unit_to_networkd_unit_path('25-fibrule-port-range.network', '11-dummy.netdev')
+ start_networkd()
+ wait_online(['test1:degraded'])
- self.check_link_exists('test1')
-
- output = subprocess.check_output(['ip', 'rule'], universal_newlines=True).rstrip()
+ output = check_output('ip rule')
print(output)
self.assertRegex(output, '111')
self.assertRegex(output, 'from 192.168.100.18')
@expectedFailureIfRoutingPolicyIPProtoIsNotAvailable()
def test_routing_policy_rule_invert(self):
- self.copy_unit_to_networkd_unit_path('25-fibrule-invert.network', '11-dummy.netdev')
-
- self.start_networkd()
+ copy_unit_to_networkd_unit_path('25-fibrule-invert.network', '11-dummy.netdev')
+ start_networkd()
+ wait_online(['test1:degraded'])
- self.check_link_exists('test1')
-
- output = subprocess.check_output(['ip', 'rule'], universal_newlines=True).rstrip()
+ output = check_output('ip rule')
print(output)
self.assertRegex(output, '111')
self.assertRegex(output, 'not.*?from.*?192.168.100.18')
self.assertRegex(output, 'lookup 7')
def test_route_static(self):
- self.copy_unit_to_networkd_unit_path('25-route-static.network', '12-dummy.netdev')
- self.start_networkd(0)
-
- self.wait_online(['dummy98:routable'])
+ copy_unit_to_networkd_unit_path('25-route-static.network', '12-dummy.netdev')
+ start_networkd()
+ wait_online(['dummy98:routable'])
- output = subprocess.check_output(['ip', '-6', 'route', 'show', 'dev', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 route show dev dummy98')
print(output)
self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff proto static')
self.assertRegex(output, '2001:1234:5:8f63::1 proto kernel')
- output = subprocess.check_output(['ip', '-6', 'route', 'show', 'dev', 'dummy98', 'default'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 route show dev dummy98 default')
self.assertRegex(output, 'default via 2001:1234:5:8fff:ff:ff:ff:ff proto static metric 1024 pref medium')
- output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 route show dev dummy98')
print(output)
self.assertRegex(output, '149.10.124.48/28 proto kernel scope link src 149.10.124.58')
self.assertRegex(output, '149.10.124.64 proto static scope link')
self.assertRegex(output, '192.168.1.1 proto static initcwnd 20')
self.assertRegex(output, '192.168.1.2 proto static initrwnd 30')
- output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'dummy98', 'default'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 route show dev dummy98 default')
self.assertRegex(output, 'default via 149.10.125.65 proto static onlink')
self.assertRegex(output, 'default via 149.10.124.64 proto static')
self.assertRegex(output, 'default proto static')
- output = subprocess.check_output(['ip', 'route', 'show', 'type', 'blackhole'], universal_newlines=True).rstrip()
+ output = check_output('ip route show type blackhole')
print(output)
self.assertRegex(output, 'blackhole 202.54.1.2 proto static')
- output = subprocess.check_output(['ip', 'route', 'show', 'type', 'unreachable'], universal_newlines=True).rstrip()
+ output = check_output('ip route show type unreachable')
print(output)
self.assertRegex(output, 'unreachable 202.54.1.3 proto static')
- output = subprocess.check_output(['ip', 'route', 'show', 'type', 'prohibit'], universal_newlines=True).rstrip()
+ output = check_output('ip route show type prohibit')
print(output)
self.assertRegex(output, 'prohibit 202.54.1.4 proto static')
# a dummy device does not make the addresses go through tentative state, so we
# reuse a bond from an earlier test, which does make the addresses go through
# tentative state, and do our test on that
- self.copy_unit_to_networkd_unit_path('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
- self.start_networkd()
+ copy_unit_to_networkd_unit_path('23-active-slave.network', '25-route-ipv6-src.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
+ start_networkd()
+ wait_online(['dummy98:enslaved', 'bond199:routable'])
- self.check_link_exists('dummy98')
- self.check_link_exists('bond199')
-
- output = subprocess.check_output(['ip', '-6', 'route', 'list', 'dev', 'bond199'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 route list dev bond199')
print(output)
self.assertRegex(output, 'abcd::/16')
self.assertRegex(output, 'src')
self.assertRegex(output, '2001:1234:56:8f63::2')
def test_ip_link_mac_address(self):
- self.copy_unit_to_networkd_unit_path('25-address-link-section.network', '12-dummy.netdev')
- self.start_networkd()
-
- self.check_link_exists('dummy98')
+ copy_unit_to_networkd_unit_path('25-address-link-section.network', '12-dummy.netdev')
+ start_networkd()
+ wait_online(['dummy98:degraded'])
- output = subprocess.check_output(['ip', 'link', 'show', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip link show dummy98')
print(output)
self.assertRegex(output, '00:01:02:aa:bb:cc')
def test_ip_link_unmanaged(self):
- self.copy_unit_to_networkd_unit_path('25-link-section-unmanaged.network', '12-dummy.netdev')
- self.start_networkd()
+ copy_unit_to_networkd_unit_path('25-link-section-unmanaged.network', '12-dummy.netdev')
+ start_networkd(5)
self.check_link_exists('dummy98')
- output = subprocess.check_output(networkctl_cmd + ['status', 'dummy98'], universal_newlines=True, env=env).rstrip()
- print(output)
- self.assertRegex(output, 'unmanaged')
+ self.check_operstate('dummy98', 'off', setup_state='unmanaged')
def test_ipv6_address_label(self):
- self.copy_unit_to_networkd_unit_path('25-ipv6-address-label-section.network', '12-dummy.netdev')
- self.start_networkd()
-
- self.check_link_exists('dummy98')
+ copy_unit_to_networkd_unit_path('25-ipv6-address-label-section.network', '12-dummy.netdev')
+ start_networkd()
+ wait_online(['dummy98:degraded'])
- output = subprocess.check_output(['ip', 'addrlabel', 'list'], universal_newlines=True).rstrip()
+ output = check_output('ip addrlabel list')
print(output)
self.assertRegex(output, '2004:da8:1::/64')
def test_ipv6_neighbor(self):
- self.copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev')
- self.start_networkd()
+ copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev')
+ start_networkd()
+ wait_online(['dummy98:degraded'], timeout='40s')
- self.check_link_exists('dummy98')
-
- output = subprocess.check_output(['ip', 'neigh', 'list'], universal_newlines=True).rstrip()
+ output = check_output('ip neigh list dev dummy98')
print(output)
self.assertRegex(output, '192.168.10.1.*00:00:5e:00:02:65.*PERMANENT')
self.assertRegex(output, '2004:da8:1::1.*00:00:5e:00:02:66.*PERMANENT')
def test_link_local_addressing(self):
- self.copy_unit_to_networkd_unit_path('25-link-local-addressing-yes.network', '11-dummy.netdev',
- '25-link-local-addressing-no.network', '12-dummy.netdev')
- self.start_networkd(0)
- self.wait_online(['test1:degraded', 'dummy98:carrier'])
-
- self.check_link_exists('test1')
- self.check_link_exists('dummy98')
+ copy_unit_to_networkd_unit_path('25-link-local-addressing-yes.network', '11-dummy.netdev',
+ '25-link-local-addressing-no.network', '12-dummy.netdev')
+ start_networkd()
+ wait_online(['test1:degraded', 'dummy98:carrier'])
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'test1'], universal_newlines=True).rstrip()
+ output = check_output('ip address show dev test1')
print(output)
self.assertRegex(output, 'inet .* scope link')
self.assertRegex(output, 'inet6 .* scope link')
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip address show dev dummy98')
print(output)
self.assertNotRegex(output, 'inet6* .* scope link')
- self.check_operstate('test1', 'degraded')
- self.check_operstate('dummy98', 'carrier')
-
'''
Documentation/networking/ip-sysctl.txt
test1_addr_gen_mode = '0'
if os.path.exists(os.path.join(os.path.join(network_sysctl_ipv6_path, 'test1'), 'addr_gen_mode')):
- self.assertEqual(self.read_ipv6_sysctl_attr('test1', 'addr_gen_mode'), test1_addr_gen_mode)
+ self.assertEqual(read_ipv6_sysctl_attr('test1', 'addr_gen_mode'), test1_addr_gen_mode)
if os.path.exists(os.path.join(os.path.join(network_sysctl_ipv6_path, 'dummy98'), 'addr_gen_mode')):
- self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'addr_gen_mode'), '1')
+ self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'addr_gen_mode'), '1')
def test_sysctl(self):
- self.copy_unit_to_networkd_unit_path('25-sysctl.network', '12-dummy.netdev')
- self.start_networkd(0)
- self.wait_online(['dummy98:degraded'])
-
- self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'forwarding'), '1')
- self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'use_tempaddr'), '2')
- self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'dad_transmits'), '3')
- self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'hop_limit'), '5')
- self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'proxy_ndp'), '1')
- self.assertEqual(self.read_ipv4_sysctl_attr('dummy98', 'forwarding'),'1')
- self.assertEqual(self.read_ipv4_sysctl_attr('dummy98', 'proxy_arp'), '1')
+ copy_unit_to_networkd_unit_path('25-sysctl.network', '12-dummy.netdev')
+ start_networkd()
+ wait_online(['dummy98:degraded'])
+
+ self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'forwarding'), '1')
+ self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'use_tempaddr'), '2')
+ self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'dad_transmits'), '3')
+ self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'hop_limit'), '5')
+ self.assertEqual(read_ipv6_sysctl_attr('dummy98', 'proxy_ndp'), '1')
+ self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'forwarding'),'1')
+ self.assertEqual(read_ipv4_sysctl_attr('dummy98', 'proxy_arp'), '1')
def test_sysctl_disable_ipv6(self):
- self.copy_unit_to_networkd_unit_path('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
+ copy_unit_to_networkd_unit_path('25-sysctl-disable-ipv6.network', '12-dummy.netdev')
print('## Disable ipv6')
- self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.all.disable_ipv6=1']), 0)
- self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.default.disable_ipv6=1']), 0)
+ check_output('sysctl net.ipv6.conf.all.disable_ipv6=1')
+ check_output('sysctl net.ipv6.conf.default.disable_ipv6=1')
- self.start_networkd(0)
- self.wait_online(['dummy98:routable'])
+ start_networkd()
+ wait_online(['dummy98:routable'])
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dummy98')
print(output)
self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 address show dummy98')
+ print(output)
+ self.assertEqual(output, '')
+ output = check_output('ip -4 route show dev dummy98')
+ print(output)
+ self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
+ output = check_output('ip -6 route show dev dummy98')
print(output)
self.assertEqual(output, '')
- self.check_operstate('dummy98', 'routable')
- self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
+ check_output('ip link del dummy98')
print('## Enable ipv6')
- self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.all.disable_ipv6=0']), 0)
- self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.default.disable_ipv6=0']), 0)
+ check_output('sysctl net.ipv6.conf.all.disable_ipv6=0')
+ check_output('sysctl net.ipv6.conf.default.disable_ipv6=0')
- self.start_networkd(0)
- self.wait_online(['dummy98:routable'])
+ restart_networkd()
+ wait_online(['dummy98:routable'])
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dummy98')
print(output)
self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98')
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 address show dummy98')
print(output)
+ self.assertRegex(output, 'inet6 2607:5300:203:3906::/64 scope global')
self.assertRegex(output, 'inet6 .* scope link')
- self.check_operstate('dummy98', 'routable')
+ output = check_output('ip -4 route show dev dummy98')
+ print(output)
+ self.assertEqual(output, '10.2.0.0/16 proto kernel scope link src 10.2.3.4')
+ output = check_output('ip -6 route show dev dummy98')
+ print(output)
+ self.assertRegex(output, 'default via 2607:5300:203:39ff:ff:ff:ff:ff proto static')
def test_bind_carrier(self):
- self.copy_unit_to_networkd_unit_path('25-bind-carrier.network', '11-dummy.netdev')
- self.start_networkd()
-
- self.check_link_exists('test1')
+ copy_unit_to_networkd_unit_path('25-bind-carrier.network', '11-dummy.netdev')
+ start_networkd()
+ wait_online(['test1:routable'])
- self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
+ check_output('ip link add dummy98 type dummy')
+ check_output('ip link set dummy98 up')
time.sleep(2)
- output = subprocess.check_output(['ip', 'address', 'show', 'test1'], universal_newlines=True).rstrip()
+ output = check_output('ip address show test1')
print(output)
self.assertRegex(output, 'UP,LOWER_UP')
self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
self.check_operstate('test1', 'routable')
- self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy99', 'type', 'dummy']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy99', 'up']), 0)
+ check_output('ip link add dummy99 type dummy')
+ check_output('ip link set dummy99 up')
time.sleep(2)
- output = subprocess.check_output(['ip', 'address', 'show', 'test1'], universal_newlines=True).rstrip()
+ output = check_output('ip address show test1')
print(output)
self.assertRegex(output, 'UP,LOWER_UP')
self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
self.check_operstate('test1', 'routable')
- self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
+ check_output('ip link del dummy98')
time.sleep(2)
- output = subprocess.check_output(['ip', 'address', 'show', 'test1'], universal_newlines=True).rstrip()
+ output = check_output('ip address show test1')
print(output)
self.assertRegex(output, 'UP,LOWER_UP')
self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
self.check_operstate('test1', 'routable')
- self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy99']), 0)
+ check_output('ip link del dummy99')
time.sleep(2)
- output = subprocess.check_output(['ip', 'address', 'show', 'test1'], universal_newlines=True).rstrip()
+ output = check_output('ip address show test1')
print(output)
self.assertNotRegex(output, 'UP,LOWER_UP')
self.assertRegex(output, 'DOWN')
self.assertNotRegex(output, '192.168.10')
self.check_operstate('test1', 'off')
- self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
+ check_output('ip link add dummy98 type dummy')
+ check_output('ip link set dummy98 up')
time.sleep(2)
- output = subprocess.check_output(['ip', 'address', 'show', 'test1'], universal_newlines=True).rstrip()
+ output = check_output('ip address show test1')
print(output)
self.assertRegex(output, 'UP,LOWER_UP')
self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1')
self.check_operstate('test1', 'routable')
def test_domain(self):
- self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network')
- self.start_networkd(0)
- self.wait_online(['dummy98:routable'])
+ copy_unit_to_networkd_unit_path('12-dummy.netdev', '24-search-domain.network')
+ start_networkd()
+ wait_online(['dummy98:routable'])
- output = subprocess.check_output(networkctl_cmd + ['status', 'dummy98'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'dummy98', env=env)
print(output)
self.assertRegex(output, 'Address: 192.168.42.100')
self.assertRegex(output, 'DNS: 192.168.42.1')
self.assertRegex(output, 'Search Domains: one')
+ def test_keep_configuration_static(self):
+ check_output('systemctl stop systemd-networkd')
+
+ check_output('ip link add name dummy98 type dummy')
+ check_output('ip address add 10.1.2.3/16 dev dummy98')
+ check_output('ip address add 10.2.3.4/16 dev dummy98 valid_lft 600 preferred_lft 500')
+ output = check_output('ip address show dummy98')
+ print(output)
+ self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
+ self.assertRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
+ output = check_output('ip route show dev dummy98')
+ print(output)
+
+ copy_unit_to_networkd_unit_path('24-keep-configuration-static.network')
+ start_networkd()
+ wait_online(['dummy98:routable'])
+
+ output = check_output('ip address show dummy98')
+ print(output)
+ self.assertRegex(output, 'inet 10.1.2.3/16 scope global dummy98')
+ self.assertNotRegex(output, 'inet 10.2.3.4/16 scope global dynamic dummy98')
+
class NetworkdBondTests(unittest.TestCase, Utilities):
links = [
'bond199',
'23-active-slave.network',
'23-bond199.network',
'23-primary-slave.network',
- '23-test1-bond199.network',
'25-bond-active-backup-slave.netdev',
'25-bond.netdev',
'bond99.network',
'bond-slave.network']
def setUp(self):
- self.remove_links(self.links)
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
def tearDown(self):
- self.remove_links(self.links)
- self.remove_unit_from_networkd_path(self.units)
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
def test_bond_active_slave(self):
- self.copy_unit_to_networkd_unit_path('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
- self.start_networkd()
-
- self.check_link_exists('dummy98')
- self.check_link_exists('bond199')
+ copy_unit_to_networkd_unit_path('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
+ start_networkd()
+ wait_online(['dummy98:enslaved', 'bond199:degraded'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond199'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show bond199')
print(output)
self.assertRegex(output, 'active_slave dummy98')
def test_bond_primary_slave(self):
- self.copy_unit_to_networkd_unit_path('23-primary-slave.network', '23-test1-bond199.network', '25-bond-active-backup-slave.netdev', '11-dummy.netdev')
- self.start_networkd()
+ copy_unit_to_networkd_unit_path('23-primary-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev')
+ start_networkd()
+ wait_online(['dummy98:enslaved', 'bond199:degraded'])
- self.check_link_exists('test1')
- self.check_link_exists('bond199')
-
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond199'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show bond199')
print(output)
- self.assertRegex(output, 'primary test1')
+ self.assertRegex(output, 'primary dummy98')
def test_bond_operstate(self):
- self.copy_unit_to_networkd_unit_path('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
- 'bond99.network','bond-slave.network')
- self.start_networkd()
-
- self.check_link_exists('bond99')
- self.check_link_exists('dummy98')
- self.check_link_exists('test1')
+ copy_unit_to_networkd_unit_path('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev',
+ 'bond99.network','bond-slave.network')
+ start_networkd()
+ wait_online(['dummy98:enslaved', 'test1:enslaved', 'bond99:routable'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show dummy98')
print(output)
self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show test1')
print(output)
self.assertRegex(output, 'SLAVE,UP,LOWER_UP')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show bond99')
print(output)
self.assertRegex(output, 'MASTER,UP,LOWER_UP')
self.check_operstate('test1', 'enslaved')
self.check_operstate('bond99', 'routable')
- self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'down']), 0)
+ check_output('ip link set dummy98 down')
time.sleep(2)
self.check_operstate('dummy98', 'off')
self.check_operstate('test1', 'enslaved')
self.check_operstate('bond99', 'degraded-carrier')
- self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
+ check_output('ip link set dummy98 up')
time.sleep(2)
self.check_operstate('dummy98', 'enslaved')
self.check_operstate('test1', 'enslaved')
self.check_operstate('bond99', 'routable')
- self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'down']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'set', 'test1', 'down']), 0)
+ check_output('ip link set dummy98 down')
+ check_output('ip link set test1 down')
time.sleep(2)
self.check_operstate('dummy98', 'off')
for trial in range(30):
if trial > 0:
time.sleep(1)
- output = subprocess.check_output(['ip', 'address', 'show', 'bond99'], universal_newlines=True).rstrip()
+ output = check_output('ip address show bond99')
print(output)
- if self.get_operstate('bond99') == 'no-carrier':
+ if get_operstate('bond99') == 'no-carrier':
break
else:
# Huh? Kernel does not recognize that all slave interfaces are down?
'26-bridge.netdev',
'26-bridge-slave-interface-1.network',
'26-bridge-slave-interface-2.network',
+ '26-bridge-vlan-master.network',
+ '26-bridge-vlan-slave.network',
'bridge99-ignore-carrier-loss.network',
'bridge99.network']
+ routing_policy_rule_tables = ['100']
+
def setUp(self):
- self.remove_links(self.links)
+ remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
def tearDown(self):
- self.remove_links(self.links)
- self.remove_unit_from_networkd_path(self.units)
+ remove_routing_policy_rule_tables(self.routing_policy_rule_tables)
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
- def test_bridge_property(self):
- self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
- '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
- 'bridge99.network')
- self.start_networkd()
+ def test_bridge_vlan(self):
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '26-bridge-vlan-slave.network',
+ '26-bridge.netdev', '26-bridge-vlan-master.network')
+ start_networkd()
+ wait_online(['test1:enslaved', 'bridge99:degraded'])
- self.check_link_exists('dummy98')
- self.check_link_exists('test1')
- self.check_link_exists('bridge99')
+ output = check_output('bridge vlan show dev test1')
+ print(output)
+ self.assertNotRegex(output, '4063')
+ for i in range(4064, 4095):
+ self.assertRegex(output, f'{i}')
+ self.assertNotRegex(output, '4095')
+
+ output = check_output('bridge vlan show dev bridge99')
+ print(output)
+ self.assertNotRegex(output, '4059')
+ for i in range(4060, 4095):
+ self.assertRegex(output, f'{i}')
+ self.assertNotRegex(output, '4095')
+
+ def test_bridge_property(self):
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
+ '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
+ 'bridge99.network')
+ start_networkd()
+ wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show test1')
print(output)
self.assertRegex(output, 'master')
self.assertRegex(output, 'bridge')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show dummy98')
print(output)
self.assertRegex(output, 'master')
self.assertRegex(output, 'bridge')
- output = subprocess.check_output(['ip', 'addr', 'show', 'bridge99'], universal_newlines=True).rstrip()
+ output = check_output('ip addr show bridge99')
print(output)
self.assertRegex(output, '192.168.0.15/24')
- output = subprocess.check_output(['bridge', '-d', 'link', 'show', 'dummy98'], universal_newlines=True).rstrip()
+ output = check_output('bridge -d link show dummy98')
print(output)
- self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode'), '1')
- self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'path_cost'), '400')
- self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood'), '1')
- self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood'), '0')
- self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave'), '1')
+ self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'hairpin_mode'), '1')
+ self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'path_cost'), '400')
+ self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'unicast_flood'), '1')
+ self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_flood'), '0')
+ self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave'), '1')
if (os.path.exists('/sys/devices/virtual/net/bridge99/lower_dummy98/brport/neigh_suppress')):
- self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress'), '1')
- self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'learning'), '0')
+ self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress'), '1')
+ self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'learning'), '0')
# CONFIG_BRIDGE_IGMP_SNOOPING=y
if (os.path.exists('/sys/devices/virtual/net/bridge00/lower_dummy98/brport/multicast_to_unicast')):
- self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast'), '1')
+ self.assertEqual(read_bridge_port_attr('bridge99', 'dummy98', 'multicast_to_unicast'), '1')
- self.check_operstate('test1', 'enslaved')
- self.check_operstate('dummy98', 'enslaved')
- self.check_operstate('bridge99', 'routable')
-
- self.assertEqual(subprocess.call(['ip', 'address', 'add', '192.168.0.16/24', 'dev', 'bridge99']), 0)
+ check_output('ip address add 192.168.0.16/24 dev bridge99')
time.sleep(1)
- output = subprocess.check_output(['ip', 'addr', 'show', 'bridge99'], universal_newlines=True).rstrip()
+ output = check_output('ip addr show bridge99')
print(output)
self.assertRegex(output, '192.168.0.16/24')
- self.check_operstate('bridge99', 'routable')
-
- self.assertEqual(subprocess.call(['ip', 'link', 'del', 'test1']), 0)
+ self.assertEqual(call('ip link del test1'), 0)
time.sleep(3)
self.check_operstate('bridge99', 'degraded-carrier')
- self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
+ check_output('ip link del dummy98')
time.sleep(3)
self.check_operstate('bridge99', 'no-carrier')
- output = subprocess.check_output(['ip', 'address', 'show', 'bridge99'], universal_newlines=True).rstrip()
+ output = check_output('ip address show bridge99')
print(output)
self.assertRegex(output, 'NO-CARRIER')
self.assertNotRegex(output, '192.168.0.15/24')
self.assertNotRegex(output, '192.168.0.16/24')
def test_bridge_ignore_carrier_loss(self):
- self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
- '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
- 'bridge99-ignore-carrier-loss.network')
-
- subprocess.call(['ip', 'rule', 'del', 'table', '100'])
-
- self.start_networkd()
-
- self.check_link_exists('dummy98')
- self.check_link_exists('test1')
- self.check_link_exists('bridge99')
+ copy_unit_to_networkd_unit_path('11-dummy.netdev', '12-dummy.netdev', '26-bridge.netdev',
+ '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network',
+ 'bridge99-ignore-carrier-loss.network')
+ start_networkd()
+ wait_online(['dummy98:enslaved', 'test1:enslaved', 'bridge99:routable'])
- self.assertEqual(subprocess.call(['ip', 'address', 'add', '192.168.0.16/24', 'dev', 'bridge99']), 0)
+ check_output('ip address add 192.168.0.16/24 dev bridge99')
time.sleep(1)
- self.assertEqual(subprocess.call(['ip', 'link', 'del', 'test1']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
+ check_output('ip link del test1')
+ check_output('ip link del dummy98')
time.sleep(3)
- output = subprocess.check_output(['ip', 'address', 'show', 'bridge99'], universal_newlines=True).rstrip()
+ output = check_output('ip address show bridge99')
print(output)
self.assertRegex(output, 'NO-CARRIER')
self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
self.assertRegex(output, 'inet 192.168.0.16/24 scope global secondary bridge99')
- subprocess.call(['ip', 'rule', 'del', 'table', '100'])
-
def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self):
- self.copy_unit_to_networkd_unit_path('26-bridge.netdev', '26-bridge-slave-interface-1.network',
- 'bridge99-ignore-carrier-loss.network')
-
- subprocess.call(['ip', 'rule', 'del', 'table', '100'])
-
- self.start_networkd()
-
- self.check_link_exists('bridge99')
-
- self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
-
- self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
+ copy_unit_to_networkd_unit_path('26-bridge.netdev', '26-bridge-slave-interface-1.network',
+ 'bridge99-ignore-carrier-loss.network')
+ start_networkd()
+ wait_online(['bridge99:no-carrier'])
- self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0)
+ for trial in range(4):
+ check_output('ip link add dummy98 type dummy')
+ check_output('ip link set dummy98 up')
+ if trial < 3:
+ check_output('ip link del dummy98')
- self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0)
- self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0)
+ wait_online(['bridge99:routable', 'dummy98:enslaved'])
- for trial in range(30):
- if trial > 0:
- time.sleep(1)
- if self.get_operstate('bridge99') == 'routable' and self.get_operstate('dummy98') == 'enslaved':
- break
- else:
- self.assertTrue(False)
-
- output = subprocess.check_output(['ip', 'address', 'show', 'bridge99'], universal_newlines=True).rstrip()
+ output = check_output('ip address show bridge99')
print(output)
self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99')
- output = subprocess.check_output(['ip', 'rule', 'list', 'table', '100'], universal_newlines=True).rstrip()
+ output = check_output('ip rule list table 100')
print(output)
self.assertEqual(output, '0: from all to 8.8.8.8 lookup 100')
- subprocess.call(['ip', 'rule', 'del', 'table', '100'])
-
class NetworkdLLDPTests(unittest.TestCase, Utilities):
links = ['veth99']
'25-veth.netdev']
def setUp(self):
- self.remove_links(self.links)
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
def tearDown(self):
- self.remove_links(self.links)
- self.remove_unit_from_networkd_path(self.units)
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
def test_lldp(self):
- self.copy_unit_to_networkd_unit_path('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
- self.start_networkd(0)
- self.wait_online(['veth99:degraded', 'veth-peer:degraded'])
+ copy_unit_to_networkd_unit_path('23-emit-lldp.network', '24-lldp.network', '25-veth.netdev')
+ start_networkd()
+ wait_online(['veth99:degraded', 'veth-peer:degraded'])
- output = subprocess.check_output(networkctl_cmd + ['lldp'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'lldp', env=env)
print(output)
self.assertRegex(output, 'veth-peer')
self.assertRegex(output, 'veth99')
'ipv6-prefix-veth.network']
def setUp(self):
- self.remove_links(self.links)
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
def tearDown(self):
- self.remove_links(self.links)
- self.remove_unit_from_networkd_path(self.units)
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
def test_ipv6_prefix_delegation(self):
- self.warn_about_firewalld()
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth.network')
- self.start_networkd(0)
- self.wait_online(['veth99:routable', 'veth-peer:degraded'])
+ warn_about_firewalld()
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6-prefix.network', 'ipv6-prefix-veth.network')
+ start_networkd()
+ wait_online(['veth99:routable', 'veth-peer:degraded'])
- output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
print(output)
self.assertRegex(output, '2002:da8:1:0')
'dhcp-server-timezone-router.network']
def setUp(self):
- self.remove_links(self.links)
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
def tearDown(self):
- self.remove_links(self.links)
- self.remove_unit_from_networkd_path(self.units)
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
def test_dhcp_server(self):
- self.warn_about_firewalld()
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client.network', 'dhcp-server.network')
- self.start_networkd(0)
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ warn_about_firewalld()
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client.network', 'dhcp-server.network')
+ start_networkd()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
print(output)
self.assertRegex(output, '192.168.5.*')
self.assertRegex(output, 'Gateway: 192.168.5.1')
self.assertRegex(output, 'NTP: 192.168.5.1')
def test_emit_router_timezone(self):
- self.warn_about_firewalld()
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client-timezone-router.network', 'dhcp-server-timezone-router.network')
- self.start_networkd(0)
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ warn_about_firewalld()
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-client-timezone-router.network', 'dhcp-server-timezone-router.network')
+ start_networkd()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
print(output)
self.assertRegex(output, 'Gateway: 192.168.5.*')
self.assertRegex(output, '192.168.5.*')
'25-vrf.netdev',
'25-vrf.network',
'dhcp-client-anonymize.network',
- 'dhcp-client-critical-connection.network',
'dhcp-client-gateway-onlink-implicit.network',
'dhcp-client-ipv4-dhcp-settings.network',
'dhcp-client-ipv4-only-ipv6-disabled.network',
'dhcp-client-ipv4-only.network',
'dhcp-client-ipv6-only.network',
'dhcp-client-ipv6-rapid-commit.network',
+ 'dhcp-client-keep-configuration-dhcp-on-stop.network',
+ 'dhcp-client-keep-configuration-dhcp.network',
'dhcp-client-listen-port.network',
'dhcp-client-route-metric.network',
'dhcp-client-route-table.network',
+ 'dhcp-client-use-dns-ipv4-and-ra.network',
+ 'dhcp-client-use-dns-ipv4.network',
+ 'dhcp-client-use-dns-no.network',
+ 'dhcp-client-use-dns-yes.network',
+ 'dhcp-client-use-routes-no.network',
'dhcp-client-vrf.network',
'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network',
'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network',
+ 'dhcp-client-with-static-address.network',
'dhcp-client.network',
'dhcp-server-veth-peer.network',
'dhcp-v4-server-veth-peer.network',
'static.network']
def setUp(self):
- self.stop_dnsmasq(dnsmasq_pid_file)
- self.remove_links(self.links)
+ stop_dnsmasq(dnsmasq_pid_file)
+ remove_links(self.links)
+ stop_networkd(show_logs=False)
def tearDown(self):
- self.stop_dnsmasq(dnsmasq_pid_file)
- self.remove_lease_file()
- self.remove_log_file()
- self.remove_links(self.links)
- self.remove_unit_from_networkd_path(self.units)
+ stop_dnsmasq(dnsmasq_pid_file)
+ remove_lease_file()
+ remove_log_file()
+ remove_links(self.links)
+ remove_unit_from_networkd_path(self.units)
+ stop_networkd(show_logs=True)
def test_dhcp_client_ipv6_only(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network')
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
print(output)
self.assertRegex(output, '2600::')
self.assertNotRegex(output, '192.168.5')
# Confirm that ipv6 token is not set in the kernel
- output = subprocess.check_output(['ip', 'token', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip token show dev veth99')
print(output)
self.assertRegex(output, 'token :: dev veth99')
def test_dhcp_client_ipv4_only(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-only-ipv6-disabled.network')
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-only-ipv6-disabled.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
print(output)
self.assertNotRegex(output, '2600::')
self.assertRegex(output, '192.168.5')
def test_dhcp_client_ipv4_ipv6(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network',
- 'dhcp-client-ipv4-only.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network',
+ 'dhcp-client-ipv4-only.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
# link become 'routable' when at least one protocol provide an valid address.
- self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255', ipv='-4')
- self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128', ipv='-6')
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
- output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
print(output)
self.assertRegex(output, '2600::')
self.assertRegex(output, '192.168.5')
def test_dhcp_client_settings(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-dhcp-settings.network')
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv4-dhcp-settings.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
print('## ip address show dev veth99')
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip address show dev veth99')
print(output)
self.assertRegex(output, '12:34:56:78:9a:bc')
self.assertRegex(output, '192.168.5')
# issue #8726
print('## ip route show table main dev veth99')
- output = subprocess.check_output(['ip', 'route', 'show', 'table', 'main', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip route show table main dev veth99')
print(output)
self.assertNotRegex(output, 'proto dhcp')
print('## ip route show table 211 dev veth99')
- output = subprocess.check_output(['ip', 'route', 'show', 'table', '211', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip route show table 211 dev veth99')
print(output)
self.assertRegex(output, 'default via 192.168.5.1 proto dhcp')
self.assertRegex(output, '192.168.5.0/24 via 192.168.5.5 proto dhcp')
self.assertRegex(output, '192.168.5.1 proto dhcp scope link')
print('## dnsmasq log')
- self.assertTrue(self.search_words_in_dnsmasq_log('vendor class: SusantVendorTest', True))
- self.assertTrue(self.search_words_in_dnsmasq_log('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc'))
- self.assertTrue(self.search_words_in_dnsmasq_log('client provides name: test-hostname'))
- self.assertTrue(self.search_words_in_dnsmasq_log('26:mtu'))
+ self.assertTrue(search_words_in_dnsmasq_log('vendor class: SusantVendorTest', True))
+ self.assertTrue(search_words_in_dnsmasq_log('DHCPDISCOVER(veth-peer) 12:34:56:78:9a:bc'))
+ self.assertTrue(search_words_in_dnsmasq_log('client provides name: test-hostname'))
+ self.assertTrue(search_words_in_dnsmasq_log('26:mtu'))
def test_dhcp6_client_settings_rapidcommit_true(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-only.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip address show dev veth99')
print(output)
self.assertRegex(output, '12:34:56:78:9a:bc')
- self.assertTrue(self.search_words_in_dnsmasq_log('14:rapid-commit', True))
+ self.assertTrue(search_words_in_dnsmasq_log('14:rapid-commit', True))
def test_dhcp6_client_settings_rapidcommit_false(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-rapid-commit.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-ipv6-rapid-commit.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip address show dev veth99')
print(output)
self.assertRegex(output, '12:34:56:78:9a:bc')
- self.assertFalse(self.search_words_in_dnsmasq_log('14:rapid-commit', True))
+ self.assertFalse(search_words_in_dnsmasq_log('14:rapid-commit', True))
def test_dhcp_client_settings_anonymize(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-anonymize.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-anonymize.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- self.assertFalse(self.search_words_in_dnsmasq_log('VendorClassIdentifier=SusantVendorTest', True))
- self.assertFalse(self.search_words_in_dnsmasq_log('test-hostname'))
- self.assertFalse(self.search_words_in_dnsmasq_log('26:mtu'))
+ self.assertFalse(search_words_in_dnsmasq_log('VendorClassIdentifier=SusantVendorTest', True))
+ self.assertFalse(search_words_in_dnsmasq_log('test-hostname'))
+ self.assertFalse(search_words_in_dnsmasq_log('26:mtu'))
def test_dhcp_client_listen_port(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-listen-port.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq('--dhcp-alternate-port=67,5555')
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-listen-port.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq('--dhcp-alternate-port=67,5555')
+ wait_online(['veth99:routable', 'veth-peer:routable'])
# link become 'routable' when at least one protocol provide an valid address.
- self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255', ipv='-4')
- self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128', ipv='-6')
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev veth99')
print(output)
self.assertRegex(output, '192.168.5.* dynamic')
+ def test_dhcp_client_with_static_address(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network',
+ 'dhcp-client-with-static-address.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
+
+ output = check_output('ip address show dev veth99 scope global')
+ print(output)
+ self.assertRegex(output, r'inet 192.168.5.250/24 brd 192.168.5.255 scope global veth99')
+ self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global secondary dynamic veth99')
+
+ output = check_output('ip route show dev veth99')
+ print(output)
+ self.assertRegex(output, r'default via 192.168.5.1 proto dhcp src 192.168.5.[0-9]* metric 1024')
+ self.assertRegex(output, r'192.168.5.0/24 proto kernel scope link src 192.168.5.250')
+ self.assertRegex(output, r'192.168.5.0/24 via 192.168.5.5 proto dhcp src 192.168.5.[0-9]* metric 1024')
+ self.assertRegex(output, r'192.168.5.1 proto dhcp scope link src 192.168.5.[0-9]* metric 1024')
+
def test_dhcp_route_table_id(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-table.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-table.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(['ip', 'route', 'show', 'table', '12'], universal_newlines=True).rstrip()
+ output = check_output('ip route show table 12')
print(output)
self.assertRegex(output, 'veth99 proto dhcp')
self.assertRegex(output, '192.168.5.1')
def test_dhcp_route_metric(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-metric.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-route-metric.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(['ip', 'route', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip route show dev veth99')
print(output)
self.assertRegex(output, 'metric 24')
- def test_dhcp_route_criticalconnection_true(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-critical-connection.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ def test_dhcp_client_use_routes_no(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
+ 'dhcp-client-use-routes-no.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq(lease_time='2m')
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip()
+ output = check_output('ip address show dev veth99 scope global')
print(output)
- self.assertRegex(output, '192.168.5.*')
+ self.assertRegex(output, r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
+
+ output = check_output('ip route show dev veth99')
+ print(output)
+ self.assertRegex(output, r'192.168.5.0/24 proto kernel scope link src 192.168.5.[0-9]*')
+ self.assertRegex(output, r'192.168.5.0/24 proto static')
+ self.assertRegex(output, r'192.168.6.0/24 proto static')
+ self.assertRegex(output, r'192.168.7.0/24 proto static')
+
+ # Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
+ print('Wait for the dynamic address to be renewed')
+ time.sleep(125)
+
+ wait_online(['veth99:routable'])
+
+ output = check_output('ip route show dev veth99')
+ print(output)
+ self.assertRegex(output, r'192.168.5.0/24 proto kernel scope link src 192.168.5.[0-9]*')
+ self.assertRegex(output, r'192.168.5.0/24 proto static')
+ self.assertRegex(output, r'192.168.6.0/24 proto static')
+ self.assertRegex(output, r'192.168.7.0/24 proto static')
+
+ def test_dhcp_keep_configuration_dhcp(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-keep-configuration-dhcp.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq(lease_time='2m')
+ wait_online(['veth99:routable', 'veth-peer:routable'])
+
+ output = check_output('ip address show dev veth99 scope global')
+ print(output)
+ self.assertRegex(output, r'192.168.5.*')
+
+ output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, r'192.168.5.*')
# Stopping dnsmasq as networkd won't be allowed to renew the DHCP lease.
- self.stop_dnsmasq(dnsmasq_pid_file)
+ stop_dnsmasq(dnsmasq_pid_file)
# Sleep for 120 sec as the dnsmasq minimum lease time can only be set to 120
+ print('Wait for the dynamic address to be expired')
time.sleep(125)
- output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip()
+ print('The lease address should be kept after lease expired')
+ output = check_output('ip address show dev veth99 scope global')
print(output)
- self.assertRegex(output, '192.168.5.*')
+ self.assertRegex(output, r'192.168.5.*')
+
+ output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, r'192.168.5.*')
+
+ check_output('systemctl stop systemd-networkd')
+
+ print('The lease address should be kept after networkd stopped')
+ output = check_output('ip address show dev veth99 scope global')
+ print(output)
+ self.assertRegex(output, r'192.168.5.*')
+
+ output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, r'192.168.5.*')
+
+ check_output('systemctl start systemd-networkd')
+ wait_online(['veth-peer:routable'])
+
+ print('Still the lease address should be kept after networkd restarted')
+ output = check_output('ip address show dev veth99 scope global')
+ print(output)
+ self.assertRegex(output, r'192.168.5.*')
+
+ output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, r'192.168.5.*')
+
+ def test_dhcp_keep_configuration_dhcp_on_stop(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-v4-server-veth-peer.network', 'dhcp-client-keep-configuration-dhcp-on-stop.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq(lease_time='2m')
+ wait_online(['veth99:routable', 'veth-peer:routable'])
+
+ output = check_output('ip address show dev veth99 scope global')
+ print(output)
+ self.assertRegex(output, r'192.168.5.*')
+
+ stop_dnsmasq(dnsmasq_pid_file)
+ check_output('systemctl stop systemd-networkd')
+
+ output = check_output('ip address show dev veth99 scope global')
+ print(output)
+ self.assertRegex(output, r'192.168.5.*')
+
+ restart_networkd()
+ wait_online(['veth-peer:routable'])
+
+ output = check_output('ip address show dev veth99 scope global')
+ print(output)
+ self.assertNotRegex(output, r'192.168.5.*')
def test_dhcp_client_reuse_address_as_static(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
# link become 'routable' when at least one protocol provide an valid address.
- self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255', ipv='-4')
- self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128', ipv='-6')
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99', 'scope', 'global'], universal_newlines=True).rstrip()
+ output = check_output('ip address show dev veth99 scope global')
print(output)
self.assertRegex(output, '192.168.5')
self.assertRegex(output, '2600::')
static_network = '\n'.join(['[Match]', 'Name=veth99', '[Network]', 'IPv6AcceptRA=no', 'Address=' + ipv4_address.group(), 'Address=' + ipv6_address.group()])
print(static_network)
- self.remove_unit_from_networkd_path(['dhcp-client.network'])
+ remove_unit_from_networkd_path(['dhcp-client.network'])
with open(os.path.join(network_unit_file_path, 'static.network'), mode='w') as f:
f.write(static_network)
# When networkd started, the links are already configured, so let's wait for 5 seconds
# the links to be re-configured.
- self.start_networkd(5)
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ restart_networkd(5)
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev veth99 scope global')
print(output)
self.assertRegex(output, '192.168.5')
self.assertRegex(output, 'valid_lft forever preferred_lft forever')
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 address show dev veth99 scope global')
print(output)
self.assertRegex(output, '2600::')
self.assertRegex(output, 'valid_lft forever preferred_lft forever')
@expectedFailureIfModuleIsNotAvailable('vrf')
def test_dhcp_client_vrf(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-vrf.network',
- '25-vrf.netdev', '25-vrf.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-vrf.network',
+ '25-vrf.netdev', '25-vrf.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable', 'vrf99:carrier'])
# link become 'routable' when at least one protocol provide an valid address.
- self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255', ipv='-4')
- self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128', ipv='-6')
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
print('## ip -d link show dev vrf99')
- output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dev', 'vrf99'], universal_newlines=True).rstrip()
+ output = check_output('ip -d link show dev vrf99')
print(output)
self.assertRegex(output, 'vrf table 42')
print('## ip address show vrf vrf99')
- output_ip_vrf = subprocess.check_output(['ip', 'address', 'show', 'vrf', 'vrf99'], universal_newlines=True).rstrip()
- print(output_ip_vrf)
+ output = check_output('ip address show vrf vrf99')
+ print(output)
+ self.assertRegex(output, 'inet 169.254.[0-9]*.[0-9]*/16 brd 169.254.255.255 scope link veth99')
+ self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
+ self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)')
+ self.assertRegex(output, 'inet6 .* scope link')
print('## ip address show dev veth99')
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip address show dev veth99')
print(output)
- self.assertEqual(output, output_ip_vrf)
self.assertRegex(output, 'inet 169.254.[0-9]*.[0-9]*/16 brd 169.254.255.255 scope link veth99')
self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
- self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic noprefixroute')
+ self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)')
self.assertRegex(output, 'inet6 .* scope link')
print('## ip route show vrf vrf99')
- output = subprocess.check_output(['ip', 'route', 'show', 'vrf', 'vrf99'], universal_newlines=True).rstrip()
+ output = check_output('ip route show vrf vrf99')
print(output)
self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.')
self.assertRegex(output, 'default dev veth99 proto static scope link')
self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5')
print('## ip route show table main dev veth99')
- output = subprocess.check_output(['ip', 'route', 'show', 'table', 'main', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip route show table main dev veth99')
print(output)
self.assertEqual(output, '')
- self.check_operstate('vrf99', 'carrier')
- self.check_operstate('veth99', 'routable')
-
def test_dhcp_client_gateway_onlink_implicit(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
- 'dhcp-client-gateway-onlink-implicit.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq()
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
+ 'dhcp-client-gateway-onlink-implicit.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq()
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip()
+ output = check_output(*networkctl_cmd, 'status', 'veth99', env=env)
print(output)
self.assertRegex(output, '192.168.5')
- output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'veth99', '10.0.0.0/8'], universal_newlines=True).rstrip()
+ output = check_output('ip route list dev veth99 10.0.0.0/8')
print(output)
self.assertRegex(output, 'onlink')
- output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'veth99', '192.168.100.0/24'], universal_newlines=True).rstrip()
+ output = check_output('ip route list dev veth99 192.168.100.0/24')
print(output)
self.assertRegex(output, 'onlink')
def test_dhcp_client_with_ipv4ll_fallback_with_dhcp_server(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
- 'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq(lease_time='2m')
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
+ 'dhcp-client-with-ipv4ll-fallback-with-dhcp-server.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq(lease_time='2m')
+ wait_online(['veth99:routable', 'veth-peer:routable'])
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip address show dev veth99')
print(output)
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 address show dev veth99 scope global dynamic')
self.assertNotRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic')
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'link'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 address show dev veth99 scope link')
self.assertRegex(output, 'inet6 .* scope link')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev veth99 scope global dynamic')
self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'link'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev veth99 scope link')
self.assertNotRegex(output, 'inet .* scope link')
print('Wait for the dynamic address to be expired')
time.sleep(130)
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip address show dev veth99')
print(output)
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 address show dev veth99 scope global dynamic')
self.assertNotRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic')
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'link'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 address show dev veth99 scope link')
self.assertRegex(output, 'inet6 .* scope link')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev veth99 scope global dynamic')
self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'link'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev veth99 scope link')
self.assertNotRegex(output, 'inet .* scope link')
- self.search_words_in_dnsmasq_log('DHCPOFFER', show_all=True)
+ search_words_in_dnsmasq_log('DHCPOFFER', show_all=True)
def test_dhcp_client_with_ipv4ll_fallback_without_dhcp_server(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
- 'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network')
- self.start_networkd(0)
- self.wait_online(['veth99:degraded', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
+ 'dhcp-client-with-ipv4ll-fallback-without-dhcp-server.network')
+ start_networkd()
+ wait_online(['veth99:degraded', 'veth-peer:routable'])
- output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip address show dev veth99')
print(output)
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 address show dev veth99 scope global dynamic')
self.assertNotRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic')
- output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'veth99', 'scope', 'link'], universal_newlines=True).rstrip()
+ output = check_output('ip -6 address show dev veth99 scope link')
self.assertRegex(output, 'inet6 .* scope link')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev veth99 scope global dynamic')
self.assertNotRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'link'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev veth99 scope link')
self.assertRegex(output, 'inet .* scope link')
def test_dhcp_client_route_remove_on_renew(self):
- self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
- 'dhcp-client-ipv4-only-ipv6-disabled.network')
- self.start_networkd(0)
- self.wait_online(['veth-peer:carrier'])
- self.start_dnsmasq(ipv4_range='192.168.5.100,192.168.5.199', lease_time='2m')
- self.wait_online(['veth99:routable', 'veth-peer:routable'])
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network',
+ 'dhcp-client-ipv4-only-ipv6-disabled.network')
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq(ipv4_range='192.168.5.100,192.168.5.199', lease_time='2m')
+ wait_online(['veth99:routable', 'veth-peer:routable'])
# test for issue #12490
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev veth99 scope global dynamic')
print(output)
self.assertRegex(output, 'inet 192.168.5.1[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
address1=None
address1 = line.split()[1].split('/')[0]
break
- output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 route show dev veth99')
print(output)
self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 1024')
self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 1024')
- self.stop_dnsmasq(dnsmasq_pid_file)
- self.start_dnsmasq(ipv4_range='192.168.5.200,192.168.5.250', lease_time='2m')
+ stop_dnsmasq(dnsmasq_pid_file)
+ start_dnsmasq(ipv4_range='192.168.5.200,192.168.5.250', lease_time='2m')
print('Wait for the dynamic address to be expired')
time.sleep(130)
- output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'veth99', 'scope', 'global', 'dynamic'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 address show dev veth99 scope global dynamic')
print(output)
self.assertRegex(output, 'inet 192.168.5.2[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99')
address2=None
self.assertNotEqual(address1, address2)
- output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'veth99'], universal_newlines=True).rstrip()
+ output = check_output('ip -4 route show dev veth99')
print(output)
self.assertNotRegex(output, f'default via 192.168.5.1 proto dhcp src {address1} metric 1024')
self.assertNotRegex(output, f'192.168.5.1 proto dhcp scope link src {address1} metric 1024')
self.assertRegex(output, f'default via 192.168.5.1 proto dhcp src {address2} metric 1024')
self.assertRegex(output, f'192.168.5.1 proto dhcp scope link src {address2} metric 1024')
+ def test_dhcp_client_use_dns_yes(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-yes.network')
+
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
+ wait_online(['veth99:routable', 'veth-peer:routable'])
+
+ # link become 'routable' when at least one protocol provide an valid address.
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
+
+ time.sleep(3)
+ output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, '192.168.5.1')
+ self.assertRegex(output, '2600::1')
+
+ def test_dhcp_client_use_dns_no(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-no.network')
+
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
+ wait_online(['veth99:routable', 'veth-peer:routable'])
+
+ # link become 'routable' when at least one protocol provide an valid address.
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
+
+ time.sleep(3)
+ output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+ print(output)
+ self.assertNotRegex(output, '192.168.5.1')
+ self.assertNotRegex(output, '2600::1')
+
+ def test_dhcp_client_use_dns_ipv4(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-ipv4.network')
+
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
+ wait_online(['veth99:routable', 'veth-peer:routable'])
+
+ # link become 'routable' when at least one protocol provide an valid address.
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
+
+ time.sleep(3)
+ output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, '192.168.5.1')
+ self.assertNotRegex(output, '2600::1')
+
+ def test_dhcp_client_use_dns_ipv4_and_ra(self):
+ copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-use-dns-ipv4-and-ra.network')
+
+ start_networkd()
+ wait_online(['veth-peer:carrier'])
+ start_dnsmasq('--dhcp-option=option:dns-server,192.168.5.1 --dhcp-option=option6:dns-server,[2600::1]')
+ wait_online(['veth99:routable', 'veth-peer:routable'])
+
+ # link become 'routable' when at least one protocol provide an valid address.
+ self.wait_address('veth99', r'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic', ipv='-4')
+ self.wait_address('veth99', r'inet6 2600::[0-9a-f]*/128 scope global (?:dynamic noprefixroute|noprefixroute dynamic)', ipv='-6')
+
+ time.sleep(3)
+ output = check_output(*resolvectl_cmd, 'dns', 'veth99', env=env)
+ print(output)
+ self.assertRegex(output, '192.168.5.1')
+ self.assertRegex(output, '2600::1')
+
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--build-dir', help='Path to build dir', dest='build_dir')
parser.add_argument('--networkd', help='Path to systemd-networkd', dest='networkd_bin')
+ parser.add_argument('--resolved', help='Path to systemd-resolved', dest='resolved_bin')
parser.add_argument('--wait-online', help='Path to systemd-networkd-wait-online', dest='wait_online_bin')
parser.add_argument('--networkctl', help='Path to networkctl', dest='networkctl_bin')
+ parser.add_argument('--resolvectl', help='Path to resolvectl', dest='resolvectl_bin')
+ parser.add_argument('--timedatectl', help='Path to timedatectl', dest='timedatectl_bin')
parser.add_argument('--valgrind', help='Enable valgrind', dest='use_valgrind', type=bool, nargs='?', const=True, default=use_valgrind)
parser.add_argument('--debug', help='Generate debugging logs', dest='enable_debug', type=bool, nargs='?', const=True, default=enable_debug)
parser.add_argument('--asan-options', help='ASAN options', dest='asan_options')
ns, args = parser.parse_known_args(namespace=unittest)
if ns.build_dir:
- if ns.networkd_bin or ns.wait_online_bin or ns.networkctl_bin:
- print('WARNING: --networkd, --wait-online, or --networkctl options are ignored when --build-dir is specified.')
+ if ns.networkd_bin or ns.resolved_bin or ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin:
+ print('WARNING: --networkd, --resolved, --wait-online, --networkctl, --resolvectl, or --timedatectl options are ignored when --build-dir is specified.')
networkd_bin = os.path.join(ns.build_dir, 'systemd-networkd')
+ resolved_bin = os.path.join(ns.build_dir, 'systemd-resolved')
wait_online_bin = os.path.join(ns.build_dir, 'systemd-networkd-wait-online')
networkctl_bin = os.path.join(ns.build_dir, 'networkctl')
+ resolvectl_bin = os.path.join(ns.build_dir, 'resolvectl')
+ timedatectl_bin = os.path.join(ns.build_dir, 'timedatectl')
else:
if ns.networkd_bin:
networkd_bin = ns.networkd_bin
+ if ns.resolved_bin:
+ resolved_bin = ns.resolved_bin
if ns.wait_online_bin:
wait_online_bin = ns.wait_online_bin
if ns.networkctl_bin:
networkctl_bin = ns.networkctl_bin
+ if ns.resolvectl_bin:
+ resolvectl_bin = ns.resolvectl_bin
+ if ns.timedatectl_bin:
+ timedatectl_bin = ns.timedatectl_bin
use_valgrind = ns.use_valgrind
enable_debug = ns.enable_debug
if use_valgrind:
networkctl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', networkctl_bin]
+ resolvectl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', resolvectl_bin]
+ timedatectl_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', timedatectl_bin]
wait_online_cmd = ['valgrind', '--track-origins=yes', '--leak-check=full', '--show-leak-kinds=all', wait_online_bin]
else:
networkctl_cmd = [networkctl_bin]
+ resolvectl_cmd = [resolvectl_bin]
+ timedatectl_cmd = [timedatectl_bin]
wait_online_cmd = [wait_online_bin]
if enable_debug:
}
+sub cleanup {
+ system("rm", "-rf", "$udev_run");
+ system("umount", "$udev_tmpfs");
+ rmdir($udev_tmpfs);
+}
+
# only run if we have root permissions
# due to mknod restrictions
if (!($<==0)) {
if (!udev_setup()) {
warn "Failed to set up the environment, skipping the test";
+ cleanup();
exit($EXIT_TEST_SKIP);
}
if (system($udev_bin, "check")) {
warn "$udev_bin failed to set up the environment, skipping the test";
+ cleanup();
exit($EXIT_TEST_SKIP);
}
print "$error errors occurred\n\n";
-# cleanup
-system("rm", "-rf", "$udev_run");
-system("umount", "$udev_tmpfs");
-rmdir($udev_tmpfs);
+cleanup();
if ($error > 0) {
exit(1);
sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse' >>/etc/apt/sources.list"
sudo apt-get update -y
sudo apt-get build-dep systemd -y
-sudo apt-get install -y ninja-build python3-pip python3-setuptools
+sudo apt-get install -y ninja-build python3-pip python3-setuptools quota
pip3 install meson
cd $REPO_ROOT
--- /dev/null
+#!/bin/bash
+
+set -e
+set -x
+set -u
+
+REPO_ROOT=${REPO_ROOT:-$(pwd)}
+
+sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse' >>/etc/apt/sources.list"
+sudo apt-get update -y
+sudo apt-get build-dep systemd -y
+sudo apt-get install -y ninja-build python3-pip python3-setuptools
+pip3 install meson
+
+cd $REPO_ROOT
+export PATH="$HOME/.local/bin/:$PATH"
+
+# We use a subset of https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#available-checks instead of "undefined"
+# because our fuzzers crash with "pointer-overflow","object-size" and "float-cast-overflow":
+# https://github.com/systemd/systemd/pull/12771#issuecomment-502139157
+# https://github.com/systemd/systemd/pull/12812#issuecomment-502780455
+# TODO: figure out what to do about unsigned-integer-overflow: https://github.com/google/oss-fuzz/issues/910
+export SANITIZER="address -fsanitize=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,unsigned-integer-overflow,vla-bound,vptr -fno-sanitize-recover=alignment,array-bounds,bool,bounds,builtin,enum,float-divide-by-zero,function,integer-divide-by-zero,nonnull-attribute,null,return,returns-nonnull-attribute,shift,signed-integer-overflow,unreachable,vla-bound,vptr"
+tools/oss-fuzz.sh
+
+FUZZING_TYPE=${1:-sanity}
+if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then
+ FUZZIT_BRANCH="${TRAVIS_BRANCH}"
+else
+ FUZZIT_BRANCH="PR-${TRAVIS_PULL_REQUEST}"
+fi
+
+# Because we want Fuzzit to run on every pull-request and Travis/Azure doesnt support encrypted keys
+# on pull-request we use a write-only key which is ok for now. maybe there will be a better solution in the future
+FUZZIT_API_KEY=7c1bd82fe0927ffe1b4bf1e2e86cc812b28dfe08a7080a7bf498e98715884a163402ee37ba95d4b1637247deffcea43e
+FUZZIT_ADDITIONAL_FILES="./out/src/shared/libsystemd-shared-242.so"
+
+# ASan options are borrowed almost verbatim from OSS-Fuzz
+ASAN_OPTIONS=redzone=32:print_summary=1:handle_sigill=1:allocator_release_to_os_interval_ms=500:print_suppressions=0:strict_memcmp=1:allow_user_segv_handler=0:allocator_may_return_null=1:use_sigaltstack=1:handle_sigfpe=1:handle_sigbus=1:detect_stack_use_after_return=1:alloc_dealloc_mismatch=0:detect_leaks=1:print_scariness=1:max_uar_stack_size_log=16:handle_abort=1:check_malloc_usable_size=0:quarantine_size_mb=64:detect_odr_violation=0:handle_segv=1:fast_unwind_on_fatal=0
+UBSAN_OPTIONS=print_stacktrace=1:print_summary=1:halt_on_error=1:silence_unsigned_overflow=1
+FUZZIT_ARGS="--type ${FUZZING_TYPE} --branch ${FUZZIT_BRANCH} --revision ${TRAVIS_COMMIT} --asan_options ${ASAN_OPTIONS} --ubsan_options ${UBSAN_OPTIONS}"
+wget -O fuzzit https://bin.fuzzit.dev/fuzzit-1.1
+chmod +x fuzzit
+
+./fuzzit auth ${FUZZIT_API_KEY}
+
+# The following was generated with
+# ./fuzzit get targets | jq --raw-output '.target_name + " " + .id' | perl -alne 'printf("./fuzzit c job \${FUZZIT_ARGS} %s ./out/%s \${FUZZIT_ADDITIONAL_FILES}\n", $F[1], $F[0])'
+./fuzzit c job ${FUZZIT_ARGS} 2ODbhEjfRF2AZtrUotMh ./out/fuzz-bus-label ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} 62XnUyWTLAvIRh1vFkEw ./out/fuzz-journald-stream ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} 6AdGwIiI3l1Edu9V4fvF ./out/fuzz-env-file ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} 7ubB4DVu2EiYgPVtRUNV ./out/fuzz-calendarspec ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} 8D0NrVtSwTpl23a9k0vv ./out/fuzz-nspawn-oci ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} 8tbrzwxsaIPalIRBHtK8 ./out/fuzz-link-parser ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} 9T5He9cANxHTBLaBURpz ./out/fuzz-journald-kmsg ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} BRaEBuU7QVlSp1HOjlDb ./out/fuzz-udev-database ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} DcE70rAA2mhrxdyBRH90 ./out/fuzz-udev-rules ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} KH6VEpV0ZoWynASJHm8z ./out/fuzz-dhcp6-client ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} MZNs1JG5UQstaIvfHYgb ./out/fuzz-netdev-parser ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} P1MpkewCNQCYLdMFggnU ./out/fuzz-journald-audit ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} RmD47BxVRbAZlq07XW30 ./out/fuzz-unit-file ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} S0dGMaaGwkvsLc0IqIJ7 ./out/fuzz-catalog ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} X7qIoGLAoBgjVf19SfvY ./out/fuzz-compress ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} YAfecldFs2xaXn0Ws1BE ./out/fuzz-dns-packet ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} bgRZAE9E5uXRbUX76tId ./out/fuzz-ndisc-rs ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} cXCm75EhdDf5t2sSBLRC ./out/fuzz-hostname-util ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} cbgsYEyX6776MHFotO9O ./out/fuzz-nspawn-settings ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} d8lokp0LCLYgQwI7vyx6 ./out/fuzz-journald-native-fd ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} eoc9rbm2jKqIEg6Kdonv ./out/fuzz-network-parser ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} ezQIlJWCX3xPUJdhLnWM ./out/fuzz-dhcp-server ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} ge3eTzephghWD3Stw2TE ./out/fuzz-journald-syslog ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} nPIt1SCDkGkSFDth5RlG ./out/fuzz-json ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} nU0lRNNkQrXirDMNOpR1 ./out/fuzz-varlink ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} pzrzgLQY2cG8Iexb0tOt ./out/fuzz-journal-remote ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} qCWFcENjlfWJX0Q3cIOT ./out/fuzz-journald-native ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} s7d3LuRbkETCPSyxUvW8 ./out/fuzz-time-util ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} udjVYJfH4N01vaHNF5Kv ./out/fuzz-lldp ${FUZZIT_ADDITIONAL_FILES}
+./fuzzit c job ${FUZZIT_ARGS} vbYVccyWoDdgqzrQeln8 ./out/fuzz-bus-message ${FUZZIT_ADDITIONAL_FILES}
python3-pyparsing
python3-setuptools
qemu-system-x86
+ quota
strace
unifont
util-linux)
+++ /dev/null
-# SPDX-License-Identifier: LGPL-2.1+
-#
-# This file is part of systemd.
-#
-# systemd is free software; you can redistribute it and/or modify it
-# under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation; either version 2.1 of the License, or
-# (at your option) any later version.
-
-[Unit]
-Description=@RC_LOCAL_SCRIPT_PATH_STOP@ Compatibility
-ConditionFileIsExecutable=@RC_LOCAL_SCRIPT_PATH_STOP@
-DefaultDependencies=no
-After=shutdown.target
-Before=final.target
-
-[Service]
-Type=oneshot
-ExecStart=@RC_LOCAL_SCRIPT_PATH_STOP@
-TimeoutSec=0
-StandardOutput=tty
-RemainAfterExit=yes
[Service]
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID CAP_MAC_OVERRIDE
+DeviceAllow=char-* rw
ExecStart=@rootlibexecdir@/systemd-journald
FileDescriptorStoreMax=4224
IPAddressDeny=any
[Service]
BusName=org.freedesktop.login1
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG CAP_LINUX_IMMUTABLE
+DeviceAllow=char-/dev/console rw
+DeviceAllow=char-drm rw
+DeviceAllow=char-input rw
+DeviceAllow=char-tty rw
+DeviceAllow=char-vcs rw
ExecStart=@rootlibexecdir@/systemd-logind
FileDescriptorStoreMax=512
IPAddressDeny=any
RestrictSUIDSGID=yes
RuntimeDirectory=systemd/sessions systemd/seats systemd/users systemd/inhibit systemd/shutdown
RuntimeDirectoryPreserve=yes
+StateDirectory=systemd/linger
SystemCallArchitectures=native
SystemCallErrorNumber=EPERM
SystemCallFilter=@system-service
[Service]
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW
+DeviceAllow=char-* rw
ExecStart=!!@rootlibexecdir@/systemd-networkd
LockPersonality=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
SystemCallFilter=@system-service @mount
+SystemCallErrorNumber=EPERM
SystemCallArchitectures=native
LockPersonality=yes
IPAddressDeny=any
[Service]
BusName=org.freedesktop.timedate1
CapabilityBoundingSet=CAP_SYS_TIME
+DeviceAllow=char-rtc r
ExecStart=@rootlibexecdir@/systemd-timedated
IPAddressDeny=any
LockPersonality=yes