]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
Merge pull request #12750 from keszybz/tmpfiles-c-copy
authorLennart Poettering <lennart@poettering.net>
Thu, 11 Jul 2019 16:13:19 +0000 (18:13 +0200)
committerGitHub <noreply@github.com>
Thu, 11 Jul 2019 16:13:19 +0000 (18:13 +0200)
Make tmpfiles C use --root

544 files changed:
.github/FUNDING.yml [new file with mode: 0644]
.travis.yml
NEWS
README.md
TODO
docs/AUTOMATIC_BOOT_ASSESSMENT.md
docs/BLOCK_DEVICE_LOCKING.md
docs/CGROUP_DELEGATION.md
docs/CODING_STYLE.md
docs/ENVIRONMENT.md
docs/HACKING.md
docs/var-log/README [new file with mode: 0644]
docs/var-log/README.in [deleted file]
docs/var-log/meson.build
fuzzbuzz.yaml [moved from fuzz.yaml with 100% similarity]
hwdb/60-sensor.hwdb
man/custom-entities.ent.in
man/kernel-command-line.xml
man/resolved.conf.xml
man/sd_bus_add_object_vtable.xml
man/sd_bus_creds_get_pid.xml
man/sd_journal_has_runtime_files.xml
man/sd_notify.xml
man/systemctl.xml
man/systemd-analyze.xml
man/systemd-debug-generator.xml
man/systemd-gpt-auto-generator.xml
man/systemd-hibernate-resume-generator.xml
man/systemd-nspawn.xml
man/systemd-system.conf.xml
man/systemd-veritysetup-generator.xml
man/systemd.automount.xml
man/systemd.exec.xml
man/systemd.link.xml
man/systemd.mount.xml
man/systemd.net-naming-scheme.xml
man/systemd.netdev.xml
man/systemd.network.xml
man/systemd.path.xml
man/systemd.resource-control.xml
man/systemd.service.xml
man/systemd.slice.xml
man/systemd.socket.xml
man/systemd.swap.xml
man/systemd.target.xml
man/systemd.timer.xml
man/systemd.unit.xml
man/systemd.xml
man/tmpfiles.d.xml
man/udevadm.xml
meson.build
meson_options.txt
semaphoreci/semaphore-runner.sh
shell-completion/bash/resolvectl
shell-completion/bash/systemd-analyze
shell-completion/bash/systemd-resolve
shell-completion/bash/udevadm
shell-completion/zsh/_systemctl.in
src/analyze/analyze-condition.c [new file with mode: 0644]
src/analyze/analyze-condition.h [new file with mode: 0644]
src/analyze/analyze-security.c
src/analyze/analyze.c
src/analyze/meson.build
src/basic/blockdev-util.c
src/basic/btrfs-util.c
src/basic/cap-list.c
src/basic/capability-util.c
src/basic/capability-util.h
src/basic/cgroup-util.c
src/basic/conf-files.c
src/basic/copy.c
src/basic/env-util.c
src/basic/errno-util.h
src/basic/extract-word.c
src/basic/extract-word.h
src/basic/fileio.c
src/basic/format-util.c
src/basic/format-util.h
src/basic/fs-util.c
src/basic/hashmap.c
src/basic/hashmap.h
src/basic/io-util.c
src/basic/io-util.h
src/basic/log.c
src/basic/log.h
src/basic/macro.h
src/basic/memory-util.h
src/basic/missing_syscall.h
src/basic/namespace-util.c
src/basic/parse-util.c
src/basic/parse-util.h
src/basic/path-util.c
src/basic/path-util.h
src/basic/proc-cmdline.c
src/basic/rm-rf.c
src/basic/rm-rf.h
src/basic/siphash24.c
src/basic/siphash24.h
src/basic/string-util.c
src/basic/string-util.h
src/basic/strv.c
src/basic/terminal-util.c
src/basic/terminal-util.h
src/basic/time-util.c
src/basic/time-util.h
src/basic/tmpfile-util.c
src/basic/unit-def.c
src/basic/unit-def.h
src/basic/virt.c
src/boot/bless-boot.c
src/boot/bootctl.c
src/boot/efi/boot.c
src/boot/efi/loader-features.h [new file with mode: 0644]
src/busctl/busctl-introspect.c
src/cgls/cgls.c
src/cgtop/cgtop.c
src/core/bpf-firewall.c
src/core/bpf-firewall.h
src/core/cgroup.c
src/core/cgroup.h
src/core/dbus-cgroup.c
src/core/dbus-execute.c
src/core/dbus-execute.h
src/core/dbus-manager.c
src/core/dbus-service.c
src/core/dbus-unit.c
src/core/dbus-unit.h
src/core/device.c
src/core/execute.c
src/core/execute.h
src/core/ip-address-access.c
src/core/ip-address-access.h
src/core/job.c
src/core/job.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/core/main.c
src/core/manager.c
src/core/manager.h
src/core/meson.build
src/core/namespace.c
src/core/path.c
src/core/selinux-access.c
src/core/service.c
src/core/service.h
src/core/show-status.c
src/core/show-status.h
src/core/socket.c
src/core/system.conf.in
src/core/timer.c
src/core/transaction.c
src/core/unit-printf.c
src/core/unit.c
src/core/unit.h
src/core/user.conf.in [moved from src/core/user.conf with 96% similarity]
src/coredump/coredump.c
src/coredump/coredumpctl.c
src/coredump/stacktrace.c
src/coredump/stacktrace.h
src/cryptsetup/cryptsetup-generator.c
src/cryptsetup/cryptsetup.c
src/debug-generator/debug-generator.c
src/delta/delta.c
src/firstboot/firstboot.c
src/fsck/fsck.c
src/getty-generator/getty-generator.c
src/gpt-auto-generator/gpt-auto-generator.c
src/hibernate-resume/hibernate-resume-generator.c
src/import/curl-util.c
src/import/curl-util.h
src/import/import-common.c
src/import/import-fs.c
src/import/import-tar.c
src/import/pull-common.c
src/import/pull-job.c
src/journal-remote/browse.html
src/journal-remote/journal-gatewayd.c
src/journal-remote/journal-remote-main.c
src/journal-remote/journal-remote-parse.c
src/journal-remote/meson.build
src/journal/journal-file.c
src/journal/journal-vacuum.c
src/journal/journalctl.c
src/journal/journald-audit.c
src/journal/journald-native.c
src/journal/journald-server.c
src/journal/lookup3.c
src/journal/meson.build
src/journal/sd-journal.c
src/journal/test-journal-interleaving.c
src/libsystemd-network/dhcp-identifier.c
src/libsystemd-network/dhcp-internal.h
src/libsystemd-network/dhcp-option.c
src/libsystemd-network/dhcp-server-internal.h
src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/lldp-internal.h
src/libsystemd-network/ndisc-internal.h
src/libsystemd-network/network-internal.c
src/libsystemd-network/network-internal.h
src/libsystemd-network/radv-internal.h
src/libsystemd-network/sd-dhcp-client.c
src/libsystemd-network/sd-dhcp6-lease.c
src/libsystemd-network/sd-ipv4acd.c
src/libsystemd-network/sd-ipv4ll.c
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h
src/libsystemd/sd-bus/bus-control.c
src/libsystemd/sd-bus/bus-control.h
src/libsystemd/sd-bus/bus-creds.c
src/libsystemd/sd-bus/bus-error.c
src/libsystemd/sd-bus/bus-internal.h
src/libsystemd/sd-bus/bus-match.c
src/libsystemd/sd-bus/bus-message.c
src/libsystemd/sd-bus/bus-message.h
src/libsystemd/sd-bus/bus-socket.c
src/libsystemd/sd-bus/sd-bus.c
src/libsystemd/sd-bus/test-bus-chat.c
src/libsystemd/sd-bus/test-bus-error.c
src/libsystemd/sd-bus/test-bus-marshal.c
src/libsystemd/sd-device/device-enumerator.c
src/libsystemd/sd-device/device-private.c
src/libsystemd/sd-device/device-private.h
src/libsystemd/sd-device/device-util.h
src/libsystemd/sd-device/sd-device.c
src/libsystemd/sd-event/sd-event.c
src/libsystemd/sd-event/test-event.c
src/libsystemd/sd-netlink/netlink-message.c
src/libsystemd/sd-netlink/netlink-socket.c
src/libsystemd/sd-netlink/netlink-types.c
src/libsystemd/sd-netlink/netlink-types.h
src/libsystemd/sd-netlink/test-netlink.c
src/libsystemd/sd-network/network-util.c
src/libsystemd/sd-network/network-util.h
src/libsystemd/sd-network/sd-network.c
src/libsystemd/sd-path/sd-path.c
src/libsystemd/sd-resolve/sd-resolve.c
src/libudev/libudev-device-internal.h
src/libudev/libudev-device.c
src/libudev/libudev-enumerate.c
src/libudev/libudev-hwdb.c
src/libudev/libudev-list-internal.h
src/libudev/libudev-list.c
src/locale/keymap-util.c
src/login/71-seat.rules.in
src/login/loginctl.c
src/login/logind-action.c
src/login/logind-brightness.c [new file with mode: 0644]
src/login/logind-brightness.h [new file with mode: 0644]
src/login/logind-dbus.c
src/login/logind-dbus.h [new file with mode: 0644]
src/login/logind-device.c
src/login/logind-inhibit.c
src/login/logind-seat-dbus.c
src/login/logind-seat-dbus.h [new file with mode: 0644]
src/login/logind-seat.c
src/login/logind-seat.h
src/login/logind-session-dbus.c
src/login/logind-session-dbus.h [new file with mode: 0644]
src/login/logind-session-device.c
src/login/logind-session.c
src/login/logind-session.h
src/login/logind-user-dbus.c
src/login/logind-user-dbus.h [new file with mode: 0644]
src/login/logind-user.c
src/login/logind-user.h
src/login/logind.c
src/login/logind.h
src/login/meson.build
src/login/org.freedesktop.login1.conf
src/login/pam_systemd.c
src/login/pam_systemd.sym
src/machine/machine-dbus.c
src/machine/machine.c
src/machine/machine.h
src/mount/mount-tool.c
src/network/meson.build
src/network/netdev/macsec.c
src/network/netdev/netdev-gperf.gperf
src/network/netdev/netdev.c
src/network/netdev/netdev.h
src/network/netdev/tunnel.c
src/network/netdev/tunnel.h
src/network/netdev/wireguard.c
src/network/netdev/xfrm.c [new file with mode: 0644]
src/network/netdev/xfrm.h [new file with mode: 0644]
src/network/networkctl.c
src/network/networkd-address.c
src/network/networkd-brvlan.c
src/network/networkd-brvlan.h
src/network/networkd-dhcp-common.c [new file with mode: 0644]
src/network/networkd-dhcp-common.h [new file with mode: 0644]
src/network/networkd-dhcp-server.c [new file with mode: 0644]
src/network/networkd-dhcp-server.h [new file with mode: 0644]
src/network/networkd-dhcp4.c
src/network/networkd-dhcp4.h [new file with mode: 0644]
src/network/networkd-dhcp6.c
src/network/networkd-dhcp6.h [new file with mode: 0644]
src/network/networkd-fdb.c
src/network/networkd-ipv4ll.c
src/network/networkd-ipv4ll.h [new file with mode: 0644]
src/network/networkd-link-bus.c
src/network/networkd-link-bus.h [new file with mode: 0644]
src/network/networkd-link.c
src/network/networkd-link.h
src/network/networkd-manager-bus.c
src/network/networkd-manager.c
src/network/networkd-manager.h
src/network/networkd-ndisc.c
src/network/networkd-network-bus.c
src/network/networkd-network-bus.h [new file with mode: 0644]
src/network/networkd-network-gperf.gperf
src/network/networkd-network.c
src/network/networkd-network.h
src/network/networkd-radv.c
src/network/networkd-radv.h
src/network/networkd-route.c
src/network/networkd-routing-policy-rule.c
src/network/networkd-routing-policy-rule.h
src/network/networkd-speed-meter.c
src/network/test-networkd-conf.c
src/nspawn/nspawn-cgroup.c
src/nspawn/nspawn-mount.c
src/nspawn/nspawn.c
src/partition/growfs.c
src/portable/portable.c
src/portable/portabled.c
src/reply-password/reply-password.c
src/resolve/resolvconf-compat.c
src/resolve/resolvectl.c
src/resolve/resolved-conf.c
src/resolve/resolved-dns-rr.c
src/resolve/resolved-dns-server.c
src/resolve/resolved-dns-server.h
src/resolve/resolved-dns-stream.h
src/resolve/resolved-dns-transaction.c
src/resolve/resolved-dns-trust-anchor.c
src/resolve/resolved-dnstls-gnutls.c
src/resolve/resolved-dnstls-gnutls.h
src/resolve/resolved-dnstls-openssl.c
src/resolve/resolved-dnstls-openssl.h
src/resolve/resolved-dnstls.h
src/resolve/resolved-link.c
src/resolve/resolved-manager.c
src/resolve/resolved-manager.h
src/run/run.c
src/shared/ask-password-api.c
src/shared/bpf-program.c
src/shared/bpf-program.h
src/shared/bus-unit-util.c
src/shared/bus-util.c
src/shared/bus-util.h
src/shared/bus-wait-for-units.c [new file with mode: 0644]
src/shared/bus-wait-for-units.h [new file with mode: 0644]
src/shared/cgroup-show.c
src/shared/condition.c
src/shared/conf-parser.c
src/shared/cpu-set-util.c
src/shared/cpu-set-util.h
src/shared/dev-setup.c
src/shared/dissect-image.c
src/shared/dm-util.c [new file with mode: 0644]
src/shared/dm-util.h [new file with mode: 0644]
src/shared/efivars.h
src/shared/ethtool-util.c [moved from src/udev/net/ethtool-util.c with 66% similarity]
src/shared/ethtool-util.h [moved from src/udev/net/ethtool-util.h with 87% similarity]
src/shared/exec-util.c
src/shared/exec-util.h
src/shared/exit-status.c
src/shared/exit-status.h
src/shared/firewall-util.c
src/shared/format-table.c
src/shared/format-table.h
src/shared/fsck-util.h [new file with mode: 0644]
src/shared/generator.c
src/shared/install.c
src/shared/journal-importer.c
src/shared/journal-importer.h
src/shared/json.c
src/shared/json.h
src/shared/linux/README
src/shared/linux/bpf_insn.h
src/shared/linux/dm-ioctl.h
src/shared/linux/ethtool.h
src/shared/log-link.h
src/shared/logs-show.c
src/shared/machine-image.c
src/shared/meson.build
src/shared/mount-util.c
src/shared/path-lookup.c
src/shared/resolve-util.c
src/shared/resolve-util.h
src/shared/securebits-util.c
src/shared/sleep-config.c
src/shared/udev-util.c
src/shared/varlink.c
src/shared/verbs.c
src/shared/verbs.h
src/shared/vlan-util.c
src/shared/vlan-util.h
src/sleep/sleep.c
src/system-update-generator/system-update-generator.c
src/systemctl/systemctl.c
src/systemd/sd-daemon.h
src/systemd/sd-netlink.h
src/systemd/sd-network.h
src/sysusers/sysusers.c
src/sysv-generator/sysv-generator.c
src/test/meson.build
src/test/test-alloc-util.c
src/test/test-bpf.c
src/test/test-btrfs.c
src/test/test-calendarspec.c
src/test/test-condition.c
src/test/test-conf-files.c
src/test/test-conf-parser.c
src/test/test-engine.c
src/test/test-exec-util.c
src/test/test-extract-word.c
src/test/test-format-util.c [new file with mode: 0644]
src/test/test-journal-importer.c
src/test/test-libudev.c
src/test/test-log.c
src/test/test-path-util.c
src/test/test-procfs-util.c
src/test/test-sleep.c
src/test/test-string-util.c
src/test/test-strv.c
src/test/test-strxcpyx.c
src/tmpfiles/tmpfiles.c
src/tty-ask-password-agent/tty-ask-password-agent.c
src/udev/meson.build
src/udev/net/link-config-gperf.gperf
src/udev/net/link-config.c
src/udev/net/link-config.h
src/udev/net/naming-scheme.c
src/udev/net/naming-scheme.h
src/udev/udev-builtin-net_id.c
src/udev/udev-ctrl.c
src/udev/udev-event.c
src/udev/udev-event.h
src/udev/udev-node.c
src/udev/udev-rules.c
src/udev/udev-rules.h
src/udev/udevadm-control.c
src/udev/udevadm-info.c
src/udev/udevadm-test.c
src/udev/udevadm-trigger.c
src/udev/udevd.c
src/veritysetup/veritysetup-generator.c
sysctl.d/50-coredump.conf.in
test/TEST-01-BASIC/test.sh
test/TEST-02-CRYPTSETUP/test.sh
test/TEST-03-JOBS/test.sh
test/TEST-04-JOURNAL/test.sh
test/TEST-05-RLIMITS/test.sh
test/TEST-06-SELINUX/test.sh
test/TEST-07-ISSUE-1981/test.sh
test/TEST-08-ISSUE-2730/test.sh
test/TEST-09-ISSUE-2691/test.sh
test/TEST-10-ISSUE-2467/test.sh
test/TEST-11-ISSUE-3166/test.sh
test/TEST-12-ISSUE-3171/test.sh
test/TEST-13-NSPAWN-SMOKE/test.sh
test/TEST-14-MACHINE-ID/test.sh
test/TEST-16-EXTEND-TIMEOUT/test.sh
test/TEST-17-UDEV-WANTS/test.sh
test/TEST-18-FAILUREACTION/test.sh
test/TEST-19-DELEGATE/test.sh
test/TEST-20-MAINPIDGAMES/test.sh
test/TEST-23-TYPE-EXEC/test.sh
test/TEST-24-UNIT-TESTS/test.sh
test/TEST-25-IMPORT/test.sh
test/TEST-26-SETENV/test.sh
test/TEST-27-STDOUTFILE/test.sh
test/TEST-28-PERCENTJ-WANTEDBY/test.sh
test/TEST-29-UDEV-ID_RENAMING/test.sh
test/TEST-30-ONCLOCKCHANGE/test.sh
test/TEST-31-DEVICE-ENUMERATION/test.sh
test/TEST-32-OOMPOLICY/test.sh
test/TEST-33-CLEAN-UNIT/Makefile [new symlink]
test/TEST-33-CLEAN-UNIT/test.sh [new file with mode: 0755]
test/TEST-33-CLEAN-UNIT/testsuite.sh [new file with mode: 0755]
test/TEST-34-DYNAMICUSERMIGRATE/Makefile [new symlink]
test/TEST-34-DYNAMICUSERMIGRATE/test.sh [new file with mode: 0755]
test/TEST-34-DYNAMICUSERMIGRATE/testsuite.sh [new file with mode: 0755]
test/a-conj.service [new file with mode: 0644]
test/fuzz/fuzz-dns-packet/crash-497be373856c321a8a7b06589df9b2ff2e0d866a [new file with mode: 0644]
test/fuzz/fuzz-link-parser/directives.link
test/fuzz/fuzz-netdev-parser/27-xfrm.netdev [new file with mode: 0644]
test/fuzz/fuzz-netdev-parser/directives.netdev
test/fuzz/fuzz-network-parser/27-xfrm.network [new file with mode: 0644]
test/fuzz/fuzz-network-parser/directives.network
test/fuzz/fuzz-network-parser/oss-fuzz-15678 [new file with mode: 0644]
test/fuzz/fuzz-unit-file/directives.service
test/i.service [new file with mode: 0644]
test/meson.build
test/test-execute/exec-basic.service
test/test-functions
test/test-network/conf/13-not-match-udev-property.network [new file with mode: 0644]
test/test-network/conf/14-match-udev-property.network [new file with mode: 0644]
test/test-network/conf/23-test1-bond199.network [deleted file]
test/test-network/conf/24-keep-configuration-static.network [new file with mode: 0644]
test/test-network/conf/25-bridge-configure-without-carrier.network [new file with mode: 0644]
test/test-network/conf/25-gre-tunnel-any-any.netdev [new file with mode: 0644]
test/test-network/conf/25-ip6gre-tunnel-any-any.netdev [new file with mode: 0644]
test/test-network/conf/25-ipip-tunnel-any-any.netdev [new file with mode: 0644]
test/test-network/conf/25-ipip-tunnel-independent-loopback.netdev [new file with mode: 0644]
test/test-network/conf/25-neighbor-section.network
test/test-network/conf/25-sit-tunnel-any-any.netdev [new file with mode: 0644]
test/test-network/conf/25-sysctl-disable-ipv6.network
test/test-network/conf/25-tunnel-any-any.network [new file with mode: 0644]
test/test-network/conf/25-vti-tunnel-any-any.netdev [new file with mode: 0644]
test/test-network/conf/25-xfrm-independent.netdev [new file with mode: 0644]
test/test-network/conf/25-xfrm.netdev [new file with mode: 0644]
test/test-network/conf/26-bridge-vlan-master.network [new file with mode: 0644]
test/test-network/conf/26-bridge-vlan-slave.network [new file with mode: 0644]
test/test-network/conf/bridge99-ignore-carrier-loss.network
test/test-network/conf/dhcp-client-keep-configuration-dhcp-on-stop.network [moved from test/test-network/conf/dhcp-client-critical-connection.network with 65% similarity]
test/test-network/conf/dhcp-client-keep-configuration-dhcp.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-use-dns-ipv4-and-ra.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-use-dns-ipv4.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-use-dns-no.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-use-dns-yes.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-use-routes-no.network [new file with mode: 0644]
test/test-network/conf/dhcp-client-with-static-address.network [new file with mode: 0644]
test/test-network/conf/gretun.network
test/test-network/conf/ip6gretun.network
test/test-network/conf/ipip.network
test/test-network/conf/netdev-link-local-addressing-yes.network
test/test-network/conf/sit.network
test/test-network/conf/vti.network
test/test-network/conf/xfrm.network [new file with mode: 0644]
test/test-network/systemd-networkd-tests.py
test/udev-test.pl
travis-ci/managers/fuzzbuzz.sh
travis-ci/managers/fuzzit.sh [new file with mode: 0755]
travis-ci/managers/xenial.sh
units/halt-local.service.in [deleted file]
units/systemd-journald.service.in
units/systemd-logind.service.in
units/systemd-networkd.service.in
units/systemd-portabled.service.in
units/systemd-timedated.service.in

diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644 (file)
index 0000000..ee3b802
--- /dev/null
@@ -0,0 +1 @@
+custom: ['https://spi-inc.org/projects/systemd/', 'https://www.paypal.com/donate/?token=fBGzXDOyIGobZH3oEhYQlYlA61OMRXVnF9XXQqNNehRs-nliAU5XxozIh9z-hlmE-xXC-m']
index ab6fe298c768d937444ed4cd56aacb1ab7cd77e0..d46e2be6f75457d9cc4efd61ab542ece0d06db16 100644 (file)
@@ -11,6 +11,15 @@ env:
         - 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
@@ -91,6 +100,22 @@ jobs:
           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:
@@ -123,8 +148,7 @@ jobs:
           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
diff --git a/NEWS b/NEWS
index 979ee4b5e55fb686ff30e59758bc1e8502096f5e..a7a257476280513f80ae3492bd9478c838a26384 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -70,6 +70,27 @@ CHANGES WITH 243 in spe:
           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.
@@ -83,9 +104,15 @@ CHANGES WITH 243 in spe:
 
         * 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:
@@ -106,6 +133,18 @@ 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
@@ -902,7 +941,7 @@ CHANGES WITH 240:
           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.
index 5ed7cfceac9dec55a9e2ef58e25e7f16382b43e1..1c428b3088b36d8a478c923e7c27862bc99a7e4a 100644 (file)
--- a/README.md
+++ b/README.md
@@ -4,6 +4,7 @@
 <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/>
diff --git a/TODO b/TODO
index 1b91b7c79951962f8d992bf38d82a1897a0ece75..24a2e2590e022c5104500c876299587b9d9efa1f 100644 (file)
--- a/TODO
+++ b/TODO
@@ -24,19 +24,38 @@ Janitorial Clean-ups:
 
 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.
@@ -52,7 +71,7 @@ Features:
   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.
 
@@ -71,8 +90,8 @@ Features:
   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
 
@@ -115,9 +134,6 @@ Features:
   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.
@@ -202,9 +218,6 @@ Features:
   /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
@@ -300,9 +313,6 @@ Features:
 * 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=.
 
@@ -508,8 +518,6 @@ Features:
 
 * 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.
 
index 6f7182a4b3f83ec87ae5d06e8208f55e28573f3f..1ef4bdcfe1c8b956f1bea2b62cc9fba3d3c00c23 100644 (file)
@@ -97,7 +97,7 @@ Here's an example walkthrough of how this all fits together.
    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
index 58178ad2f70b07775bd761fe84f2929855189fb4..5509b41942ca7aa8cb9ee9a293412dbba2a62ba1 100644 (file)
@@ -34,7 +34,7 @@ formatting tools to safely and easily take exclusive ownership of a block
 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
index 8bf1b698fc2b6c116b9a18a120e5050cb605a1d8..607ba6f81094b1ed4b00264c214822d597203da8 100644 (file)
@@ -230,7 +230,7 @@ guarantees:
 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
index d945f8cdbe7f3fb3d59c1e29ed2d3f9586216520..bf7ba3ea69c75594c2c8b3f544bdf35f858691f4 100644 (file)
@@ -201,6 +201,19 @@ title: Coding Style
   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
index f72ed20dd0fc094a2bcbe7c76134df614704eb96..e81eaf2a3551cd40d4423580b648ec721f5c869f 100644 (file)
@@ -217,7 +217,7 @@ systemd itself:
 
 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
index b14be72128538a68f137c3a6bb61a4ae01a0f515..7dc1eb98cbaf91134fb2ef4db6b6f4efbc853656 100644 (file)
@@ -96,8 +96,8 @@ Happy hacking!
 ## 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
@@ -116,6 +116,10 @@ python infra/helper.py build_fuzzers --sanitizer memory systemd ../systemd
 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.
 
diff --git a/docs/var-log/README b/docs/var-log/README
new file mode 100644 (file)
index 0000000..5c6c592
--- /dev/null
@@ -0,0 +1,25 @@
+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
diff --git a/docs/var-log/README.in b/docs/var-log/README.in
deleted file mode 100644 (file)
index 2e64fb1..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-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
index 0ddff20ce56bd2bd1513103d9b78219f776cbe44..81b786543885955d5f46811f64de374b6dc9d4af 100644 (file)
@@ -1,11 +1,6 @@
 # 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
similarity index 100%
rename from fuzz.yaml
rename to fuzzbuzz.yaml
index 979e96bf000c6c46f8db2b4061d4834a9592a46b..df7ad251d0708adc863e5fe1866f876872247e31 100644 (file)
@@ -116,6 +116,9 @@ sensor:modalias:acpi:INVN6500*:dmi:*svn*ASUSTeK*:*pn*TP300LA*
 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
 
@@ -284,6 +287,12 @@ sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:*:svnTobefilledbyO.
 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
 #########################################
index e2bd44e5e785b68f84fceb7334e9b80bc2fa56ab..85805777a0d57727973802d0a26d6b7d3333ccb2 100644 (file)
@@ -8,3 +8,4 @@
 <!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@>
index 40b7766214d75369eb8493bb96d905151c3e4e40..f9408a028d348bb632ce21d775282f1f35685b9b 100644 (file)
@@ -59,6 +59,7 @@
         <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>
index c8ab6942c1409f1394bf467193c11a2699f0ecca..a647a4ace7786018a98ac5a74101acef3efbf8b7 100644 (file)
       <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"
index 6cbb84e7ff856a853eecd644fe737ace427bb7f7..9d7e30a5048457e79b059bc3b0837c6a7a4029df 100644 (file)
     <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>
index 9e79d13cdd150427c1b353c6fd3592dcc484535a..a7690d5817443da6c6b169d9123c18476e5105ac 100644 (file)
     <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
index 4b0075cbe0c562b47ed6406de602d68ecb227a18..7e6e7d4b9d29d1238bc4e859cc17fd2d3e3098eb 100644 (file)
@@ -4,8 +4,6 @@
 
 <!--
   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">
index 00640cb290d1f0bf6c42d9c8a727e255f45986c7..3046ca88ee728c703d7b411406db05b92c564ecd 100644 (file)
         <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>
index d991e979f153e537a907fd78e0ad9f8fd9079a68..b2e3cbcb214d9085ddafd8721d6d672806acc9fb 100644 (file)
         </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>
@@ -904,6 +919,24 @@ Sun 2017-02-26 20:57:49 EST  2h 3min left  Sun 2017-02-26 11:56:36 EST  6h ago
             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>
 
@@ -1341,7 +1374,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
                   </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>
index 651a73848ee983f74b65e8902ee27564ee07d42b..5dce2ae8fb58368ac6e68f49a7300a71c472e216 100644 (file)
       <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>
@@ -348,6 +354,33 @@ $ eog targets.svg</programlisting>
       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 = ! &lt;4.0' \
+        'ConditionKernelVersion = &gt;=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=&gt;=5.1 succeeded.
+test.service: ConditionKernelVersion=!&lt;4.0 succeeded.
+Conditions succeeded.</programlisting>
+      </example>
+    </refsect2>
+
     <refsect2>
       <title><command>systemd-analyze syscall-filter <optional><replaceable>SET</replaceable>...</optional></command></title>
 
index 1f9a79db82f24a7e3bc38151e605b97f6ebcea7c..305dc2ff371bb92020e90673d5426ecb88f7361f 100644 (file)
@@ -1,7 +1,10 @@
 <?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
index d94d6ac715317888b3c44c6583c1a5a6fc33bfba..22cd638f1f81fe9f8e4ab8198b8c276cec6d58b8 100644 (file)
     <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
index ff105d435c3da121125e826b6369665f5ba0908a..f532a19a48a387178bcc225ee39a901973d8ec6a 100644 (file)
         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>
 
index f1d6f14812519b6765078d71fcdb17853e4f85c1..8c1a1e6871026edb790bf452e70079d7d2baa691 100644 (file)
       <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
index f5d419c5196a4b30ba3bd46b5250a9bdf5419a58..5b80479a0a1c85f8efea6936773381ea1c4d19fc 100644 (file)
         <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>
 
index 305dda4b8e8f4eab20a476dc4fc4c38e33a00dad..bcacd59cf965ddf1c7f28174e16ee5ef59f96b2f 100644 (file)
     <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
@@ -61,7 +61,7 @@
         <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
@@ -75,8 +75,8 @@
         <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>
index 48deb0220e419f0c8938eab3cf121e9d7ed93340..75302e07e9fd15919c8ec12f2e5f66f691a99871 100644 (file)
@@ -35,9 +35,9 @@
     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
index d65b842f44c00954c8b1a168d1e11b85d62cd756..48dd42ca3cf557cc82eaaf4cf080e3c624f06521 100644 (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
@@ -761,6 +762,28 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         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>
 
@@ -949,6 +972,20 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
         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),
@@ -1517,24 +1554,29 @@ RestrictNamespaces=~cgroup net</programlisting>
       <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
@@ -1694,6 +1736,22 @@ RestrictNamespaces=~cgroup net</programlisting>
 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>,
@@ -1706,11 +1764,13 @@ SystemCallErrorNumber=EPERM</programlisting>
       <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>
@@ -1935,12 +1995,12 @@ SystemCallErrorNumber=EPERM</programlisting>
       <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>
 
@@ -1951,23 +2011,20 @@ SystemCallErrorNumber=EPERM</programlisting>
         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
@@ -1996,13 +2053,14 @@ SystemCallErrorNumber=EPERM</programlisting>
         <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" &gt; /dev/stderr</command> for writing text to stderr will not work. To mitigate this use the construct
-        <command>echo "hello" >&amp;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" &gt; /dev/stderr</command> for
+        writing text to stderr will not work. To mitigate this use the construct <command>echo "hello"
+        >&amp;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
@@ -2120,12 +2178,12 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
       <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>
@@ -2136,12 +2194,13 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
         <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>
@@ -2153,7 +2212,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
         <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
@@ -2166,12 +2225,13 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
         <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>
 
@@ -2917,6 +2977,12 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
             <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>
index b1be32955e5f8aeb5e2688f4c09e38378617484d..7ea9a71107bfb4f72720a43283f66d79323d04cd 100644 (file)
         <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>
index 178dc188a5a2a35ea096cc96b2d0027e398834d0..a72a33240d94e16bc256cf4bd5c6a2114043e952 100644 (file)
@@ -34,9 +34,9 @@
     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>,
index eb8faad254eb99d903264de2e8823aad3cbdac00..91ad57df03fbd76a360f72defd51e25bdc873c53 100644 (file)
   <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>
index d083fb9a6f406e52e595052a5fc72a213292d26f..ff37f26990cfb3de8648a2575efbc15e51c8c583 100644 (file)
           <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
@@ -2048,6 +2088,16 @@ PublicKey=RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=
 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>
index 695bcaa0a47020475e8056457cff8d6891e7cb25..c544315601e1c94a23038036b5447b865247d5b1 100644 (file)
           <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>
@@ -2315,6 +2363,29 @@ Name=enp0s25
 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>
index de284d877e0030cb37f6c9e8eb1d9395a8e1b039..39cca8cf514faf863112c2731f919d06d85e0423 100644 (file)
@@ -34,9 +34,9 @@
     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,
index 95209a8a6aa39b1f1a1ce647d610a31f8bdc2132..e7b5dfbce67cc605c50e31a6b84507ad9385b138 100644 (file)
         </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>
 
index c2b3e21076c87c807a2acea9b3e1ce9ce02a0bf7..145f97206c5fb3f0acaafb235c332f605f702c11 100644 (file)
         </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>
index 5019bf9976a4e442685d76b75d18fcad146b1d6d..7157dfa32d390ed3f4dda254f64146b0f4d78f70 100644 (file)
@@ -55,9 +55,9 @@
     <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>
index 84faf89c2e668162a2270c6a4eadaf81fc7b954b..60ea63f742a2766938cb6083223c394308873808 100644 (file)
@@ -35,9 +35,9 @@
     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>,
index d2a9123b0b06db509b56cdc3434559fb6c25bd9e..23547bb273c8e6b875b47dec7446239d4bd9a1f2 100644 (file)
@@ -37,9 +37,9 @@
     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>,
index a706a4588af78c3330901f045e101eb44825dfe7..3052b177864c56f5123ba1b4ef642c64388083f6 100644 (file)
@@ -34,8 +34,8 @@
     <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
index 13b9ed35d2267a639afe43631c9b8d1829ebf4c3..0f6518dbc2d94006980f647b10b2fa5f7297a295 100644 (file)
@@ -35,9 +35,9 @@
     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>
index d13212ebcc51bda59bffb885a4055113aaf773a9..0ac9ff4882212d97b6c05d7ad282f384a203101b 100644 (file)
         <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>&lt;</literal>, <literal>&lt;=</literal>, <literal>=</literal>,
-        <literal>!=</literal>, <literal>&gt;=</literal>, <literal>&gt;</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>&lt;</literal>,
+        <literal>&lt;=</literal>, <literal>=</literal>, <literal>!=</literal>, <literal>&gt;=</literal>,
+        <literal>&gt;</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>
index c51f0bf4d1f407a19a16059e89a72260a7b4d7e0..41c0f5f643eb2e1b6e21542d240618f7238e3468 100644 (file)
       <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>
@@ -83,9 +89,8 @@
       <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>
index 26afc116b77d3e72badba2e914f3f946c35a2208..f097045b7bbeea4c8420695aa77ac536e8576689 100644 (file)
@@ -526,6 +526,14 @@ w- /proc/sys/vm/swappiness - - - - 10</programlisting></para>
       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>
index 396d418f831f6be347b7e6884e4d7deaba608e3f..d12c39b7651a4c3a7cd781bd2406496f0140be10 100644 (file)
             <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>
index 30e4743489814571f2ecbf5ba80eec4ef23d9ac5..93d69146275cb7d9ab18bc6cc20c007f45a7dbd6 100644 (file)
@@ -29,6 +29,10 @@ substs.set('PROJECT_VERSION',      meson.project_version())
 # 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')
@@ -113,7 +117,6 @@ pkgconfiglibdir = get_option('pkgconfiglibdir') == '' ? join_paths(libdir, 'pkgc
 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'
@@ -186,6 +189,7 @@ if pamconfdir == ''
 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'))
@@ -239,6 +243,7 @@ conf.set_quoted('USER_KEYRING_PATH',                          join_paths(pkgsysc
 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)
@@ -272,7 +277,6 @@ substs.set('systemenvgeneratordir',                           systemenvgenerator
 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'))
@@ -280,6 +284,7 @@ substs.set('SYSTEM_SYSVINIT_PATH',                            sysvinit_path)
 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())
 
@@ -496,6 +501,10 @@ foreach ident : [
                                  #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')
@@ -785,11 +794,11 @@ conf.set_quoted('GETTEXT_PACKAGE', meson.project_name())
 
 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
@@ -797,8 +806,6 @@ foreach name : get_option('debug-extra')
                 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
@@ -806,7 +813,6 @@ endforeach
 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'))
@@ -985,24 +991,24 @@ if want_libidn == 'true' and want_libidn2 == 'true'
         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
@@ -1203,7 +1209,7 @@ if dns_over_tls != 'false'
                 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
@@ -1221,7 +1227,7 @@ if skip_deps
         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',
@@ -1369,6 +1375,7 @@ config_h = configure_file(
 meson_apply_m4 = find_program('tools/meson-apply-m4.sh')
 
 includes = include_directories('src/basic',
+                               'src/boot',
                                'src/shared',
                                'src/systemd',
                                'src/journal',
@@ -3195,7 +3202,6 @@ foreach tuple : [
         ['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')],
index 494a8de43c487ec17f145ea0a6a411f549267473..ae2930861f18dab9b54c0111f0906354f59ec128 100644 (file)
@@ -46,7 +46,7 @@ option('debug-shell', type : 'string', value : '/bin/sh',
        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')
@@ -95,6 +95,8 @@ option('timesyncd', type : 'boolean',
        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'],
@@ -161,6 +163,9 @@ option('default-hierarchy', type : 'combo',
 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',
@@ -206,7 +211,7 @@ option('default-dnssec', type : 'combo',
        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')
index 1951dcbbada6a8e9fe6bdf33025ac99e8b8fb4ea..41454b474aea74b390de2bd724544b6fc3a63ad2 100755 (executable)
@@ -34,7 +34,7 @@ create_container() {
 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
index 161ac4d3c577a38c8b8b31063c958592b1bcc85e..d4ac339038126371fc8cbc30478b1b555361fdab 100644 (file)
@@ -53,7 +53,7 @@ _resolvectl() {
         [FAMILY]='tcp udp sctp'
         [RESOLVE]='yes no resolve'
         [DNSSEC]='yes no allow-downgrade'
-        [DNSOVERTLS]='no opportunistic'
+        [DNSOVERTLS]='yes no opportunistic'
     )
     local interfaces=$( __get_interfaces )
 
index b80e2260fc8426ca591b3d63bfe1a21cd931081d..cb73f17c0c896f0fddf0947b95c1c1395f346ea6 100644 (file)
@@ -55,7 +55,7 @@ _systemd_analyze() {
     )
 
     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'
index 84747b7462f31ab9ee45fee48d97b9d2aaa55578..66f0ae04a0a54abd9061df1f0f1039bc808f446e 100644 (file)
@@ -64,7 +64,7 @@ _systemd-resolve() {
                 comps="yes no allow-downgrade"
                 ;;
             --set-dnsovertls)
-                comps="no opportunistic"
+                comps="yes no opportunistic"
                 ;;
         esac
         COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
index c151a419780e04a4ce109d7d385e4616f0d5d016..cb12c25538f1d94995fba72045b10c9d147d3b92 100644 (file)
@@ -119,7 +119,7 @@ _udevadm() {
                         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 )
@@ -196,7 +196,7 @@ _udevadm() {
             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'
index 07db6d47ce0b5877e82fca9db52bdfe50796ee35..67c1c2b327a773dc1c292dd819fe473bd44606c4 100644 (file)
@@ -35,6 +35,7 @@
         "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"
diff --git a/src/analyze/analyze-condition.c b/src/analyze/analyze-condition.c
new file mode 100644 (file)
index 0000000..d0cefa0
--- /dev/null
@@ -0,0 +1,155 @@
+/* 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;
+}
diff --git a/src/analyze/analyze-condition.h b/src/analyze/analyze-condition.h
new file mode 100644 (file)
index 0000000..2ef278e
--- /dev/null
@@ -0,0 +1,6 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+#include "install.h"
+
+int verify_conditions(char **lines, UnitFileScope scope);
index 0962950dd0bbee88c5f6a652c8fb3879c23d8b3c..3cf6515f5f36c595d848720d3cb87536cec35cff 100644 (file)
@@ -45,6 +45,9 @@ struct security_info {
         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;
@@ -590,7 +593,10 @@ static int assess_ip_address_allow(
         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) {
@@ -1824,6 +1830,33 @@ static int property_read_ip_address_allow(
         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,
@@ -1873,6 +1906,8 @@ static int acquire_security_info(sd_bus *bus, const char *name, struct security_
                 { "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)                },
index 5217a92b438583fb2148c7d3b83b7161d9c963e9..97642660e49f4a2da6dafbbf7a4eba9ee8115c8c 100644 (file)
@@ -13,6 +13,7 @@
 #include "sd-bus.h"
 
 #include "alloc-util.h"
+#include "analyze-condition.h"
 #include "analyze-security.h"
 #include "analyze-verify.h"
 #include "build.h"
@@ -543,7 +544,7 @@ static int pretty_boot_time(sd_bus *bus, char **_buf) {
         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,
@@ -1897,6 +1898,10 @@ static int service_watchdogs(int argc, char *argv[], void *userdata) {
         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);
 }
@@ -1955,6 +1960,7 @@ static int help(int argc, char *argv[], void *userdata) {
                "  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"
@@ -2157,6 +2163,7 @@ static int run(int argc, char *argv[]) {
                 { "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         },
index 4db4dfa5526ad72bc38e05538e6e318a18702c25..58760d609b36650bd45ac964b4dd0538dd288686 100644 (file)
@@ -2,6 +2,8 @@
 
 systemd_analyze_sources = files('''
         analyze.c
+        analyze-condition.c
+        analyze-condition.h
         analyze-verify.c
         analyze-verify.h
         analyze-security.c
index 0d7ea83b7577a46a4e6f88b13234aab3ec67ec67..0ee14cbc0b2927582ef738652056b078dbd955f5 100644 (file)
@@ -21,6 +21,9 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
 
         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) {
@@ -31,7 +34,7 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
         /* 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);
@@ -46,10 +49,10 @@ int block_get_whole_disk(dev_t d, dev_t *ret) {
         /* 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) {
@@ -115,11 +118,11 @@ int block_get_originating(dev_t dt, dev_t *ret) {
                          * 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;
 
index db6c0c3f1e449930d774b81086b8daa428e13b36..10ca893da016fb9430327ee489519f6a101fea49 100644 (file)
@@ -1530,7 +1530,7 @@ static int subvol_snapshot_children(
                         if (old_child_fd < 0)
                                 return -errno;
 
-                        np = strjoin(subvolume, "/", ino_args.name);
+                        np = path_join(subvolume, ino_args.name);
                         if (!np)
                                 return -ENOMEM;
 
index 29a17d9320e36fb4bd796ec3c112d61637923ee3..79d6a40aa9eb48d54a7175570c44e2a3d62464cf 100644 (file)
@@ -62,7 +62,7 @@ int capability_set_to_string_alloc(uint64_t set, char **s) {
 
         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;
@@ -100,7 +100,7 @@ int capability_set_from_string(const char *s, uint64_t *set) {
                 _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)
index 2a9c3b80f8150a7bc31adc36f5d3a18e9356f63b..d62bb62de4773501f767b7d449bbc6111f27a652 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "alloc-util.h"
 #include "capability-util.h"
+#include "cap-list.h"
 #include "fileio.h"
 #include "log.h"
 #include "macro.h"
@@ -90,7 +91,7 @@ int capability_update_inherited_set(cap_t caps, uint64_t set) {
         /* 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;
@@ -126,7 +127,7 @@ int capability_ambient_set_apply(uint64_t set, bool also_inherit) {
                         return -errno;
         }
 
-        for (i = 0; i < cap_last_cap(); i++) {
+        for (i = 0; i <= cap_last_cap(); i++) {
 
                 if (set & (UINT64_C(1) << i)) {
 
@@ -364,6 +365,43 @@ bool ambient_capabilities_supported(void) {
         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;
index e69b2fbb957854da3ec0eb52ca977729155e4e3d..b5bce29ab53841d672a7d0871ff95fd917c5df4f 100644 (file)
@@ -69,4 +69,9 @@ static inline bool capability_quintet_is_set(const CapabilityQuintet *q) {
                 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);
index e6a8b0e0fb2423dfce21b3603eb981ba64c28141..9b145aa70f4c8bafa45ca47077e2af6f965568f2 100644 (file)
 #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;
 
@@ -62,6 +62,10 @@ int cg_enumerate_processes(const char *controller, const char *path, FILE **_f)
         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;
 
@@ -91,10 +95,9 @@ int cg_read_event(
                 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);
@@ -105,22 +108,33 @@ int cg_read_event(
         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) {
@@ -211,14 +225,15 @@ int cg_rmdir(const char *controller, const char *path) {
         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;
@@ -249,7 +264,7 @@ int cg_kill(
                 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;
@@ -312,6 +327,31 @@ int cg_kill(
         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,
@@ -348,7 +388,7 @@ int cg_kill_recursive(
         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;
@@ -482,7 +522,7 @@ int cg_migrate_recursive(
         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;
@@ -570,13 +610,13 @@ static int join_path_legacy(const char *controller, const char *path, const char
         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;
 
@@ -592,11 +632,11 @@ static int join_path_unified(const char *path, const char *suffix, char **fs) {
         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;
 
@@ -623,7 +663,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch
                 else if (!path)
                         t = strdup(suffix);
                 else
-                        t = strjoin(path, "/", suffix);
+                        t = path_join(path, suffix);
                 if (!t)
                         return -ENOMEM;
 
@@ -1227,7 +1267,7 @@ int cg_is_empty_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;
index 7c85022f08ee859080254b4110ff5d0a0447359a..3f36b5cc340d191634bed5490d5cdf616ff7e152 100644 (file)
@@ -115,7 +115,7 @@ static int files_add(
 
                         key = p;
                 } else {
-                        p = strjoin(dirpath, "/", de->d_name);
+                        p = path_join(dirpath, de->d_name);
                         if (!p)
                                 return -ENOMEM;
 
@@ -207,7 +207,7 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p
                                 _cleanup_free_ char *rdir = NULL;
                                 char *p1, *p2;
 
-                                rdir = prefix_root(root, *dir);
+                                rdir = path_join(root, *dir);
                                 if (!rdir)
                                         return -ENOMEM;
 
@@ -221,7 +221,7 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p
                                 if (p2) {
                                         /* Our new entry has higher priority */
 
-                                        t = prefix_root(root, path);
+                                        t = path_join(root, path);
                                         if (!t)
                                                 return log_oom();
 
@@ -238,7 +238,7 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p
         }
 
         /* 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;
 
@@ -313,7 +313,7 @@ int conf_files_list_with_replacement(
                 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();
         }
index eed9cfdff79437ee9578bd33378f8bd53a224c78..36ea7821f23d1a78eea1df3871e57564ae8d4be9 100644 (file)
@@ -566,7 +566,7 @@ static int fd_copy_directory(
 
                 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;
 
index 896eec58356a0673ec7feb1a951e0b0839123bea..b39b1ed0f8bd16e83ce71edf4da2a24659089468 100644 (file)
@@ -688,7 +688,7 @@ char **replace_env_argv(char **argv, char **env) {
                         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);
index d7a5ea771f4ffaea05efdf19094797d6fb3f1ede..3ad732b91ee2ea6ad8022491ebd1e178b5563e80 100644 (file)
@@ -1,6 +1,9 @@
 /* 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) {
@@ -28,6 +31,11 @@ static inline int negative_errno(void) {
         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
index a861b5665323bdff09f4fe77c8f4d21652bd3515..34cfb36a4a806ce0ffa646de28c73340af5f684f 100644 (file)
@@ -135,7 +135,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra
                         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)) {
index 705ebbe95bbf38bb41567a2a077b73e39094dd6f..e2d433893ac89acceeb0bfc0a4a5c92fe7f049a8 100644 (file)
@@ -7,7 +7,7 @@ typedef enum ExtractFlags {
         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;
index 4c18212668eddc4a7c466bcda787d3ae20ef5471..40a20c2b3b7918943090e83b0f261a64bbffd484 100644 (file)
@@ -589,10 +589,7 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c
                 _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;
 
index 39ef2fceefb7826584e0cebf8f6cc71c76c53141..aec929a06a80f7e3524deab3041f3fdf818fe6f5 100644 (file)
@@ -1,5 +1,7 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#include <stdio.h>
+
 #include "format-util.h"
 #include "memory-util.h"
 
@@ -8,3 +10,61 @@ char *format_ifname(int ifindex, char buf[static IF_NAMESIZE + 1]) {
         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;
+
+}
index 9925a5e9918dff24e5daa7573fe399f9c772ef30..e0d184a541968f57af54ee9cb462f84fb5c217b8 100644 (file)
@@ -3,6 +3,7 @@
 
 #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);
+}
index 7dfc1b309d9717aefb44996732db47d0b6006c49..3ed8e2c8a9371bf62865fbac4cebb0cc5493433b 100644 (file)
@@ -226,7 +226,6 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
 }
 
 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;
 
@@ -236,11 +235,9 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
          * 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 =
@@ -262,16 +259,16 @@ int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
                 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;
@@ -983,9 +980,9 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
 
                                 /* 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;
 
index c7bd7323a1af4b7d5e7bb119bab99897781108c9..f244d767da72a8b0c5f1d3f796b38d5f4a65912c 100644 (file)
@@ -11,6 +11,7 @@
 #include "macro.h"
 #include "memory-util.h"
 #include "mempool.h"
+#include "missing.h"
 #include "process-util.h"
 #include "random-util.h"
 #include "set.h"
@@ -285,7 +286,11 @@ _destructor_ static void cleanup_pools(void) {
         /* 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);
index 41c8adb16b445baf6ed2d9c75931156340b1bafe..1b071c230e77201d82d397f251fb640d1a8d6f07 100644 (file)
@@ -76,7 +76,7 @@ typedef struct {
 
 #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
index 575398fbe6b31865bd50e99196c91f13c8ea6b31..d19c78c2ca663385d9c8b5bd4a4856fd6bcb23c4 100644 (file)
@@ -262,3 +262,85 @@ char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *f
                 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;
+}
index 792a64ad5ed3beb6e640c78fda34f8e58a4bdaa9..719e19e85d6124e86b9f7f18ff01393297f1fb88 100644 (file)
@@ -73,3 +73,20 @@ static inline bool FILE_SIZE_VALID_OR_INFINITY(uint64_t l) {
 #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);
index a81c350ab4d3ca1945920b426c4b64aeb9af20f9..31ecbd1d1bdbf1852bb17bbad97b46c6b1f18ad4 100644 (file)
@@ -351,7 +351,13 @@ static int write_to_console(
                 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);
         }
 
@@ -1110,11 +1116,11 @@ void log_parse_environment_realm(LogRealm realm) {
 
         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) {
@@ -1232,18 +1238,29 @@ int log_syntax_internal(
         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,
index aa3d5b75f138549320dabbb2f5816db9cc9b862a..2e0179b2a585e34d624c7efce3ced20f26b85448 100644 (file)
@@ -73,6 +73,9 @@ int log_get_max_level_realm(LogRealm realm) _pure_;
  * 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);
@@ -210,7 +213,7 @@ void log_assert_failed_return_realm(
         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, ...)                  \
@@ -218,7 +221,7 @@ void log_assert_failed_return_realm(
                 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);                             \
         })
 
@@ -254,20 +257,20 @@ int log_emergency_level(void);
 /* 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_;
 
@@ -320,7 +323,7 @@ int log_syntax_invalid_utf8_internal(
         ({                                                              \
                 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);                             \
         })
 
@@ -328,7 +331,7 @@ int log_syntax_invalid_utf8_internal(
         ({                                                              \
                 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;                                      \
         })
 
index ae8907db04bc719ecd13c16cbe64d2a570c77dc6..81add201fab21e66e23f258dda46dcbd81a7a889 100644 (file)
@@ -324,12 +324,12 @@ static inline int __coverity_check__(int condition) {
 #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__ */
 
@@ -345,7 +345,7 @@ static inline int __coverity_check__(int condition) {
 
 #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)
index 0e8957b7838ed84f45878c042b9dab963d56ff52..9cb8ac3c10fcac861a76bd67262120d8eeaa804e 100644 (file)
@@ -2,6 +2,7 @@
 #pragma once
 
 #include <inttypes.h>
+#include <malloc.h>
 #include <stdbool.h>
 #include <string.h>
 #include <sys/types.h>
@@ -78,6 +79,16 @@ static inline void* explicit_bzero_safe(void *p, size_t l) {
 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));
index d1aa32218b34ad34df72bc5d8bd843cc93a22838..cd455eb47cda51da4bc69808969a4dce74d13b84 100644 (file)
@@ -444,3 +444,46 @@ static inline ssize_t missing_statx(int dfd, const char *filename, unsigned flag
 
 #  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
index 67bdaa19d5eeadbedbc0a81273bad184fae943c5..1fc593df4091d9b5b68d4f839655783eaa04e40a 100644 (file)
@@ -62,21 +62,19 @@ int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *
         }
 
         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;
 }
index 7774e794d43af05e1bee0466a0c79c96887583f1..115a1494a26596d068f27a4f20e1fde389e10854 100644 (file)
@@ -362,47 +362,6 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
         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;
index 5a05dfeac550d3d317656c817dc67bf0ef3197d7..3a70b792766fb10740036be3586795148eb3de49 100644 (file)
@@ -22,9 +22,6 @@ int parse_range(const char *t, unsigned *lower, unsigned *upper);
 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) {
index 55cb140d873508f2a330083ec3e2feef183b0605..570f88627d6b584fe4e50d5d122335fc6b8ca0ed 100644 (file)
@@ -68,10 +68,7 @@ char *path_make_absolute(const char *p, const char *prefix) {
         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) {
@@ -256,7 +253,7 @@ char **path_strv_resolve(char **l, const char *root) {
 
                 if (root) {
                         orig = *s;
-                        t = prefix_root(root, orig);
+                        t = path_join(root, orig);
                         if (!t) {
                                 enomem = true;
                                 continue;
@@ -581,7 +578,7 @@ int find_binary(const char *name, char **ret) {
                 if (!path_is_absolute(element))
                         continue;
 
-                j = strjoin(element, "/", name);
+                j = path_join(element, name);
                 if (!j)
                         return -ENOMEM;
 
@@ -686,40 +683,6 @@ int mkfs_exists(const char *fstype) {
         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;
@@ -1027,7 +990,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version)
                 _cleanup_free_ char *path = NULL;
                 char *c, **name;
 
-                path = prefix_root(root, pattern);
+                path = path_join(root, pattern);
                 if (!path)
                         return -ENOMEM;
 
index 86c5a577cb46d2db8ad0cf3e1256fe91a5878c15..4a1ed0a1a8b07e9b968dda5d7f60794f3883fae5 100644 (file)
@@ -116,10 +116,8 @@ int mkfs_exists(const char *fstype);
              _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;     \
@@ -127,7 +125,7 @@ char *prefix_root(const char *root, const char *path);
                 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;     \
index b82ca4b21b2e5131d9e6d8c94eded1431449c55c..09169cf96346f8563db98e9282aa9d819b9b9853 100644 (file)
@@ -47,7 +47,7 @@ static int proc_cmdline_extract_first(const char **p, char **ret_word, ProcCmdli
                 _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)
index b751933c83b4c9974d807d9059a19b9278b3a813..796eb93c4b3db0ac9c8607647296d3f892b29d5e 100644 (file)
@@ -161,9 +161,8 @@ int rm_rf(const char *path, RemoveFlags flags) {
         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.",
@@ -175,6 +174,9 @@ int rm_rf(const char *path, RemoveFlags flags) {
                 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;
 
@@ -183,34 +185,45 @@ int rm_rf(const char *path, RemoveFlags flags) {
 
         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;
 }
index d42ebef4349135ff533de1a372b805a4c0a35a9c..40cbff21c04e50697b19244df6dc73955b0b3243 100644 (file)
@@ -6,10 +6,11 @@
 #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);
index 61180819b1bf0f995cddb9ca2ee9f02e1be9bb28..666431aa31fd0592bd4cb9ed72d01803b6d911fc 100644 (file)
@@ -151,6 +151,12 @@ void siphash24_compress(const void *_in, size_t inlen, struct siphash *state) {
         }
 }
 
+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;
 
index 67c4f7560c767cc2922cc64c04ec9e89e0965f99..1937fea2985fa1f43fbbc4efe5d4a025e7424bf7 100644 (file)
@@ -17,6 +17,7 @@ struct siphash {
 
 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);
index 779048904a08f0ed67b3099a59f7497d1eb8f772..474903b2a87b0b96496efd1fe7263edd018b0810 100644 (file)
@@ -1032,20 +1032,6 @@ int free_and_strndup(char **p, const char *s, size_t l) {
         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;
 
index 47b17c9d3e7c07f30ff3687ab8a27580c0973b1c..ef136da49e02bfaf89c7d7d7195628a92b980467 100644 (file)
@@ -197,12 +197,6 @@ static inline int free_and_strdup_warn(char **p, const char *s) {
 }
 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) {
index 21c106149b219910cf5b2dd07b20aecdcc0d9e96..0a1adbf30bd7a0c3d5c801b196019c2105af90ba 100644 (file)
@@ -11,6 +11,7 @@
 #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"
@@ -78,9 +79,9 @@ char **strv_free_erase(char **l) {
         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) {
index 3a0d16a74f59dcf09d9c4f3e434bf555fb04239f..76d6d1a20c9e011ae5c1c14a2e1ac3549c2319d8 100644 (file)
@@ -720,8 +720,7 @@ int get_kernel_consoles(char ***ret) {
 
         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)
@@ -736,17 +735,16 @@ int get_kernel_consoles(char ***ret) {
                                 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;
         }
@@ -1049,7 +1047,34 @@ int ptsname_malloc(int fd, char **ret) {
         }
 }
 
-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
@@ -1068,8 +1093,8 @@ int ptsname_namespace(int pty, char **ret) {
         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;
@@ -1088,18 +1113,13 @@ int openpt_in_namespace(pid_t pid, int flags) {
         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);
@@ -1113,7 +1133,17 @@ int openpt_in_namespace(pid_t pid, int flags) {
         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) {
index 87d09f87598a1dc53f7f49223a8d2d2f96fb55e0..3e39768ab1650ce36451e2defdc3fc93d0f19772 100644 (file)
 #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"
@@ -151,9 +152,9 @@ int getttyname_malloc(int fd, char **r);
 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);
index 8ccb4a941a7467a5dac5cf7c2bb5e6cf0cfc1f54..434159f41ced6df0bebd7ec60f708b300aba4cc8 100644 (file)
@@ -267,13 +267,12 @@ static char *format_timestamp_internal(
 
         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 */
index a238f6914d4a662cf2c14871c11a82c1a5d75765..e3a529d9709f6f549520929385d69eb4fd881fd3 100644 (file)
@@ -81,15 +81,19 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u)
 #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);
index 5100d6a5ff9fc3c6419e1eb96431541fea4734f2..afcf58aeac8bb9aa1a840574fb2a28a7825552de 100644 (file)
@@ -320,7 +320,7 @@ int mkdtemp_malloc(const char *template, char **ret) {
                 if (r < 0)
                         return r;
 
-                p = strjoin(tmp, "/XXXXXX");
+                p = path_join(tmp, "XXXXXX");
         }
         if (!p)
                 return -ENOMEM;
index 245daabcf82f280ed97263abcd31e99d42859e88..0544fee9ecd4823998e45875e33419940f450e8c 100644 (file)
@@ -102,7 +102,8 @@ static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
         [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);
@@ -177,6 +178,7 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
         [SERVICE_FINAL_SIGKILL] = "final-sigkill",
         [SERVICE_FAILED] = "failed",
         [SERVICE_AUTO_RESTART] = "auto-restart",
+        [SERVICE_CLEANING] = "cleaning",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
index 85f3e42d30b9c652aa14c877c63ccec4a2d7a870..a7cdb97ad2bd8dc3d8a8694a54930cfa61eb0a07 100644 (file)
@@ -40,6 +40,7 @@ typedef enum UnitActiveState {
         UNIT_FAILED,
         UNIT_ACTIVATING,
         UNIT_DEACTIVATING,
+        UNIT_MAINTENANCE,
         _UNIT_ACTIVE_STATE_MAX,
         _UNIT_ACTIVE_STATE_INVALID = -1
 } UnitActiveState;
@@ -116,6 +117,7 @@ typedef enum ServiceState {
         SERVICE_FINAL_SIGKILL,
         SERVICE_FAILED,
         SERVICE_AUTO_RESTART,
+        SERVICE_CLEANING,
         _SERVICE_STATE_MAX,
         _SERVICE_STATE_INVALID = -1
 } ServiceState;
index 84b2d64b25621773bcab9fe21742691ef88a4f57..7a7400773bd8f168931651757708fae1f951c52b 100644 (file)
@@ -198,7 +198,6 @@ static int detect_vm_xen(void) {
 /* 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);
@@ -229,17 +228,22 @@ static int detect_vm_xen_dom0(void) {
         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) {
index b5d110f4224aa111e8b5419fa9bb16b18e85df5e..f2d033fc407f624bbdfb7be438ad2466e79b898f 100644 (file)
@@ -480,13 +480,12 @@ exists:
 }
 
 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    },
                 {}
         };
 
index 55924adf069cdd0daf10c3d38b62d4e61a9a8e0f..7e64d13c7f616b9e064f27929b1eaaaaecf2d1c4 100644 (file)
@@ -25,6 +25,7 @@
 #include "copy.h"
 #include "dirent-util.h"
 #include "efivars.h"
+#include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
@@ -165,13 +166,13 @@ finish:
 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)
@@ -377,13 +378,24 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) {
                                      *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);
@@ -754,7 +766,7 @@ static int install_variables(const char *esp_path,
                              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;
 
@@ -763,7 +775,7 @@ static int install_variables(const char *esp_path,
                 return 0;
         }
 
-        p = strjoina(esp_path, path);
+        p = prefix_roota(esp_path, path);
         if (access(p, F_OK) < 0) {
                 if (errno == ENOENT)
                         return 0;
@@ -792,12 +804,12 @@ static int install_variables(const char *esp_path,
 }
 
 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)
@@ -838,9 +850,9 @@ static int remove_boot_efi(const char *esp_path) {
 }
 
 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);
 
@@ -870,10 +882,10 @@ static int remove_esp_subdirs(const char *esp_path) {
 }
 
 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);
@@ -888,7 +900,7 @@ static int remove_loader_config(const char *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)
@@ -933,7 +945,7 @@ static int install_loader_config(const char *esp_path, sd_id128_t machine_id) {
         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;
 
@@ -1144,11 +1156,12 @@ static int verb_status(int argc, char *argv[], void *userdata) {
                         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;
@@ -1435,16 +1448,15 @@ static int verb_set_default(int argc, char *argv[], void *userdata) {
 }
 
 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 },
                 {}
         };
 
index 57c423bfb6d8e38c3086b098cacde8b64f28ce87..4c3b39b57a7ba6d10f1af23bf159de068bfbd844 100644 (file)
@@ -9,6 +9,7 @@
 #include "disk.h"
 #include "graphics.h"
 #include "linux.h"
+#include "loader-features.h"
 #include "measure.h"
 #include "pe.h"
 #include "shim.h"
@@ -2277,11 +2278,12 @@ static VOID config_write_entries_to_variable(Config *config) {
 
 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;
diff --git a/src/boot/efi/loader-features.h b/src/boot/efi/loader-features.h
new file mode 100644 (file)
index 0000000..40095e2
--- /dev/null
@@ -0,0 +1,13 @@
+/* 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)
index 18254efd2d35a89312c84cc13e293db627cc0188..7016c90ddc8a69a1c3bd1a395f52aa1f45572913 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "alloc-util.h"
 #include "busctl-introspect.h"
+#include "path-util.h"
 #include "string-util.h"
 #include "util.h"
 #include "xml.h"
@@ -250,10 +251,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth
                                         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();
                                 }
index adc028bc8e655418b1ea1983ee1d42c554b8a8dc..34d95c76600671a8b7e74a13693929a8ada535e8 100644 (file)
@@ -243,7 +243,7 @@ static int run(int argc, char *argv[]) {
 
                                 controller = c ?: SYSTEMD_CGROUP_CONTROLLER;
                                 if (p) {
-                                        j = strjoin(root, "/", p);
+                                        j = path_join(root, p);
                                         if (!j)
                                                 return log_oom();
 
index fff6b505cc36fe3ba6c04284ea95eb96e4ca760d..dca594a0838b5407246765c58e676b9e945a2560 100644 (file)
@@ -459,7 +459,7 @@ static int refresh_one(
                 if (r == 0)
                         break;
 
-                p = strjoin(path, "/", fn);
+                p = path_join(path, fn);
                 if (!p)
                         return -ENOMEM;
 
index 7b9aeb6fbf418aa62f23edaf87de3b87d6ccd006..7a8b848fb34ab68a31a315eff62a3c3a6e002872 100644 (file)
@@ -125,10 +125,30 @@ static int add_lookup_instructions(
         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[] = {
                 /*
@@ -187,7 +207,9 @@ static int bpf_firewall_compile_bpf(
                 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;
@@ -234,6 +256,18 @@ static int bpf_firewall_compile_bpf(
                         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));
@@ -375,15 +409,18 @@ static int bpf_firewall_prepare_access_maps(
                 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;
@@ -392,7 +429,16 @@ static int bpf_firewall_prepare_access_maps(
                 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) {
@@ -430,10 +476,9 @@ static int bpf_firewall_prepare_access_maps(
                         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;
 }
 
@@ -475,6 +520,7 @@ static int bpf_firewall_prepare_accounting_maps(Unit *u, bool enabled, int *fd_i
 int bpf_firewall_compile(Unit *u) {
         CGroupContext *cc;
         int r, supported;
+        bool ip_allow_any = false, ip_deny_any = false;
 
         assert(u);
 
@@ -517,11 +563,11 @@ int bpf_firewall_compile(Unit *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");
         }
@@ -530,17 +576,106 @@ int bpf_firewall_compile(Unit *u) {
         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;
@@ -568,6 +703,9 @@ int bpf_firewall_install(Unit *u) {
                 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)
@@ -582,7 +720,8 @@ int bpf_firewall_install(Unit *u) {
         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);
 
@@ -591,13 +730,22 @@ int bpf_firewall_install(Unit *u) {
         }
 
         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;
 }
 
@@ -655,27 +803,17 @@ int bpf_firewall_supported(void) {
         _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");
@@ -686,19 +824,6 @@ int bpf_firewall_supported(void) {
                 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 =
index 10cafcc02e01bdf32778da10f43b23448de15064..f1460d982dd08d8a1aab3335734fc0cdff0cd004 100644 (file)
@@ -15,6 +15,7 @@ int bpf_firewall_supported(void);
 
 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);
index a7263855dcac2b5d3f24025fd263a79e1d7f05d2..0428f624813d5384652c61aa20c534e23df2e8bb 100644 (file)
@@ -199,6 +199,9 @@ void cgroup_context_done(CGroupContext *c) {
 
         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) {
@@ -210,6 +213,7 @@ 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];
 
@@ -360,6 +364,12 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) {
                 (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) {
@@ -945,6 +955,7 @@ static void cgroup_apply_firewall(Unit *u) {
         if (bpf_firewall_compile(u) < 0)
                 return;
 
+        (void) bpf_firewall_load_custom(u);
         (void) bpf_firewall_install(u);
 }
 
@@ -1353,7 +1364,9 @@ static bool unit_get_needs_bpf_firewall(Unit *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 */
@@ -1378,6 +1391,8 @@ static CGroupMask unit_get_cgroup_mask(Unit *u) {
 
         c = unit_get_cgroup_context(u);
 
+        assert(c);
+
         /* Figure out which controllers we need, based on the cgroup context object */
 
         if (c->cpu_accounting)
@@ -1615,11 +1630,7 @@ char *unit_default_cgroup_path(const Unit *u) {
         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) {
@@ -1921,6 +1932,12 @@ int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_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;
@@ -1928,7 +1945,7 @@ int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path) {
         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);
 
@@ -2382,10 +2399,13 @@ void unit_prune_cgroup(Unit *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;
@@ -2470,7 +2490,7 @@ static int unit_watch_pids_in_path(Unit *u, 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)
index fe347ea11453303ba24b448628295ab7aec848cc..d1537c503e3c4af961f917c1e755d8c60113ea47 100644 (file)
@@ -114,6 +114,9 @@ struct CGroupContext {
         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;
index 9f4fd06dc4094e6ca086107d9024ba77172fca7a..f70e6c87ee977f9a3484b0688831119935f195c1 100644 (file)
@@ -362,6 +362,8 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
         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
 };
@@ -461,6 +463,80 @@ static int bus_cgroup_set_transient_property(
                         }
                 }
 
+                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;
         }
 
index 3a71e7bcde06ee14b0e4bdc2cc024a535871e6dd..c816569f2b6fb6604cea677318a6d6ebf4d7174a 100644 (file)
@@ -225,6 +225,48 @@ static int property_get_cpu_affinity(
         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,
@@ -700,6 +742,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
         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),
@@ -829,6 +873,50 @@ static int append_exec_command(sd_bus_message *reply, ExecCommand *c) {
         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,
@@ -883,6 +971,47 @@ int bus_property_get_exec_command_list(
         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,
@@ -890,15 +1019,16 @@ int bus_set_transient_exec_command(
                 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;
 
@@ -913,7 +1043,7 @@ int bus_set_transient_exec_command(
                 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;
 
@@ -936,7 +1066,12 @@ int bus_set_transient_exec_command(
 
                         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);
@@ -967,7 +1102,7 @@ int bus_set_transient_exec_command(
                 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);
@@ -978,11 +1113,11 @@ int bus_set_transient_exec_command(
                         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);
@@ -1559,9 +1694,10 @@ int bus_exec_context_set_transient_property(
                 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);
@@ -1574,7 +1710,7 @@ int bus_exec_context_set_transient_property(
 
                 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;
@@ -1586,7 +1722,7 @@ int bus_exec_context_set_transient_property(
                                 /* 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;
 
@@ -1596,6 +1732,20 @@ int bus_exec_context_set_transient_property(
 
                 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;
 
@@ -2017,22 +2167,19 @@ int bus_exec_context_set_transient_property(
                                 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;
                         }
index 84051700ab0ac22727ab8d5e1b6cd00eeabfe86d..cbb2f97cf1dae2f46661d9c14b9bf2ab631cf623 100644 (file)
 #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);
index c113140e8f621d570db459b9013d8a60981b25be..9fb3ed516a9784c6c01a94fb29d992cec4d61116 100644 (file)
@@ -634,6 +634,12 @@ static int method_kill_unit(sd_bus_message *message, void *userdata, sd_bus_erro
         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) */
@@ -683,7 +689,7 @@ static int reply_unit_info(sd_bus_message *reply, Unit *u) {
                         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) {
@@ -2473,6 +2479,7 @@ const sd_bus_vtable bus_manager_vtable[] = {
         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),
index 824955b944d8e8fe21273f2b72e680529acce787..e0cc9a8c871a06d58e4a8a7694aa89aa9b292668 100644 (file)
@@ -105,6 +105,7 @@ const sd_bus_vtable bus_service_vtable[] = {
         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),
@@ -124,6 +125,7 @@ const sd_bus_vtable bus_service_vtable[] = {
         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),
@@ -133,8 +135,11 @@ const sd_bus_vtable bus_service_vtable[] = {
 
         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),
@@ -346,7 +351,7 @@ static int bus_service_set_transient_property(
                         if (e) {
                                 char *z;
 
-                                z = strjoin("/run/", e);
+                                z = path_join("/run", e);
                                 if (!z)
                                         return log_oom();
 
@@ -387,6 +392,7 @@ static int bus_service_set_transient_property(
                 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);
 
index a60362fff65c56cc98eef39f89c44af6f5281a54..53daa9c2ece8df277002f7bc222cde8042ad7ec7 100644 (file)
@@ -52,6 +52,42 @@ static BUS_DEFINE_PROPERTY_GET(property_get_can_isolate, "b", Unit, unit_can_iso
 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,
@@ -617,6 +653,74 @@ int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error
         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,
@@ -701,6 +805,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         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),
@@ -748,6 +853,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         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),
@@ -956,7 +1062,7 @@ static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) {
                 if (r == 0)
                         break;
 
-                j = strjoin(p, "/", g);
+                j = path_join(p, g);
                 if (!j)
                         return -ENOMEM;
 
index 740bb1c86bcbc16c929b8776f1f23afcaa98cb4d..24abf3c088e9226f2bdb9a37abc5b9bc29bb2292 100644 (file)
@@ -25,6 +25,7 @@ int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bu
 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,
index f478393f1a19fde97a3497d120df4e9d73ee70fd..e2abca469a5d892d4a8d14b5ef7adbc940983bba 100644 (file)
@@ -374,7 +374,7 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) {
         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)
@@ -626,7 +626,7 @@ static int device_process_new(Manager *m, sd_device *dev) {
         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)
index af994d97d1590a16b655d77d5829db4bd43cb35d..36a6641421f41fb05d6a1fe3395b7c3b8b21edc0 100644 (file)
@@ -2099,27 +2099,30 @@ static int setup_exec_directory(
                      (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) {
@@ -2150,6 +2153,10 @@ static int setup_exec_directory(
                                  * 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;
@@ -2168,6 +2175,46 @@ static int setup_exec_directory(
                                 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)
@@ -2334,7 +2381,7 @@ static int compile_bind_mounts(
                          * 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;
@@ -2350,9 +2397,9 @@ static int compile_bind_mounts(
 
                         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;
@@ -2364,7 +2411,7 @@ static int compile_bind_mounts(
                                 /* 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) {
@@ -2511,7 +2558,6 @@ static int apply_working_directory(
                 const ExecContext *context,
                 const ExecParameters *params,
                 const char *home,
-                const bool needs_mount_ns,
                 int *exit_status) {
 
         const char *d, *wd;
@@ -2533,15 +2579,9 @@ static int apply_working_directory(
         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) {
@@ -2552,6 +2592,26 @@ static int apply_working_directory(
         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,
@@ -2800,9 +2860,9 @@ static int compile_suggested_paths(const ExecContext *c, const ExecParameters *p
                         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;
 
@@ -2840,7 +2900,7 @@ static int exec_parameters_get_cgroup_path(const ExecParameters *params, char **
 
         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)
@@ -3148,6 +3208,16 @@ static int exec_child(
                         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;
@@ -3483,6 +3553,11 @@ static int exec_child(
                 }
         }
 
+        /* 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);
@@ -3515,7 +3590,7 @@ static int exec_child(
 
         /* 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");
 
@@ -3854,6 +3929,7 @@ void exec_context_init(ExecContext *c) {
         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) {
@@ -3898,6 +3974,7 @@ 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);
@@ -4336,6 +4413,14 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
                 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);
 
@@ -4689,6 +4774,60 @@ void exec_context_revert_tty(ExecContext *c) {
         }
 }
 
+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);
 
@@ -4806,7 +4945,7 @@ static void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
         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);
 }
@@ -5364,6 +5503,7 @@ static const char* const exec_preserve_mode_table[_EXEC_PRESERVE_MODE_MAX] = {
 
 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",
@@ -5374,6 +5514,21 @@ static const char* const exec_directory_type_table[_EXEC_DIRECTORY_TYPE_MAX] = {
 
 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",
index 7ddc36e6f3d5289789ae384344598e7a4f73f9e0..0d5398acfae473686251c6bc1b612e2dea6ee3cb 100644 (file)
@@ -15,6 +15,7 @@ typedef struct Manager Manager;
 
 #include "cgroup-util.h"
 #include "cpu-set-util.h"
+#include "exec-util.h"
 #include "fdset.h"
 #include "list.h"
 #include "missing_resource.h"
@@ -88,14 +89,6 @@ struct ExecStatus {
         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;
@@ -139,6 +132,19 @@ typedef struct ExecDirectory {
         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. */
@@ -174,6 +180,7 @@ struct ExecContext {
         int cpu_sched_priority;
 
         CPUSet cpu_set;
+        NUMAPolicy numa_policy;
 
         ExecInput std_input;
         ExecOutput std_output;
@@ -375,6 +382,9 @@ void exec_context_free_log_extra_fields(ExecContext *c);
 
 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);
@@ -407,3 +417,6 @@ ExecKeyringMode exec_keyring_mode_from_string(const char *s) _pure_;
 
 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_;
index 36cec70c2c212f0b890735a3250bc34ceb99c818..db87b12a78c478c438569e59d30c0751462840e6 100644 (file)
@@ -188,3 +188,21 @@ IPAddressAccessItem* ip_address_access_reduce(IPAddressAccessItem *first) {
 
         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;
+}
index 77078e1f142bc1ce79fd9635428e8a880d40e5e7..8d3ab731f167093410590405fe3e5ad5ee54d602 100644 (file)
@@ -19,3 +19,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ip_address_access);
 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);
index 81f5f9cb722ae3704b06693448bf3f74a1bed5b4..90c829d08d26b5b4a34564eb5cae4d16bcd38f07 100644 (file)
@@ -477,36 +477,14 @@ static bool job_is_runnable(Job *j) {
         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;
 }
 
@@ -579,7 +557,7 @@ static void job_log_begin_status_message(Unit *u, uint32_t job_id, JobType t) {
         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 :
@@ -889,7 +867,7 @@ static void job_log_done_status_message(Unit *u, uint32_t job_id, JobType t, Job
         /* 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),
@@ -909,7 +887,7 @@ static void job_log_done_status_message(Unit *u, uint32_t job_id, JobType t, Job
          * 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) {
@@ -1347,7 +1325,7 @@ int job_coldplug(Job *j) {
         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)
@@ -1455,46 +1433,14 @@ bool job_may_gc(Job *j) {
         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;
 }
@@ -1512,7 +1458,7 @@ void job_add_to_gc_queue(Job *j) {
         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);
 }
 
@@ -1521,7 +1467,7 @@ static size_t sort_job_list(Job **list, size_t n) {
         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++) {
@@ -1552,23 +1498,21 @@ int job_get_before(Job *j, Job*** ret) {
                 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))
@@ -1602,7 +1546,7 @@ int job_get_after(Job *j, Job*** ret) {
                 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))
@@ -1610,19 +1554,20 @@ int job_get_after(Job *j, Job*** ret) {
                 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);
@@ -1692,3 +1637,45 @@ const char* job_type_to_access_method(JobType t) {
         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;
+}
index 0f15cbf821cb791cf26c1356c69b80096f1db148..9c79c873d157ec1c3ad2019194b9f81fa5c139cb 100644 (file)
@@ -237,3 +237,5 @@ const char* job_result_to_string(JobResult t) _const_;
 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);
index b868a367f1ebd299ea6563f355f67c9c58a0d03b..76c50166b6479f0200675912997107f2ab4ad1d5 100644 (file)
@@ -36,6 +36,8 @@ $1.CPUSchedulingPolicy,          config_parse_exec_cpu_sched_policy, 0,
 $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)
@@ -203,6 +205,8 @@ $1.DisableControllers,           config_parse_disable_controllers,   0,
 $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)
@@ -250,6 +254,7 @@ Unit.SuccessAction,              config_parse_emergency_action,      0,
 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)
@@ -310,6 +315,7 @@ Service.TimeoutSec,              config_parse_service_timeout,       0,
 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
index bf414e62f1c03080061facf7290ea544374aed82..3288b0b83885c2fb07631705e0d126e615c32cc6 100644 (file)
@@ -18,6 +18,7 @@
 #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"
@@ -71,6 +72,47 @@ static int parse_socket_protocol(const char *s) {
         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");
@@ -92,6 +134,7 @@ DEFINE_CONFIG_PARSE_PTR(config_parse_blockio_weight, cg_blkio_weight_parse, uint
 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,
@@ -291,7 +334,7 @@ int config_parse_unit_path_strv_printf(
         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)
@@ -595,7 +638,7 @@ int config_parse_exec(
 
                 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;
 
@@ -676,7 +719,7 @@ int config_parse_exec(
                         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();
 
@@ -741,7 +784,7 @@ int config_parse_exec(
                                 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)
@@ -1211,6 +1254,33 @@ int config_parse_exec_cpu_sched_policy(const char *unit,
         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,
@@ -2174,7 +2244,7 @@ int config_parse_environ(
         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)
@@ -2242,7 +2312,7 @@ int config_parse_pass_environ(
         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)
@@ -2318,7 +2388,7 @@ int config_parse_unset_environ(
         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)
@@ -2393,7 +2463,7 @@ int config_parse_log_extra_fields(
                 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)
@@ -2517,17 +2587,18 @@ int config_parse_unit_condition_string(
                 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;
         }
 
@@ -2617,7 +2688,7 @@ int config_parse_unit_requires_mounts_for(
         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)
@@ -2812,7 +2883,7 @@ int config_parse_syscall_archs(
                 _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)
@@ -2916,7 +2987,7 @@ int config_parse_address_families(
                 _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)
@@ -3235,7 +3306,7 @@ int config_parse_delegate(
                         _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)
@@ -3292,7 +3363,7 @@ int config_parse_device_allow(
                 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) {
@@ -3363,7 +3434,7 @@ int config_parse_io_device_weight(
                 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) {
@@ -3437,7 +3508,7 @@ int config_parse_io_device_latency(
                 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) {
@@ -3511,7 +3582,7 @@ int config_parse_io_limit(
                 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) {
@@ -3602,7 +3673,7 @@ int config_parse_blockio_device_weight(
                 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) {
@@ -3680,7 +3751,7 @@ int config_parse_blockio_bandwidth(
                 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) {
@@ -3800,7 +3871,7 @@ int config_parse_exec_directories(
         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) {
@@ -3936,7 +4007,7 @@ int config_parse_namespace_path_strv(
                 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)
@@ -4014,7 +4085,7 @@ int config_parse_temporary_filesystems(
                 _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)
@@ -4090,7 +4161,7 @@ int config_parse_bind_paths(
                 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)
@@ -4119,7 +4190,7 @@ int config_parse_bind_paths(
 
                 /* 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) {
@@ -4148,7 +4219,7 @@ int config_parse_bind_paths(
                         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) {
@@ -4428,6 +4499,66 @@ int config_parse_disable_controllers(
         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) {
@@ -4891,3 +5022,151 @@ void unit_dump_config_items(FILE *f) {
                 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;
+}
index 0891f367604f1bb9c22a8265cb2d5b6bfa45dd0d..326e80893bdee3041e70abb71ab96e76bd3a0297 100644 (file)
@@ -4,6 +4,10 @@
 #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);
@@ -108,6 +112,14 @@ CONFIG_PARSER_PROTOTYPE(config_parse_pid_file);
 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);
index b33ea1b5b5286335ac04de7c6f2178ab1a74e303..75c488795e47d8d9a25120a56cf31263dc27b2b6 100644 (file)
@@ -107,6 +107,7 @@ static bool arg_crash_shell;
 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;
@@ -142,6 +143,7 @@ static sd_id128_t arg_machine_id;
 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);
 
@@ -295,47 +297,6 @@ static int console_setup(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);
@@ -391,7 +352,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 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);
                 }
@@ -438,6 +399,17 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                 } 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))
@@ -560,212 +532,71 @@ DEFINE_SETTER(config_parse_level2, log_set_max_level_from_string, "log level");
 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                },
                 {}
         };
 
@@ -845,6 +676,7 @@ static void set_manager_settings(Manager *m) {
         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[]) {
@@ -1017,7 +849,7 @@ 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);
@@ -1157,13 +989,13 @@ static int help(void) {
                "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"
@@ -1245,9 +1077,9 @@ static void bump_file_max_and_nr_open(void) {
 #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;
         }
@@ -1753,6 +1585,27 @@ static void update_cpu_affinity(bool skip_setup) {
                 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[],
@@ -1924,6 +1777,7 @@ static int invoke_main_loop(
                         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);
@@ -2084,6 +1938,7 @@ static int initialize_runtime(
                 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. */
@@ -2112,7 +1967,7 @@ static int initialize_runtime(
                                 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);
         }
 
@@ -2221,6 +2076,7 @@ static void reset_arguments(void) {
         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;
@@ -2262,6 +2118,7 @@ static void reset_arguments(void) {
         arg_default_oom_policy = OOM_STOP;
 
         cpu_set_reset(&arg_cpu_affinity);
+        numa_policy_reset(&arg_numa_policy);
 }
 
 static int parse_configuration(void) {
index a3dc5bb40ea29d117718e55abe3a6c8aa3ad331e..09507648fb1a2e88b2f2c9e59f84dbde7e510110 100644 (file)
@@ -231,7 +231,7 @@ static void manager_print_jobs_in_progress(Manager *m) {
                               "%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);
 }
 
@@ -734,6 +734,8 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager
                 .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,
@@ -1378,6 +1380,9 @@ static void manager_enumerate_perpetual(Manager *m) {
 
         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)) {
@@ -1395,6 +1400,9 @@ static void manager_enumerate(Manager *m) {
 
         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)) {
@@ -1483,7 +1491,7 @@ static void manager_build_unit_path_cache(Manager *m) {
                 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;
@@ -2088,13 +2096,15 @@ void manager_dump(Manager *m, FILE *f, const char *prefix) {
         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);
@@ -2890,7 +2900,7 @@ int manager_loop(Manager *m) {
         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)) {
@@ -2921,7 +2931,7 @@ int manager_loop(Manager *m) {
                         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;
index 92bfd48997558cbae137c953bcf4b42349be8d2d..9879082fea7ecf005ca008276efe15c370c22085 100644 (file)
@@ -323,6 +323,7 @@ struct Manager {
         uint8_t return_value;
 
         ShowStatus show_status;
+        StatusUnitFormat status_unit_format;
         char *confirm_spawn;
         bool no_console_output;
         bool service_watchdogs;
index f1df760a676cc69848e1313c2e793e956304ba45..34762dbc21a88bef1626d617442162a25d13bac1 100644 (file)
@@ -178,6 +178,7 @@ systemd_sources = files('main.c')
 
 in_files = [['macros.systemd',   rpmmacrosdir],
             ['system.conf',      pkgsysconfdir],
+            ['user.conf',        pkgsysconfdir],
             ['systemd.pc',       pkgconfigdatadir],
             ['triggers.systemd', '']]
 
@@ -204,9 +205,6 @@ policy = configure_file(
 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))
index ec7af3ab1c0204bb81dc8d9fdb0a1b6cd702a7cf..7aab2f7593c33fbb51bad8284612316b54e8ec6a 100644 (file)
@@ -444,7 +444,7 @@ static int prefix_where_needed(MountEntry *m, size_t n, const char *root_directo
                 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;
 
index 5a00150e9b3096aa51f9a7c3a10a5bc58a3e3aeb..562cd352a3e57b933501c3d8e13cf31975e09f51 100644 (file)
@@ -89,7 +89,7 @@ int path_spec_watch(PathSpec *s, sd_event_io_handler_t handler) {
                                 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;
index c8b1a8588b1c999187cbbee22252f47eeb20e87f..cb136322fe1f9adae5cdfa8df37d030973b322e6 100644 (file)
@@ -17,6 +17,7 @@
 #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"
@@ -126,7 +127,7 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) {
 #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);
@@ -158,7 +159,7 @@ static int access_init(sd_bus_error *error) {
                 /* 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);
index f16601a7288acc36353ac766cc85e9aa307a15f8..30687a967a4d094308822bf426225819286314b4 100644 (file)
@@ -30,6 +30,7 @@
 #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"
@@ -59,7 +60,8 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
         [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
@@ -80,7 +82,8 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
         [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);
@@ -103,6 +106,7 @@ static void service_init(Unit *u) {
         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;
@@ -603,7 +607,7 @@ static int service_verify(Service *s) {
                 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;
 }
@@ -787,8 +791,9 @@ static int service_load(Unit *u) {
 }
 
 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;
@@ -802,6 +807,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                 "%sService State: %s\n"
                 "%sResult: %s\n"
                 "%sReload Result: %s\n"
+                "%sClean Result: %s\n"
                 "%sPermissionsStartOnly: %s\n"
                 "%sRootDirectoryStartOnly: %s\n"
                 "%sRemainAfterExit: %s\n"
@@ -814,6 +820,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                 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),
@@ -869,8 +876,10 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) {
                         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));
 
@@ -1069,7 +1078,8 @@ static void service_set_state(Service *s, ServiceState state) {
                     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,
@@ -1085,7 +1095,8 @@ static void service_set_state(Service *s, ServiceState 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;
@@ -1151,6 +1162,9 @@ static usec_t service_coldplug_timeout(Service *s) {
         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;
         }
@@ -1188,13 +1202,14 @@ static int service_coldplug(Unit *u) {
                    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);
@@ -2368,7 +2383,7 @@ static int service_start(Unit *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! */
@@ -2455,6 +2470,12 @@ static int service_stop(Unit *u) {
                 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);
@@ -2530,14 +2551,16 @@ static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command
                         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))
@@ -2682,7 +2705,7 @@ static int service_deserialize_exec_command(Unit *u, const char *key, const char
         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)
@@ -3561,6 +3584,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
                                         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.");
                         }
@@ -3677,6 +3708,15 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
                 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.");
         }
@@ -4084,6 +4124,7 @@ static void service_reset_failed(Unit *u) {
 
         s->result = SERVICE_SUCCESS;
         s->reload_result = SERVICE_SUCCESS;
+        s->clean_result = SERVICE_SUCCESS;
         s->n_restarts = 0;
         s->flush_n_restarts = false;
 }
@@ -4153,6 +4194,77 @@ static int service_exit_status(Unit *u) {
         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",
@@ -4188,6 +4300,14 @@ static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] =
 
 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",
@@ -4245,6 +4365,8 @@ const UnitVTable service_vtable = {
         .can_reload = service_can_reload,
 
         .kill = service_kill,
+        .clean = service_clean,
+        .can_clean = service_can_clean,
 
         .serialize = service_serialize,
         .deserialize_item = service_deserialize_item,
index 2aebc5f09d87eacffe6a6a26701ce7b7cb241df9..de56728c224ab51f302e470a44c7ab850c6a6617 100644 (file)
@@ -99,6 +99,7 @@ struct Service {
         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;
@@ -147,6 +148,7 @@ struct Service {
         /* If we shut down, remember why */
         ServiceResult result;
         ServiceResult reload_result;
+        ServiceResult clean_result;
 
         bool main_pid_known:1;
         bool main_pid_alien:1;
@@ -210,6 +212,9 @@ ServiceType service_type_from_string(const char *s) _pure_;
 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_;
 
index 02601e52db9bf2718fa64a711216193a98a2245a..c998b51abdb74e68c1580de2a02b1d99c30a2169 100644 (file)
 #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);
@@ -118,3 +118,10 @@ int status_printf(const char *status, ShowStatusFlags flags, const char *format,
 
         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);
index f574d92d8465258cb2dedfc663b050dbd385b29d..247caec77c4290fd7a3493b0341a33929234210f 100644 (file)
@@ -21,9 +21,19 @@ typedef enum ShowStatusFlags {
         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);
index b6a4290c3fc20f4a2243de48d537129652e18e95..cc3dc5973d7f9cc5025a7284c612520ebae15364 100644 (file)
@@ -782,7 +782,7 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) {
 
                         r = socket_address_print(&p->address, &k);
                         if (r < 0)
-                                t = strerror(-r);
+                                t = strerror_safe(r);
                         else
                                 t = k;
 
index 548e6dfb8c99fa031feb43c522977c11a3164fa1..8617ec20aa8239d6fb1862754a2a223a78e5170c 100644 (file)
@@ -23,6 +23,8 @@
 #CrashReboot=no
 #CtrlAltDelBurstAction=reboot-force
 #CPUAffinity=1 2
+#NUMAPolicy=default
+#NUMAMask=
 #RuntimeWatchdogSec=0
 #ShutdownWatchdogSec=10min
 #WatchdogDevice=
@@ -30,6 +32,7 @@
 #NoNewPrivileges=no
 #SystemCallArchitectures=
 #TimerSlackNSec=
+#StatusUnitFormat=@STATUS_UNIT_FORMAT_DEFAULT@
 #DefaultTimerAccuracySec=1min
 #DefaultStandardOutput=journal
 #DefaultStandardError=inherit
index ba74dc7aea3ab06e3f17f9de0490241da0027c3a..fa0af4c3a2d37f0430bb423ca944fbcd464bb704 100644 (file)
@@ -833,6 +833,41 @@ static void timer_timezone_change(Unit *u) {
         }
 }
 
+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",
@@ -872,6 +907,9 @@ const UnitVTable timer_vtable = {
         .start = timer_start,
         .stop = timer_stop,
 
+        .clean = timer_clean,
+        .can_clean = timer_can_clean,
+
         .serialize = timer_serialize,
         .deserialize_item = timer_deserialize_item,
 
index 3b6b240d361f70e5b0cfcf8701cfada3b3da4592..d1bf2e64c915df861469655e396b0e6636c4121f 100644 (file)
@@ -353,6 +353,11 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
         Unit *u;
         void *v;
         int r;
+        static const UnitDependency directions[] = {
+                UNIT_BEFORE,
+                UNIT_AFTER,
+        };
+        size_t d;
 
         assert(tr);
         assert(j);
@@ -441,25 +446,33 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi
         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
index 72391ace3a95c19e5b10b1ccd945581b2e8a30de..ffa12d8a929f1ac27d61b306b9bba9395fc05e1b 100644 (file)
@@ -244,6 +244,10 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
          * 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 },
@@ -281,9 +285,5 @@ int unit_full_printf(Unit *u, const char *format, char **ret) {
                 {}
         };
 
-        assert(u);
-        assert(format);
-        assert(ret);
-
         return specifier_printf(format, table, u, ret);
 }
index cab6b44c036fbea69293c437039126efbaf9cb1f..5bf905c3127d591e15e51450594ef07463d849c5 100644 (file)
@@ -12,6 +12,7 @@
 
 #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"
@@ -682,6 +683,11 @@ void unit_free(Unit *u) {
         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);
@@ -1005,7 +1011,7 @@ int unit_add_exec_dependencies(Unit *u, ExecContext *c) {
                 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;
 
@@ -1061,6 +1067,15 @@ const char *unit_description(Unit *u) {
         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;
@@ -1320,7 +1335,7 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
                         "%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);
@@ -1638,7 +1653,7 @@ static bool unit_test_assert(Unit *u) {
 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);
 
@@ -1735,6 +1750,8 @@ int unit_start(Unit *u) {
         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)
@@ -4520,7 +4537,7 @@ int unit_make_transient(Unit *u) {
 
         (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;
 
@@ -5500,6 +5517,12 @@ int unit_prepare_exec(Unit *u) {
 
         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);
@@ -5729,6 +5752,53 @@ int unit_test_trigger_loaded(Unit *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",
index 007c4aea9eaa21607b192e4ffb78926fcb171655..603c20a9047ccc86d06809d3059cef8036d5aba5 100644 (file)
@@ -10,6 +10,7 @@
 #include "emergency-action.h"
 #include "install.h"
 #include "list.h"
+#include "set.h"
 #include "unit-name.h"
 #include "cgroup.h"
 
@@ -281,6 +282,10 @@ typedef struct Unit {
 
         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];
 
@@ -470,6 +475,12 @@ typedef struct UnitVTable {
 
         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
@@ -666,6 +677,7 @@ int unit_set_slice(Unit *u, Unit *slice);
 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);
 
@@ -848,13 +860,16 @@ int unit_failure_action_exit_status(Unit *u);
 
 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__)
similarity index 96%
rename from src/core/user.conf
rename to src/core/user.conf.in
index 0b52f733efc9a5172e080aa878a44ad2dfe30fbf..95a162e0f6cc0d33f873c6f2fb947980af378e36 100644 (file)
@@ -17,6 +17,7 @@
 #LogLocation=no
 #SystemCallArchitectures=
 #TimerSlackNSec=
+#StatusUnitFormat=@STATUS_UNIT_FORMAT_DEFAULT@
 #DefaultTimerAccuracySec=1min
 #DefaultStandardOutput=inherit
 #DefaultStandardError=inherit
index 2cceb49f4b1aa05b51a210cb357f6dd81fa0d29a..6308cda61014ca15a211594c70771ff7480648d1 100644 (file)
 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,
@@ -707,24 +700,21 @@ static bool is_pid1_crash(const char *context[_CONTEXT_MAX]) {
                 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);
@@ -739,16 +729,15 @@ static int submit_coredump(
                 /* 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);
@@ -756,9 +745,10 @@ static int submit_coredump(
         /* 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)
@@ -766,48 +756,34 @@ static int submit_coredump(
 
 #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) {
@@ -817,18 +793,17 @@ log:
                         /* 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");
 
@@ -873,9 +848,10 @@ static void map_context_fields(const struct iovec *iovec, const char* context[])
 
 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);
@@ -897,40 +873,34 @@ static int process_socket(int fd) {
                 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 &&
@@ -953,17 +923,15 @@ static int process_socket(int fd) {
                 }
 
                 /* 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 */
@@ -977,24 +945,22 @@ static int process_socket(int fd) {
         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,
@@ -1004,7 +970,7 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd)
         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);
@@ -1014,9 +980,9 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd)
         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];
@@ -1035,7 +1001,7 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd)
                                          * 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;
@@ -1057,23 +1023,10 @@ static int send_iovec(const struct iovec iovec[], size_t n_iovec, int input_fd)
         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;
@@ -1086,12 +1039,8 @@ static int gather_pid_metadata(
                 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)
@@ -1110,104 +1059,113 @@ static int gather_pid_metadata(
                         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 */
 }
@@ -1215,8 +1173,7 @@ static int gather_pid_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...");
@@ -1234,29 +1191,24 @@ static int process_kernel(int argc, char* argv[]) {
         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]);
@@ -1268,20 +1220,18 @@ static int process_kernel(int argc, char* argv[]) {
 
 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];
@@ -1291,21 +1241,16 @@ static int process_backtrace(int argc, char *argv[]) {
         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);
@@ -1318,39 +1263,36 @@ static int process_backtrace(int argc, char *argv[]) {
                         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]);
index d62cbd0323df553ea08cd94b028f8144e21ab5c6..0529654788a5089b102ed8db83d293f9b702b1a4 100644 (file)
@@ -776,7 +776,7 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp)
                 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();
 
index 66b2ec8cca32bac084578b4e90c708bceb407a9f..a962cde125b85a6baf7b0897a79a0f9fb181e071 100644 (file)
@@ -108,7 +108,7 @@ static int thread_callback(Dwfl_Thread *thread, void *userdata) {
         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,
@@ -183,3 +183,13 @@ finish:
 
         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");
+}
index 52900424a700a8f988240e56f709dde540548fac..2462c763f911608346ae75427c5ea9dc9fe084e2 100644 (file)
@@ -1,4 +1,4 @@
 /* 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);
index 6d858f51ad086a5e67c7b860bba73a6f41ed22f7..92314b667ed3193e0ebf647077ab79f2f0f8d548 100644 (file)
@@ -235,7 +235,7 @@ static int create_disk(
                 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();
 
@@ -546,9 +546,11 @@ static int add_crypttab_devices(void) {
                         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);
 
index af95b418162f6c3a6c5f6fa44f14c00794ea99ad..02f131086bd08d9898ca3c280a74eeefcf65b869 100644 (file)
@@ -534,7 +534,7 @@ static int attach_luks_or_plain(struct crypt_device *cd,
                 /* 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, &params);
                 if (r < 0)
@@ -637,6 +637,11 @@ static int run(int argc, char *argv[]) {
 
         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")) {
index 1b5fb2a28dfc18e8e72389f773a77680d66ec175..09220dc98c7310e3c9b68482cafe39e6a0f23cba 100644 (file)
@@ -3,9 +3,11 @@
 #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"
@@ -16,11 +18,12 @@ static const char *arg_dest = NULL;
 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;
@@ -56,15 +59,16 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
                         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")) {
 
@@ -99,7 +103,7 @@ static int generate_mask_symlinks(void) {
         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();
 
@@ -142,6 +146,23 @@ static int generate_wants_symlinks(void) {
         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;
 
@@ -155,6 +176,8 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
                 r = strv_extend(&arg_wants, "debug-shell.service");
                 if (r < 0)
                         return log_oom();
+
+                install_debug_shell_dropin(arg_dest);
         }
 
         r = generate_mask_symlinks();
index 63baa747949e590c8c55bf62a59bea834a4ad7bd..bc11aa828a6f2f395fb9f6ec032009aa44a98451 100644 (file)
@@ -200,7 +200,7 @@ static int enumerate_dir_d(
 
         assert(!endswith(drop, "/"));
 
-        path = strjoin(toppath, "/", drop);
+        path = path_join(toppath, drop);
         if (!path)
                 return -ENOMEM;
 
@@ -230,7 +230,7 @@ static int enumerate_dir_d(
                 if (!endswith(*file, ".conf"))
                         continue;
 
-                p = strjoin(path, "/", *file);
+                p = path_join(path, *file);
                 if (!p)
                         return -ENOMEM;
                 d = p + strlen(toppath) + 1;
@@ -347,7 +347,7 @@ static int enumerate_dir(
         STRV_FOREACH(t, files) {
                 _cleanup_free_ char *p = NULL;
 
-                p = strjoin(path, "/", *t);
+                p = path_join(path, *t);
                 if (!p)
                         return -ENOMEM;
 
@@ -371,22 +371,27 @@ static int enumerate_dir(
         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;
                 }
@@ -423,10 +428,10 @@ static int process_suffix(const char *suffix, const char *onlyprefix) {
         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;
index e6b40294bf6df17883eb6867c4a767e363136d11..e0c834cfc3221ecbb5faad3c8688d1c346ca6ffb 100644 (file)
@@ -30,6 +30,7 @@
 #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"
@@ -68,7 +69,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_locale_messages, freep);
 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;
index 8101f9ce95853284cf4260236dd22a06e1766d82..0a5863667c024e991cdfe6e1f2ba6b435c374ea2 100644 (file)
@@ -22,6 +22,7 @@
 #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;
index 35501b61f59d43df8c8997384044d47ddfb0c8f8..8610ab67058462e3c1dba9a757c1bf302d177155 100644 (file)
@@ -189,9 +189,11 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
                        "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;
 
index 6c70054d934ebbfd91ac688bf3ce3888cc5c1953..f6e37670b90624c5b516194a15202209fb006f3c 100644 (file)
@@ -71,7 +71,7 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, bool requir
         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);
@@ -181,7 +181,7 @@ static int add_mount(
         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();
 
@@ -299,7 +299,7 @@ static int add_swap(const char *path) {
         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();
 
@@ -360,7 +360,7 @@ static int add_automount(
         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);
index 02eb0c9a519266d4394b8265665896c064d37d63..3f49f564d6d759fe886312a640b273de1a76bbdc 100644 (file)
 
 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) {
 
@@ -35,6 +39,22 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
                 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.");
@@ -49,6 +69,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
 
 static int process_resume(void) {
         _cleanup_free_ char *name = NULL, *lnk = NULL;
+        const char *opts;
         int r;
 
         if (!arg_resume_device)
@@ -66,6 +87,15 @@ static int process_resume(void) {
         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;
 }
 
index febcc43ce87f224797f6a5fcac2f3a6209a90d5c..96cf696652f684ac866a06aed4c41046365f1062 100644 (file)
@@ -28,13 +28,11 @@ static void curl_glue_check_finished(CurlGlue *g) {
 
 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)
@@ -44,7 +42,7 @@ static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *u
         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.");
 
@@ -53,7 +51,7 @@ static int curl_glue_on_io(sd_event_source *s, int fd, uint32_t revents, void *u
 }
 
 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;
@@ -61,21 +59,11 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v
         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;
@@ -87,12 +75,6 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v
                 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)
@@ -107,19 +89,10 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v
                 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");
@@ -130,16 +103,6 @@ static int curl_glue_socket_callback(CURLM *curl, curl_socket_t s, int action, v
                         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;
@@ -204,14 +167,6 @@ CurlGlue *curl_glue_unref(CurlGlue *g) {
                 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);
         }
 
index 6a7be6421cb8abbfed325afcd6f978b666cbf7d0..fa80b233422c080b273cca422a695ce937984448 100644 (file)
@@ -16,7 +16,6 @@ struct CurlGlue {
         CURLM *curl;
         sd_event_source *timer;
         Hashmap *ios;
-        Hashmap *translate_fds;
 
         void (*on_finished)(CurlGlue *g, CURL *curl, CURLcode code);
         void *userdata;
index 1f63ebb7619448307b24bf31f50bbe7ee5ea514f..2f27dda76b29bf909ac1b5fce25a8c7ade9a2b00 100644 (file)
@@ -213,7 +213,7 @@ int import_mangle_os_tree(const char *path) {
                 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);
index 04344492c86368f2dfb79b68c5983026a26c3cc1..f8f3a23206ff2097c4d9e7c34301d918baea5591 100644 (file)
@@ -6,13 +6,13 @@
 #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"
@@ -161,7 +161,7 @@ static int import_fs(int argc, char *argv[], void *userdata) {
                 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)
index 4d0d9254d0125888f11482fa160f9d167b38fd05..f3eff82a2990b735b6237ef11bf44c7f8cd2fb7f 100644 (file)
@@ -211,7 +211,7 @@ static int tar_import_fork_tar(TarImport *i) {
         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();
 
index 766b97cb0c7b50bf514daf8d1372edd3638bc021..249ea533e63b63c474aef241c471df999be444e0 100644 (file)
@@ -61,8 +61,8 @@ int pull_find_old_etags(
         }
 
         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)
@@ -97,12 +97,10 @@ int pull_find_old_etags(
                 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;
         }
@@ -122,7 +120,7 @@ int pull_make_local_copy(const char *final, const char *image_root, const char *
         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);
index 9f759a77e096c92de62ca8531dbe5e59b3add12c..881bba0eebc5b1ac6b2d036aa13e979caf089459 100644 (file)
@@ -6,6 +6,7 @@
 
 #include "alloc-util.h"
 #include "fd-util.h"
+#include "format-util.h"
 #include "gcrypt-util.h"
 #include "hexdecoct.h"
 #include "import-util.h"
index e5162d108897d26f340dd9a181309e3591b67ecd..9a5ae803fcd8983fcf6bfccef3cf2374b455c2f3 100644 (file)
 
                 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);
index 734e8c7f3334e80b5a466cf21ae4fdcc8460a455..459d8e86a1c957d917dbd3eece46b1da37ac88c1 100644 (file)
@@ -15,6 +15,7 @@
 
 #include "alloc-util.h"
 #include "bus-util.h"
+#include "errno-util.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "hostname-util.h"
@@ -252,7 +253,7 @@ static ssize_t request_reader_entries(
         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;
         }
 
@@ -605,7 +606,7 @@ static ssize_t request_reader_fields(
         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;
         }
 
index d32c51c3cec376411797033ce936d412626bdc46..329505cd2c109159c7447af430c1e97c3f9ea0a8 100644 (file)
@@ -123,7 +123,7 @@ static int spawn_getter(const char *getter) {
         _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");
 
index ebcae2fcaaa9b03afb01596f0bd386bb8752bbc3..dc047b2d49955a56266f1e142a76fb99a717d456 100644 (file)
@@ -38,7 +38,7 @@ RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer) {
         if (!source)
                 return NULL;
 
-        source->importer.fd = fd;
+        source->importer = JOURNAL_IMPORTER_MAKE(fd);
         source->importer.passive_fd = passive_fd;
         source->importer.name = name;
 
index 2887daa4bf901562aa8a797c7a1d95b5c200f252..87b8ba6495f826ddcb730a50cb3b3f1f3fb8a025 100644 (file)
@@ -63,9 +63,11 @@ if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_MICROHTTPD') == 1
         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
index c2dcf76deb86dd177594b1bbe9cccb3bc41e9fb2..91ca53e5ebedbb1d4746455ac2c2e102068ca9f5 100644 (file)
 #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"
index a932314e19e61c984356db64ebf9c736ae904730..87d65896c61e7001f6af1fb177b47645c14fa882 100644 (file)
@@ -9,11 +9,11 @@
 #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"
index 3d053c59f008520264bb50c231cb276ca49c2c5c..3e1ca5b1ba233b140e59aaa543613b9d90462ab7 100644 (file)
@@ -35,6 +35,7 @@
 #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"
@@ -752,7 +753,7 @@ static int parse_argv(int argc, char *argv[]) {
                         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));
 
@@ -1424,7 +1425,7 @@ static int add_boot(sd_journal *j) {
         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",
@@ -1678,9 +1679,11 @@ static int add_syslog_identifier(sd_journal *j) {
         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;
index 71d9282ed52133c5510b09c06e2089fede07006c..fae9138ecb23b4360bc3e2efe6c53927a9f0e40b 100644 (file)
@@ -264,8 +264,8 @@ static int map_all_fields(
                 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
@@ -279,7 +279,10 @@ static int map_all_fields(
                                 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);
                         }
                 }
index 4efe34c6646cb5b32ba3c48ee6096554be35eaa1..73a96da9f7a99d65b91f39a559dd1a0fe328997d 100644 (file)
@@ -63,7 +63,7 @@ static void server_process_entry_meta(
                  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;
@@ -73,7 +73,7 @@ static void server_process_entry_meta(
                    startswith(p, "MESSAGE=")) {
                 char *t;
 
-                t = strndup(p + 8, l - 8);
+                t = memdup_suffix0(p + 8, l - 8);
                 if (t) {
                         free(*message);
                         *message = t;
index 2a4fc250c9190ea4e240af77ab4c891b8da4206b..ce0d9ce8c985e030255ad64ca9da81228002c009 100644 (file)
@@ -1061,7 +1061,7 @@ void server_driver_message(Server *s, pid_t object_pid, const char *message_id,
                 /* 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");
@@ -2192,8 +2192,8 @@ int server_init(Server *s) {
         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;
 
index 102d2fee183294b80ce85ce207cc4c6705a85aec..74c80b724df30494b6c694515e7ca8f715de6d25 100644 (file)
@@ -319,7 +319,7 @@ uint32_t jenkins_hashlittle( const void *key, size_t length, uint32_t initval)
      * 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)
     {
@@ -504,7 +504,7 @@ void jenkins_hashlittle2(
      * 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)
     {
@@ -680,7 +680,7 @@ uint32_t jenkins_hashbig( const void *key, size_t length, uint32_t initval)
      * 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)
     {
index e03d6dc232713a35da9e60216e46d9514149f417..5796f77cac365c80c02da0ca3e2e0a1720060f4c 100644 (file)
@@ -111,20 +111,22 @@ endif
 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
index 40baf2b3740f343dcad856874a5182ba711b35bb..82b180b019f2fe8c9015ed8217205a4fd6a1909f 100644 (file)
@@ -1376,7 +1376,7 @@ static int add_file_by_name(
         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);
 }
 
@@ -1392,7 +1392,7 @@ static void remove_file_by_name(
         assert(prefix);
         assert(filename);
 
-        path = strjoina(prefix, "/", filename);
+        path = prefix_roota(prefix, filename);
         f = ordered_hashmap_get(j->files, path);
         if (!f)
                 return;
@@ -1553,10 +1553,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname)
         /* 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;
index 449ac8ea1fa6fe612f1667f63e9996df65386baf..c0bda68811c2fe4bf885ab12af99e5b82158d604 100644 (file)
@@ -30,7 +30,7 @@ _noreturn_ static void log_assert_errno(const char *text, int error, const char
         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) {
index 0c1ff34e5de339b22de17c1381fa0fa5ccee5293..c01c1cf6d6f50b5177ccfe65b175896075e8c020 100644 (file)
@@ -190,7 +190,7 @@ int dhcp_identifier_set_iaid(
                                 /* device is under renaming */
                                 return -EBUSY;
 
-                        name = net_get_name(device);
+                        name = net_get_name_persistent(device);
                 }
         }
 
index a0f9c2299fa6a7a8538117ff35a2cff3b1c81fea..e0269b54564f15837f4e98fc5473d2bfbf89998c 100644 (file)
@@ -51,5 +51,5 @@ int dhcp_packet_verify_headers(DHCPPacket *packet, size_t len, bool checksum, ui
 #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__)
index a68de4ff98e715818908730dc05cd3bd3faa9504..0abb8fdef028a0e767480e3c1b0fe258042b7048 100644 (file)
@@ -198,7 +198,7 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo
                                 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;
 
index b30be7e5b2d6e4b0173a2b2d6417ef60b18875d4..256006ebebf05814bbbedb36aa28a3c619b61bf0 100644 (file)
@@ -69,8 +69,8 @@ typedef struct DHCPRequest {
         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);
index 157fc0aadde96436b278b0e68fb7e28ac708c5d9..f28ba68dd1e9a8e09bbc879414887570c8ebafe4 100644 (file)
@@ -79,7 +79,7 @@ struct DHCP6IA {
 
 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,
index 88b54933c3941afb9518f84dd6a43f06d0e51334..9598438dbaee12e11a87fa08421c241c8fc8b27f 100644 (file)
@@ -32,7 +32,7 @@ struct sd_lldp {
         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_;
index 28f801cd7fb3d1bafabfd9ebb4e1b262b97d65b2..c66dc3d44316640950aad0049611ce1e4b6952d5 100644 (file)
@@ -37,7 +37,7 @@ struct sd_ndisc {
         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_;
index a112b9f70f98660a8234f165c9b29ef1e4d30202..1f2e5c7e65eb5c5318c41c1c7ddb1079e747494a 100644 (file)
@@ -12,6 +12,7 @@
 #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"
@@ -24,7 +25,7 @@
 #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);
@@ -39,7 +40,7 @@ const char *net_get_name(sd_device *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;
@@ -47,10 +48,10 @@ int net_get_unique_predictable_data(sd_device *device, uint64_t *result) {
 
         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),
@@ -73,26 +74,66 @@ int net_get_unique_predictable_data(sd_device *device, uint64_t *result) {
         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,
@@ -100,12 +141,25 @@ 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;
 
@@ -121,6 +175,9 @@ bool net_match_config(Set *match_mac,
         if (!net_condition_test_strv(match_names, dev_name))
                 return false;
 
+        if (!net_condition_test_property(match_property, device))
+                return false;
+
         return true;
 }
 
@@ -164,7 +221,7 @@ int config_parse_net_condition(const char *unit,
         return 0;
 }
 
-int config_parse_ifnames(
+int config_parse_match_strv(
                 const char *unit,
                 const char *filename,
                 unsigned line,
@@ -176,7 +233,9 @@ int config_parse_ifnames(
                 void *data,
                 void *userdata) {
 
+        const char *p = rvalue;
         char ***sv = data;
+        bool invert;
         int r;
 
         assert(filename);
@@ -184,30 +243,154 @@ int config_parse_ifnames(
         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,
index 62f5a4a76c5ab3154a485f8b495a0ced30aedeea..7059c8ae45843ffcc5f444a5ea9e562007578cd0 100644 (file)
@@ -19,21 +19,22 @@ bool net_match_config(Set *match_mac,
                       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,
index 66f49ed44ec805656ef5c473a9ad22e810c1a93c..517e4dc528afcd641c7ff2441916b734a056e8ff 100644 (file)
@@ -98,6 +98,6 @@ struct sd_radv_prefix {
         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__)
index 6e077c0860c543857b918b7fd811ecc7b0fe701b..85238c21d1efc954b2a6cfff975b6953a2313e00 100644 (file)
@@ -576,7 +576,7 @@ static void client_stop(sd_dhcp_client *client, int error) {
         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
@@ -1385,6 +1385,23 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force,
         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;
@@ -1437,12 +1454,10 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t le
 
         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);
         }
index 8b424811ad16a97ba170aea81e7b509f93ed9e77..8aebb53c873da63c6d0e0d889070c4188718cf51 100644 (file)
@@ -205,12 +205,8 @@ int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
         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;
 
@@ -336,12 +332,8 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen)
         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;
 
index c8e34497fd04f463b3da2d6a1da8a43046a77136..d960027a0f9bc4c13a5d3cf2c692aaa674455161 100644 (file)
@@ -72,7 +72,7 @@ struct sd_ipv4acd {
         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) {
index a59a95232656bc8d1c85a14690b593264a95c161..3104d8c5a8e5ea5e077e79ba49071d20ab2dcda7 100644 (file)
@@ -50,7 +50,7 @@ struct sd_ipv4ll {
         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);
index ef53d0a450ac380c913b6c9d6b57318ec2515c11..edd30bf84d8ad1f2b948850d8829efa6cda26e74 100644 (file)
@@ -56,6 +56,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
         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),
index 2544bdebc1027256696ef84d669831ec572edc9f..4a29b3bea8ecba1ececc7270bc2c77b606994460 100644 (file)
@@ -27,6 +27,8 @@
 #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"
@@ -51,6 +53,7 @@
 #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"
index f817cf0a850d49ba959339eb33fc0dbf8043b1fc..b7ca79bb583f5b999c2fba00a45936574ca6ea93 100644 (file)
@@ -803,9 +803,12 @@ _public_ int sd_bus_get_owner_creds(sd_bus *bus, uint64_t mask, sd_bus_creds **r
 
 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);
 
@@ -814,16 +817,24 @@ int bus_add_match_internal(
 
         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(
index 3fb52b67c6ad399aaead6661e3d1a628f0b788db..eb1ae75c147e784bf5d74879ed54d01c63c9491b 100644 (file)
@@ -3,7 +3,7 @@
 
 #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);
index 81d97ff968bb92db7b50dde2d88c74af4eabf1a7..908b9e75b25429a572de963e90d09c5cc8b504fc 100644 (file)
@@ -662,7 +662,9 @@ static int has_cap(sd_bus_creds *c, size_t offset, int capability) {
         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));
 }
@@ -714,7 +716,7 @@ static int parse_caps(sd_bus_creds *c, unsigned offset, const char *p) {
         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);
@@ -1259,7 +1261,7 @@ int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret)
         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;
 
index 96319645d1f70e94412d6b3ce78605e04a549bb4..386187412b786a4b45a46696a35a42332a139209 100644 (file)
@@ -577,7 +577,7 @@ const char *bus_error_message(const sd_bus_error *e, int error) {
         if (error < 0)
                 error = -error;
 
-        return strerror(error);
+        return strerror_safe(error);
 }
 
 static bool map_ok(const sd_bus_error_map *map) {
index 39610c5d450052a6acf38eb5f276d6800d05b913..6d2c1e84051a8ac0df1f365b69a6ca973c7957a7 100644 (file)
@@ -44,6 +44,11 @@ struct match_callback {
 
         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;
@@ -226,6 +231,7 @@ struct sd_bus {
         size_t wqueue_allocated;
 
         uint64_t cookie;
+        uint64_t read_counter; /* A counter for each incoming msg */
 
         char *unique_name;
         uint64_t unique_id;
index 14204eeb6b2d35ea614515d6467d69df9c393c9f..57ce8cca0439bf1dce0669ccbc5608c601aee0ef 100644 (file)
@@ -287,8 +287,16 @@ int bus_match_run(
         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;
                 }
index 774bf10219089ed4e8ffa74d72c743616c1411b4..5360bce90409aedb1ef7ce390b935aed776bb59d 100644 (file)
@@ -5265,7 +5265,7 @@ int bus_message_parse_fields(sd_bus_message *m) {
                         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;
 
index a7c4f81c4bac4f0879aa894c13f299d8250e9637..ced0bb3d34a91ea1764b70669ba5376648806d03 100644 (file)
@@ -128,6 +128,8 @@ struct sd_bus_message {
 
         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) {
index 9221b33d487d4ab6978da48993c7b6751e44d36f..3541c411a8b42b4ff49808906dbc601454696d83 100644 (file)
@@ -1146,6 +1146,7 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) {
         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);
         }
index 1c25ba9dea257db6d8f121a3bd69f3fe40313292..303dcea106ff2f3a63950d75f3b9d074ebcf1328 100644 (file)
@@ -36,6 +36,7 @@
 #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"
@@ -483,6 +484,7 @@ static int synthesize_connected_signal(sd_bus *bus) {
                 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)
@@ -2421,6 +2423,8 @@ static int process_timeout(sd_bus *bus) {
         if (r < 0)
                 return r;
 
+        m->read_counter = ++bus->read_counter;
+
         r = bus_seal_synthetic_message(bus, m);
         if (r < 0)
                 return r;
@@ -2523,6 +2527,7 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) {
                 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)
@@ -2865,6 +2870,8 @@ static int process_closing_reply_callback(sd_bus *bus, struct reply_callback *c)
         if (r < 0)
                 return r;
 
+        m->read_counter = ++bus->read_counter;
+
         r = bus_seal_synthetic_message(bus, m);
         if (r < 0)
                 return r;
@@ -2929,6 +2936,7 @@ static int process_closing(sd_bus *bus, sd_bus_message **ret) {
                 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)
@@ -3238,8 +3246,6 @@ static int add_match_callback(
                 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));
@@ -3247,6 +3253,9 @@ static int add_match_callback(
                 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);
 
@@ -3318,7 +3327,7 @@ static int bus_add_match_full(
                                  * 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;
 
@@ -3756,7 +3765,7 @@ _public_ int sd_bus_path_encode(const char *prefix, const char *external_id, cha
         if (!e)
                 return -ENOMEM;
 
-        ret = strjoin(prefix, "/", e);
+        ret = path_join(prefix, e);
         if (!ret)
                 return -ENOMEM;
 
index 6181fb163e1f64399b29e158b1e22fe96244eb73..d9716ae74514f80fd83c82b41bb19c50b7093f5e 100644 (file)
@@ -12,6 +12,7 @@
 #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"
@@ -308,7 +309,7 @@ static void* client1(void *p) {
 
         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;
         }
 
index f464b5b23de42b7715489957e60ad932b6925f56..cb5d62099383582c86b6d967747915ee63117b52 100644 (file)
@@ -6,6 +6,7 @@
 #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;
@@ -88,7 +89,7 @@ static void test_error(void) {
         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));
index ade16e532df3180bcf9c45911dc8f533e2743de9..d7d51ba9504f4b2bc9196d8c8851d0de37503b51 100644 (file)
@@ -203,6 +203,7 @@ int main(int argc, char *argv[]) {
         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;
index 4357860170a5f905fd14880e6570719485232a14..5b54641213ee180d250e939957e91bb032a5f20a 100644 (file)
@@ -752,7 +752,7 @@ static int parent_crawl_children(sd_device_enumerator *enumerator, const char *p
                 if (dent->d_type != DT_DIR)
                         continue;
 
-                child = strjoin(path, "/", dent->d_name);
+                child = path_join(path, dent->d_name);
                 if (!child)
                         return -ENOMEM;
 
index a0855001e308f1af59951d253fa7ae8e8407625b..16f8627bdbc147a511f58d38b980f8180d0d442d 100644 (file)
@@ -609,8 +609,7 @@ void device_set_watch_handle(sd_device *device, int handle) {
 
 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);
@@ -620,7 +619,7 @@ int device_rename(sd_device *device, const char *name) {
         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);
@@ -999,3 +998,7 @@ static const char* const device_action_table[_DEVICE_ACTION_MAX] = {
 };
 
 DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction);
+
+void dump_device_action_table(void) {
+        DUMP_STRING_TABLE(device_action, DeviceAction, _DEVICE_ACTION_MAX);
+}
index 9696f9c02dcc4e343308409a4d8e2cd46930834c..2bde53e969b1463fbb19abad73f81de8c1eafc18 100644 (file)
@@ -75,3 +75,4 @@ static inline int device_read_db(sd_device *device) {
 
 DeviceAction device_action_from_string(const char *s) _pure_;
 const char *device_action_to_string(DeviceAction a) _const_;
+void dump_device_action_table(void);
index 94f6174bff961a95a83acaec0bab27a36c9bb581..a25682d89360ff19ef447df35f7c1e05a155b4cb 100644 (file)
@@ -39,7 +39,7 @@
                                                                         \
                 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__);         \
         })
index c2315c03fe6d803403e0a0c747fa59c3270c822a..617d6de667f88bf8bc8f8b3773fc636af5e96ec9 100644 (file)
@@ -167,7 +167,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
                                                        "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;
 
@@ -201,6 +201,10 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
 
         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;
@@ -1600,14 +1604,16 @@ static int device_sysattrs_read_all(sd_device *device) {
                 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;
@@ -1733,8 +1739,7 @@ static int device_get_sysattr_value(sd_device *device, const char *_key, const c
  * 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;
 
@@ -1761,7 +1766,7 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
         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 */
@@ -1836,7 +1841,7 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
         if (r < 0)
                 return r;
 
-        path = strjoina(syspath, "/", sysattr);
+        path = prefix_roota(syspath, sysattr);
 
         len = strlen(_value);
 
index 09285c19d86b6d29047128a2be01e2ee3c0bc13b..5adbceeb0247c7a7dcdf8e9ab22df27fe0927bed 100644 (file)
@@ -23,6 +23,7 @@
 #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)
@@ -3248,15 +3249,16 @@ _public_ int sd_event_dispatch(sd_event *e) {
 }
 
 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) {
index 6fd6d9f0b041fb81005e5a61cfd93a9e9b45712c..224ea93a77447d544ccdf12750a10a5f0dad9263 100644 (file)
@@ -10,6 +10,7 @@
 #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"
@@ -464,7 +465,7 @@ static void test_inotify(unsigned n_create_events) {
                 _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);
         }
index 68b232b7d42242929285ad631bfa8d0aae42fca4..78504063234a631caf91bb26a208d66f0129ce2d 100644 (file)
@@ -802,24 +802,18 @@ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type,
 
 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");
@@ -830,7 +824,7 @@ static int netlink_container_parse(sd_netlink_message *m,
         }
 
         container->attributes = TAKE_PTR(attributes);
-        container->n_attributes = count;
+        container->n_attributes = n_allocated;
 
         return 0;
 }
@@ -911,14 +905,13 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
         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) {
@@ -931,6 +924,36 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
         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);
@@ -1015,7 +1038,6 @@ int sd_netlink_message_rewind(sd_netlink_message *m) {
 
                 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)
index d1c95b260f8f91b275b623a93668f65203a28857..98edb7e2bae2be11660a3005c69607c72deb7b87 100644 (file)
@@ -392,7 +392,7 @@ int socket_read_message(sd_netlink *rtnl) {
 
                 /* 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;
                 }
 
index 0ac3d1664b2ded813aaa758f31e95214d6667b70..450e298f2cac605c33e52f6cd8814cfc3f88a635 100644 (file)
@@ -327,6 +327,11 @@ static const NLType rtnl_link_info_data_macsec_types[] = {
         [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",
@@ -358,6 +363,7 @@ static const char* const nl_union_link_info_data_table[] = {
         [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);
@@ -411,6 +417,8 @@ static const NLTypeSystem rtnl_link_info_data_type_systems[] = {
                                                        .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 = {
@@ -785,9 +793,20 @@ static const NLTypeSystem genl_wireguard_type_system = {
         .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 = {
index fed43ae43c09640a23f54f033c497fcc82986908..efc59a0a4bc0dbbd281276f12475a97176b49f44 100644 (file)
@@ -83,6 +83,7 @@ typedef enum NLUnionLinkInfoData {
         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;
index b13fa224cc4c06a94f772d4ea185e35dedcbd942..868fcd01241892a24d6f61357e098544d5a9b10a 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <net/if.h>
 #include <netinet/ether.h>
+#include <linux/genetlink.h>
 
 #include "sd-netlink.h"
 
@@ -11,6 +12,7 @@
 #include "missing.h"
 #include "netlink-util.h"
 #include "socket-util.h"
+#include "stdio-util.h"
 #include "string-util.h"
 #include "util.h"
 
@@ -507,6 +509,48 @@ static void test_message(sd_netlink *rtnl) {
         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;
@@ -524,6 +568,7 @@ int main(void) {
         test_route(rtnl);
         test_message(rtnl);
         test_container(rtnl);
+        test_array();
 
         if_loopback = (int) if_nametoindex("lo");
         assert_se(if_loopback > 0);
index f46a3ff788daa43a7c9c7f1a55361e04e9c2f12d..08ed9426389c7fbbd8df1a9b8261b0fb0ab338de 100644 (file)
@@ -7,14 +7,19 @@
 #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;
@@ -32,3 +37,22 @@ static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = {
 };
 
 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);
index 6936fd536b9287d6e79d6ec7034fcffd993448b5..a19435393df6c3a61d2f716698bc1a4ca6ed0dac 100644 (file)
@@ -20,5 +20,30 @@ typedef enum LinkOperationalState {
         _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_;
index 2d715043e11fa1a0cfff17e8af01baa988ca1a6a..a3c0542d7a03ec6889209b713ea7fdfe51a9df8b 100644 (file)
 #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)
@@ -32,11 +32,23 @@ _public_ int sd_network_get_operational_state(char **state) {
         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;
@@ -149,6 +161,14 @@ _public_ int sd_network_link_get_operational_state(int ifindex, char **state) {
         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;
index 9949c23802f56d2457afc6a88b19653c30f47a1a..2dfde4ca7172d1c07e26afe49a600b47dd2057a6 100644 (file)
@@ -58,10 +58,7 @@ static int from_home_dir(const char *envname, const char *suffix, char **buffer,
         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;
 
@@ -328,8 +325,9 @@ static int get_path(uint64_t type, char **buffer, const char **ret) {
 }
 
 _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);
@@ -354,7 +352,7 @@ _public_ int sd_path_home(uint64_t type, const char *suffix, char **path) {
                 if (!buffer)
                         return -ENOMEM;
 
-                *path = buffer;
+                *path = TAKE_PTR(buffer);
                 return 0;
         }
 
@@ -369,23 +367,16 @@ _public_ int sd_path_home(uint64_t type, const char *suffix, char **path) {
                                 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;
 }
 
@@ -397,9 +388,9 @@ static int search_from_environment(
                 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);
@@ -412,7 +403,7 @@ static int search_from_environment(
                                 return -ENOMEM;
 
                         if (env_search_sufficient) {
-                                *list = l;
+                                *list = TAKE_PTR(l);
                                 return 0;
                         }
                 }
@@ -433,37 +424,27 @@ static int search_from_environment(
                 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;
 }
 
@@ -621,12 +602,7 @@ _public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) {
 
         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;
 
index 868cc023b12984b9ea689c4fc3c59625ed2d1c0d..00a6464e2d653647d58d3b3d4b39a3b08fbac4bf 100644 (file)
@@ -248,6 +248,8 @@ static int send_addrinfo_reply(
                 ._h_errno = _h_errno,
         };
 
+        msan_unpoison(&resp, sizeof(resp));
+
         if (ret == 0 && ai) {
                 void *p = &buffer;
                 struct addrinfo *k;
@@ -308,6 +310,8 @@ static int send_nameinfo_reply(
                 ._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);
@@ -352,6 +356,8 @@ static int handle_request(int out_fd, const Packet *packet, size_t length) {
                        .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;
 
@@ -960,6 +966,8 @@ int resolve_getaddrinfo_with_destroy_callback(
                 .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);
@@ -1046,6 +1054,8 @@ int resolve_getnameinfo_with_destroy_callback(
                 .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);
 
index 8a6e5a48f6596f615d287b8e095fd2b5a3b7876c..cd6c2a5bc57157fd14ecd93f740e1da5f22733c2 100644 (file)
@@ -4,36 +4,7 @@
 #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);
index 357adf696440ef2377c6f4d783b2de7ff4a277d5..91dc910773c1b4caaf3e6ef6b36bdeb33efb94f9 100644 (file)
@@ -23,6 +23,7 @@
 #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
@@ -168,10 +199,24 @@ _public_ const char *udev_device_get_property_value(struct udev_device *udev_dev
 }
 
 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);
@@ -180,13 +225,12 @@ struct udev_device *udev_device_new(struct udev *udev, sd_device *device) {
                 .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;
 }
 
@@ -429,10 +473,10 @@ static struct udev_device *udev_device_free(struct udev_device *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);
 }
@@ -587,17 +631,17 @@ _public_ struct udev_list_entry *udev_device_get_devlinks_list_entry(struct udev
             !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);
 }
 
 /**
@@ -619,17 +663,17 @@ _public_ struct udev_list_entry *udev_device_get_properties_list_entry(struct ud
             !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);
 }
 
 /**
@@ -739,16 +783,16 @@ _public_ struct udev_list_entry *udev_device_get_sysattr_list_entry(struct udev_
         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);
 }
 
 /**
@@ -794,17 +838,17 @@ _public_ struct udev_list_entry *udev_device_get_tags_list_entry(struct udev_dev
             !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);
 }
 
 /**
@@ -821,3 +865,9 @@ _public_ int udev_device_has_tag(struct udev_device *udev_device, const char *ta
 
         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;
+}
index 80d5bafdf7d0a9cea774b9daf978bca4818f9d95..a8b3f53572413e967e230076c0ce22233adb27d8 100644 (file)
@@ -17,6 +17,7 @@
 #include "device-enumerator-private.h"
 #include "device-util.h"
 #include "libudev-device-internal.h"
+#include "libudev-list-internal.h"
 
 /**
  * SECTION:libudev-enumerate
@@ -34,7 +35,7 @@
 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;
@@ -50,6 +51,7 @@ struct udev_enumerate {
  **/
 _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;
 
@@ -61,6 +63,10 @@ _public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) {
         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);
@@ -69,17 +75,16 @@ _public_ struct udev_enumerate *udev_enumerate_new(struct udev *udev) {
                 .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);
 }
@@ -134,7 +139,7 @@ _public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enume
         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;
@@ -144,14 +149,14 @@ _public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enume
                         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);
 
@@ -168,12 +173,19 @@ _public_ struct udev_list_entry *udev_enumerate_get_list_entry(struct udev_enume
  * 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;
 }
 
 /**
@@ -186,12 +198,19 @@ _public_ int udev_enumerate_add_match_subsystem(struct udev_enumerate *udev_enum
  * 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;
 }
 
 /**
@@ -205,12 +224,19 @@ _public_ int udev_enumerate_add_nomatch_subsystem(struct udev_enumerate *udev_en
  * 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;
 }
 
 /**
@@ -224,12 +250,19 @@ _public_ int udev_enumerate_add_match_sysattr(struct udev_enumerate *udev_enumer
  * 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;
 }
 
 /**
@@ -243,12 +276,19 @@ _public_ int udev_enumerate_add_nomatch_sysattr(struct udev_enumerate *udev_enum
  * 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;
 }
 
 /**
@@ -261,12 +301,19 @@ _public_ int udev_enumerate_add_match_property(struct udev_enumerate *udev_enume
  * 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;
 }
 
 /**
@@ -280,12 +327,19 @@ _public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate,
  * 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;
 }
 
 /**
@@ -307,9 +361,16 @@ _public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumera
  * 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;
 }
 
 /**
@@ -322,12 +383,19 @@ _public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev
  * 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;
 }
 
 /**
@@ -356,6 +424,7 @@ _public_ int udev_enumerate_add_syspath(struct udev_enumerate *udev_enumerate, c
         if (r < 0)
                 return r;
 
+        udev_enumerate->devices_uptodate = false;
         return 0;
 }
 
index ed755e5d3cc0dcb0b72cd886f3f93a65822b8d64..5299e0a16f57a61382c96bcdb8231f909f6d51e8 100644 (file)
@@ -23,7 +23,7 @@
 struct udev_hwdb {
         unsigned n_ref;
         sd_hwdb *hwdb;
-        struct udev_list properties_list;
+        struct udev_list *properties_list;
 };
 
 /**
@@ -35,6 +35,7 @@ struct udev_hwdb {
  * 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;
@@ -43,6 +44,10 @@ _public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
         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);
@@ -50,10 +55,9 @@ _public_ struct udev_hwdb *udev_hwdb_new(struct udev *udev) {
         *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;
 }
 
@@ -61,7 +65,7 @@ static struct udev_hwdb *udev_hwdb_free(struct udev_hwdb *hwdb) {
         assert(hwdb);
 
         sd_hwdb_unref(hwdb->hwdb);
-        udev_list_cleanup(&hwdb->properties_list);
+        udev_list_free(hwdb->properties_list);
         return mfree(hwdb);
 }
 
@@ -105,13 +109,13 @@ _public_ struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev
         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);
 
index 4e1632c78d612a5e77bfeb940c0a756785f8fb96..a15b385343916bc700bcaf35c900fa26259dada5 100644 (file)
@@ -3,19 +3,14 @@
 
 #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);
index 00fd58b9ea50bf33384d566d68cf2b47734158be..95a9942f64f60f3d378159da22bc783b2d23ebbe 100644 (file)
@@ -1,13 +1,10 @@
 /* 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;
 }
 
 /**
@@ -237,15 +189,11 @@ struct udev_list_entry *udev_list_get_entry(struct udev_list *list) {
  * 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;
 }
 
 /**
@@ -258,18 +206,11 @@ _public_ struct udev_list_entry *udev_list_entry_get_next(struct udev_list_entry
  * 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);
 }
 
 /**
index e238e5a124c2df6f5778a5186c397e12a4b440a6..139451483941bf71554ea782a81ce76b0da5c3e4 100644 (file)
@@ -252,7 +252,7 @@ int x11_read_data(Context *c, sd_bus_message *m) {
                 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;
 
@@ -276,7 +276,7 @@ int x11_read_data(Context *c, sd_bus_message *m) {
                 } 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;
 
@@ -491,7 +491,7 @@ static int read_next_mapping(const char* filename,
                 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;
 
@@ -648,11 +648,11 @@ int find_legacy_keymap(Context *c, char **ret) {
                         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;
                         }
index b67966cdf6d0339b6a7cacd925b9968490b8fcb6..6010f048aef52fcb5407d22eaff2da72d1e55039 100644 (file)
@@ -14,6 +14,10 @@ SUBSYSTEM=="sound", KERNEL=="card*", TAG+="seat"
 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"
 
index 687a534f7b85d0cbc4e1b69ef63f077450a6073b..2ad9887066f6c09a2987bff7ce9e26b9e88fbe24 100644 (file)
@@ -846,23 +846,11 @@ static int show_session(int argc, char *argv[], void *userdata) {
         (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++) {
@@ -895,8 +883,7 @@ static int show_user(int argc, char *argv[], void *userdata) {
         (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);
 
@@ -953,12 +940,11 @@ static int show_seat(int argc, char *argv[], void *userdata) {
         (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++) {
@@ -1005,11 +991,8 @@ static int activate(int argc, char *argv[], void *userdata) {
         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;
@@ -1030,7 +1013,7 @@ static int activate(int argc, char *argv[], void *userdata) {
                                 &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;
index 6c9366761da71408a10a6b9ceff6a6f76910314b..fa92f4870a29090d400ed3f68d29ee5ef694de64 100644 (file)
@@ -8,6 +8,8 @@
 #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"
@@ -59,12 +61,8 @@ int manager_handle_action(
         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,
@@ -80,7 +78,9 @@ int manager_handle_action(
         /* 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;
                 }
         }
@@ -111,20 +111,21 @@ int manager_handle_action(
         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));
 
@@ -141,27 +142,23 @@ int manager_handle_action(
                 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;
 }
diff --git a/src/login/logind-brightness.c b/src/login/logind-brightness.c
new file mode 100644 (file)
index 0000000..8dfa97d
--- /dev/null
@@ -0,0 +1,256 @@
+/* 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;
+}
diff --git a/src/login/logind-brightness.h b/src/login/logind-brightness.h
new file mode 100644 (file)
index 0000000..b22ee37
--- /dev/null
@@ -0,0 +1,9 @@
+/* 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);
index be767186ff8f04dd43e07adfdadb03c9d05729bd..0e8925ab9449a29d7facea68a354e657345163e3 100644 (file)
 #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)
@@ -97,7 +132,6 @@ int manager_get_session_from_creds(Manager *m, sd_bus_message *message, const ch
 }
 
 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;
@@ -109,21 +143,20 @@ static int get_sender_user(Manager *m, sd_bus_message *message, sd_bus_error *er
                 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) {
@@ -145,7 +178,13 @@ int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid,
         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;
 
@@ -153,16 +192,17 @@ int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char
         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)
@@ -809,11 +849,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                 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);
@@ -827,9 +865,13 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus
                         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);
 
@@ -990,8 +1032,7 @@ static int method_activate_session_on_seat(sd_bus_message *message, void *userda
         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)
@@ -1367,11 +1408,22 @@ static int method_attach_device(sd_bus_message *message, void *userdata, sd_bus_
         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,
diff --git a/src/login/logind-dbus.h b/src/login/logind-dbus.h
new file mode 100644 (file)
index 0000000..6c73a96
--- /dev/null
@@ -0,0 +1,31 @@
+/* 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);
index e724365b131c209af7584c49a91348d384a98b79..20108544aad6de2b6a90cde84b7f40301088e906 100644 (file)
@@ -4,6 +4,7 @@
 
 #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) {
index d427dcaa0128a81af35bed164fab27e96682e3c9..d963706dcef98b227fd29d4c3e8a5b5d616b7636 100644 (file)
@@ -13,6 +13,7 @@
 #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"
index 6ee5a1c95d27ce94ee30c1a85a1ee1cd3a83af2f..c33a0e0ad43c7eefe140261e26f5c7260c0c845c 100644 (file)
@@ -7,7 +7,10 @@
 #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"
@@ -255,7 +258,10 @@ const sd_bus_vtable seat_vtable[] = {
 };
 
 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;
 
@@ -265,32 +271,25 @@ int seat_object_find(sd_bus *bus, const char *path, const char *interface, void
         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;
@@ -335,25 +334,47 @@ int seat_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
         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;
 }
 
diff --git a/src/login/logind-seat-dbus.h b/src/login/logind-seat-dbus.h
new file mode 100644 (file)
index 0000000..2590f64
--- /dev/null
@@ -0,0 +1,17 @@
+/* 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);
index f5ffb68238edacf21798cfd39b2e77f34d3d0650..c3ad5f9b5b00c6af1ea4e8cee00ce3e708b700f8 100644 (file)
@@ -9,11 +9,14 @@
 #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"
@@ -375,7 +378,7 @@ int seat_read_active_vt(Seat *s) {
 
         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;
         }
 
index 6236f1360bce0b77936785fa5d03c991f6a4cd84..64cdf2f25ae78fc9372432cb20f26f3f9570cf73 100644 (file)
@@ -67,13 +67,10 @@ void seat_add_to_gc_queue(Seat *s);
 
 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");
+}
index df5bfba98213bd9f601fec8c5d60a256cc5de945..c297f62cdf9936811be02a17064c627b778f38a3 100644 (file)
@@ -8,13 +8,20 @@
 #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(
@@ -479,6 +486,57 @@ static int method_pause_device_complete(sd_bus_message *message, void *userdata,
         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),
 
@@ -519,6 +577,7 @@ const sd_bus_vtable session_vtable[] = {
         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),
@@ -529,8 +588,11 @@ const sd_bus_vtable session_vtable[] = {
 };
 
 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);
@@ -539,32 +601,25 @@ int session_object_find(sd_bus *bus, const char *path, const char *interface, vo
         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;
@@ -609,10 +664,12 @@ int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char
         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);
@@ -620,13 +677,32 @@ int session_node_enumerator(sd_bus *bus, const char *path, void *userdata, char
                                         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;
 }
 
diff --git a/src/login/logind-session-dbus.h b/src/login/logind-session-dbus.h
new file mode 100644 (file)
index 0000000..9d2315c
--- /dev/null
@@ -0,0 +1,23 @@
+/* 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);
index 3e2ff6d5b8db454ac5e69d009c6c2dd705c4dbce..3057e72394d13f81152949fb7659014291b3518a 100644 (file)
@@ -7,14 +7,15 @@
 #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 {
index f1efeb0e017c7b3aa897aa1544b829764e7f756f..17700c6921901e766d83b0ef3bedce9022635092 100644 (file)
 #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"
index f3c17a8d918e6694d8fa643f9d6c5905af24cf2a..50fe29e7970cf651253bd848fe24aa1b171f4f5a 100644 (file)
@@ -7,6 +7,7 @@ typedef enum KillWho KillWho;
 #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 */
@@ -145,18 +146,6 @@ int session_kill(Session *s, KillWho who, int signo);
 
 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_;
 
@@ -179,7 +168,10 @@ bool session_is_controller(Session *s, const char *sender);
 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");
+}
index fcaeba13f6b2c20c5e6248415cef82e40d1ec7bd..beb97362e7302e48d1868f780a16463a577048b6 100644 (file)
@@ -6,6 +6,9 @@
 #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"
@@ -245,6 +248,10 @@ int user_object_find(sd_bus *bus, const char *path, const char *interface, void
                         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 {
@@ -305,10 +312,11 @@ int user_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***
         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));
diff --git a/src/login/logind-user-dbus.h b/src/login/logind-user-dbus.h
new file mode 100644 (file)
index 0000000..acfcb98
--- /dev/null
@@ -0,0 +1,17 @@
+/* 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);
index c5d442865cc39ec102c305cc463fe37d181db83e..b17fb2e3225e6372244acade33b5ce86328fa7aa 100644 (file)
@@ -19,7 +19,9 @@
 #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"
@@ -663,12 +665,12 @@ static bool elect_display_filter(Session *s) {
         /* 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,
index c41973e27dc01d8e64eed83991ff95b2fee24c1b..4bd65d8373414348ee0d225d9ac206b12083d437 100644 (file)
@@ -69,18 +69,7 @@ int user_check_linger_file(User *u);
 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);
index 4c0e8ce6b1c8668837cf0261792278c276547b0d..f4bc1d2eb7375600e24752b14993eca7c5170f30 100644 (file)
 #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"
@@ -44,10 +48,9 @@ static int manager_new(Manager **ret) {
         *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);
@@ -118,6 +121,7 @@ static Manager* manager_unref(Manager *m) {
         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);
@@ -1017,7 +1021,7 @@ static int manager_dispatch_idle_action(sd_event_source *s, uint64_t t, void *us
 
                 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;
@@ -1215,7 +1219,7 @@ static int run(int argc, char *argv[]) {
         (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)
index 7b6f73c6ec63510091b48e49af5651077ad83cfc..f260f2dc96de8e4115ea094fb26c93ad7a568732 100644 (file)
@@ -31,6 +31,7 @@ struct Manager {
         Hashmap *users;
         Hashmap *inhibitors;
         Hashmap *buttons;
+        Hashmap *brightness_writers;
 
         LIST_HEAD(Seat, seat_gc_queue);
         LIST_HEAD(Session, session_gc_queue);
@@ -158,24 +159,6 @@ void manager_reconnect_utmp(Manager *m);
 
 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);
 
@@ -184,11 +167,5 @@ int manager_set_lid_switch_ignore(Manager *m, usec_t until);
 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);
index 1cc75fd1cfeee3102795f2647c2fa5a9ee4a79c2..0a7d3d5440589e6e8f19931febacf88c857a1c80 100644 (file)
@@ -12,29 +12,35 @@ logind_gperf_c = custom_target(
         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]
@@ -60,6 +66,9 @@ user_runtime_dir_sources = files('''
         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',
@@ -68,9 +77,6 @@ if conf.get('ENABLE_LOGIND') == 1
         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',
index f3c13ad89a00ac139b4513d5c8d43c40c362907a..124a25810e313f2ab13c4d13f4b0fd4ad494dcc2 100644 (file)
                        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"/>
index a26895e695ea887be9090a2710576690ac5b4b04..3f762cbbc30b4d7d8c89f025bbad38c5491f0202 100644 (file)
@@ -21,6 +21,7 @@
 #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"
@@ -251,7 +252,7 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
         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 {
@@ -259,7 +260,7 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
                 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 {
@@ -267,11 +268,11 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co
                         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);
                 }
         }
 
@@ -290,11 +291,11 @@ static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, con
         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;
 }
@@ -310,7 +311,7 @@ static int append_session_cg_weight(pam_handle_t *handle, sd_bus_message *m, con
         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"))
@@ -375,7 +376,7 @@ static bool validate_runtime_directory(pam_handle_t *handle, const char *path, u
          * 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;
         }
 
@@ -549,7 +550,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         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;
         }
 
@@ -574,7 +575,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                         "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;
         }
 
@@ -593,13 +594,13 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                         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;
         }
 
@@ -621,7 +622,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
 
         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;
         }
 
@@ -648,7 +649,7 @@ _public_ PAM_EXTERN int pam_sm_open_session(
                                 &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;
         }
 
@@ -759,7 +760,7 @@ _public_ PAM_EXTERN int pam_sm_close_session(
 
                 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;
                 }
 
index 62d5d26b04b8c09da86559f97e3a235f8b71ad2d..030a00ecb7ce85bb6b8028d6976475da3c03c26a 100644 (file)
@@ -1,11 +1,4 @@
-/***
-  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:
index 1ee878055eb164ccafe2f640ee26c004bc2fe586..b89e6046d93a679a8f35544a9ec6a22616184b72 100644 (file)
@@ -423,14 +423,10 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_
         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;
@@ -514,17 +510,12 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu
         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)
@@ -630,14 +621,10 @@ int bus_machine_method_open_shell(sd_bus_message *message, void *userdata, sd_bu
         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);
 
index b916d038d7a1c61c10e789aa84b2fb6be433c4fc..ef8ccd98fb249053a199fbf54185cc874aa4b05b 100644 (file)
@@ -530,29 +530,20 @@ int machine_kill(Machine *m, KillWho who, int signo) {
         return manager_kill_unit(m->manager, m->unit, signo, NULL);
 }
 
-int machine_openpt(Machine *m, int flags) {
+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;
index 9ff9a656717a94b2f1269c676365f571a52e5083..f7471be8f5281ed9a759613ec5db43c3802a6a81 100644 (file)
@@ -89,7 +89,7 @@ MachineState machine_state_from_string(const char *s) _pure_;
 const char *kill_who_to_string(KillWho k) _const_;
 KillWho kill_who_from_string(const char *s) _pure_;
 
-int machine_openpt(Machine *m, int flags);
+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);
index 43e216acad09f3a65546333f51a337ac000a6317..457ba4ea16f043fb3d46f99cedac779ccbf4a465 100644 (file)
@@ -789,7 +789,7 @@ static int find_loop_device(const char *backing_file, char **loop_dev) {
                 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;
 
@@ -802,7 +802,7 @@ static int find_loop_device(const char *backing_file, char **loop_dev) {
                 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;
 
@@ -1125,7 +1125,7 @@ static int acquire_mount_where(sd_device *d) {
                 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);
 
@@ -1257,7 +1257,7 @@ static int discover_loop_backing_file(void) {
                         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();
 
@@ -1533,7 +1533,8 @@ static int run(int argc, char* argv[]) {
         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;
         }
index 959421fc5c4455c5cbb042c70fab651c6c6c80bc..32317c7c9f8b5ba367188141a75d7efc2dedf92d 100644 (file)
@@ -43,6 +43,8 @@ sources = files('''
         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
@@ -55,14 +57,22 @@ sources = files('''
         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
@@ -79,6 +89,7 @@ sources = files('''
         networkd-radv.c
         networkd-radv.h
         networkd-network-bus.c
+        networkd-network-bus.h
         networkd-network.c
         networkd-network.h
         networkd-route.c
@@ -189,7 +200,6 @@ if conf.get('ENABLE_NETWORKD') == 1
      []],
 
     [['src/network/test-network-tables.c',
-      'src/network/test-network-tables.c',
       test_tables_h],
      [libnetworkd_core,
       libudev_static,
index a4967e817bcfe0b84472772afa3598288a946b9f..cf281e75a6d456ff512c896be5cd4d507b85a6ab 100644 (file)
@@ -718,7 +718,7 @@ int config_parse_macsec_key(
 
         _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;
@@ -743,18 +743,17 @@ int config_parse_macsec_key(
 
         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;
 
@@ -972,7 +971,7 @@ int config_parse_macsec_use_for_encoding(
 }
 
 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;
 
@@ -987,12 +986,10 @@ static int macsec_read_key_file(NetDev *netdev, SecurityAssociation *sa) {
                 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);
index 0cf6949968b759c7dace9f964c6a73df5c93b414..8641d18026be4a9d494770796ede9f93ce4afe1d 100644 (file)
@@ -22,6 +22,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #include "netdev/wireguard.h"
 #include "netdev/fou-tunnel.h"
 #include "netdev/l2tp-tunnel.h"
+#include "netdev/xfrm.h"
 #include "vlan-util.h"
 %}
 struct ConfigPerfItem;
@@ -69,6 +70,7 @@ Tunnel.IPv6FlowLabel,                     config_parse_ipv6_flowlabel,
 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)
@@ -218,3 +220,5 @@ WireGuardPeer.PublicKey,                  config_parse_wireguard_public_key,
 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)
index 542923760355d4cf7764c52f89395b7161cc302b..7735b455b79d57e1462a22535aae51e2308bb865 100644 (file)
@@ -29,6 +29,7 @@
 #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"
@@ -72,6 +73,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = {
         [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] = {
@@ -107,6 +109,7 @@ 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);
@@ -807,6 +810,9 @@ int netdev_load_one(Manager *manager, const char *filename) {
         case NETDEV_KIND_ERSPAN:
                 independent = ERSPAN(netdev)->independent;
                 break;
+        case NETDEV_KIND_XFRM:
+                independent = XFRM(netdev)->independent;
+                break;
         default:
                 break;
         }
index 57fabbef40ddfc36bfb63a991a3c90294a6a560a..732b563d67627a4d4a4fab9df45a00489c033784 100644 (file)
@@ -50,6 +50,7 @@ typedef enum NetDevKind {
         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
@@ -192,8 +193,8 @@ const struct ConfigPerfItem* network_netdev_gperf_lookup(const char *key, GPERF_
 #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__)
index 1572836b69cc7c1eab932df8701750c3b227b876..c2525408bacc24ef49f719a2944a71f0b4fdcadd 100644 (file)
@@ -44,10 +44,9 @@ static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_ne
 
         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");
         }
@@ -136,10 +135,9 @@ static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_
         }
 
         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");
         }
@@ -239,11 +237,10 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl
                 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");
         }
@@ -287,11 +284,9 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink
                 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");
         }
@@ -330,10 +325,9 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl
         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");
         }
@@ -435,8 +429,8 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) {
 
         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);
 
@@ -445,8 +439,8 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *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);
 
@@ -467,6 +461,10 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *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;
 }
 
index 3637e4f377256c2655aa1daf21a927d070fb38bc..681e80b0159d752011d74f6b36a82f4c472e996d 100644 (file)
@@ -51,6 +51,7 @@ typedef struct Tunnel {
         bool copy_dscp;
         bool independent;
         bool fou_tunnel;
+        bool assign_to_loopback;
 
         uint16_t encap_src_port;
         uint16_t fou_destination_port;
index 154ca3b7a4490c71a1692b8c91a50059ba615cd3..2e9651ccebee878a473e253ad45132136ac74152 100644 (file)
@@ -480,7 +480,7 @@ static int wireguard_decode_key_and_warn(
                 unsigned line,
                 const char *lvalue) {
 
-        _cleanup_free_ void *key = NULL;
+        _cleanup_(erase_and_freep) void *key = NULL;
         size_t len;
         int r;
 
@@ -501,12 +501,10 @@ static int wireguard_decode_key_and_warn(
         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;
@@ -894,7 +892,7 @@ static void wireguard_done(NetDev *netdev) {
 }
 
 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;
 
@@ -905,17 +903,11 @@ static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_
         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) {
diff --git a/src/network/netdev/xfrm.c b/src/network/netdev/xfrm.c
new file mode 100644 (file)
index 0000000..afedb4b
--- /dev/null
@@ -0,0 +1,33 @@
+/* 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
+};
diff --git a/src/network/netdev/xfrm.h b/src/network/netdev/xfrm.h
new file mode 100644 (file)
index 0000000..47355a5
--- /dev/null
@@ -0,0 +1,14 @@
+/* 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;
index b265f1f056634eb078215ca772ca2354517a6e2c..2a748b8599f7d9f5a98c20cb74dc4ae6fe4804d0 100644 (file)
@@ -22,6 +22,7 @@
 #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"
@@ -119,8 +120,14 @@ typedef struct LinkInfo {
                 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;
@@ -128,6 +135,7 @@ typedef struct LinkInfo {
         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) {
@@ -229,11 +237,11 @@ static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
                                       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);
 
@@ -241,7 +249,7 @@ static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
         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;
 }
@@ -249,6 +257,7 @@ static int acquire_link_bitrates(sd_bus *bus, LinkInfo *link) {
 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;
@@ -269,14 +278,22 @@ static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, Lin
                 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);
@@ -898,32 +915,6 @@ static int dump_statistics(Table *table, const LinkInfo *info) {
         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,
@@ -1043,8 +1034,8 @@ static int link_status_one(
         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;
 
@@ -1106,10 +1097,10 @@ static int link_status_one(
                 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;
         }
@@ -1140,11 +1131,7 @@ static int link_status_one(
         }
 
         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)
@@ -1153,9 +1140,9 @@ static int link_status_one(
                 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;
         }
@@ -1172,6 +1159,59 @@ static int link_status_one(
                         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;
index d6c6f5f27159198ad825e621f46a342886d7a23a..a207bb07901df5dc01be861690ac35efcbd8baaf 100644 (file)
@@ -566,6 +566,11 @@ int address_configure(
         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)
@@ -665,7 +670,7 @@ int address_configure(
                 return log_link_error_errno(link, r, "Could not add address: %m");
         }
 
-        return 0;
+        return 1;
 }
 
 int config_parse_broadcast(
@@ -788,7 +793,7 @@ int config_parse_address(const char *unit,
                 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;
 
index 8f9103f1466d2a54bafa66021aea3214bb88b490..c3c5d535ac663d94e8fb486a1b278a9d9134104f 100644 (file)
 
 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) {
@@ -44,16 +44,16 @@ 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++) {
@@ -63,7 +63,7 @@ static int append_vlan_info_data(Link *const link, sd_netlink_message *req, uint
 
                 base_bit = k * 32;
                 i = -1;
-                done = 0;
+                done = false;
                 do {
                         j = find_next_bit(i, vid_map);
                         if (j > 0) {
@@ -80,7 +80,7 @@ static int append_vlan_info_data(Link *const link, sd_netlink_message *req, uint
                                         goto next;
                                 }
                         } else
-                                done = 1;
+                                done = true;
 
                         if (begin != UINT16_MAX) {
                                 cnt++;
@@ -129,9 +129,8 @@ static int append_vlan_info_data(Link *const link, sd_netlink_message *req, uint
                         i = j;
                 } while (!done);
         }
-        if (!cnt)
-                return -EINVAL;
 
+        assert(cnt > 0);
         return cnt;
 }
 
@@ -149,9 +148,9 @@ static int set_brvlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *lin
 
 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);
@@ -204,57 +203,15 @@ int br_vlan_configure(Link *link, uint16_t pvid, uint32_t *br_vid_bitmap, uint32
         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;
@@ -271,8 +228,8 @@ int config_parse_brvlan_vlan(const char *unit, const char *filename,
                              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);
@@ -286,16 +243,9 @@ int config_parse_brvlan_vlan(const char *unit, const char *filename,
                 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;
 }
@@ -321,19 +271,11 @@ int config_parse_brvlan_untagged(const char *unit, const char *filename,
                 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;
 }
index f6af140d3eee79d83f458c6317e0c510eedec976..7533034021690c9ec7694e0b08109e17a385ff53 100644 (file)
@@ -9,6 +9,9 @@
 
 #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);
diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c
new file mode 100644 (file)
index 0000000..81edb8c
--- /dev/null
@@ -0,0 +1,206 @@
+/* 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);
diff --git a/src/network/networkd-dhcp-common.h b/src/network/networkd-dhcp-common.h
new file mode 100644 (file)
index 0000000..c5af0be
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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);
diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c
new file mode 100644 (file)
index 0000000..f4c2178
--- /dev/null
@@ -0,0 +1,347 @@
+/* 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;
+        }
+}
diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h
new file mode 100644 (file)
index 0000000..e97e56b
--- /dev/null
@@ -0,0 +1,11 @@
+/* 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);
index 4ed020ea8e59b5f9e9bac8605ec03a2371387127..c0d776f464b75b5b541e944e9fd06f90229dfa38 100644 (file)
@@ -7,12 +7,42 @@
 #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;
 
@@ -21,14 +51,20 @@ static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li
 
         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);
         }
 
@@ -111,10 +147,12 @@ static int link_set_dhcp_routes(Link *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++;
         }
@@ -153,7 +191,7 @@ static int link_set_dhcp_routes(Link *link) {
 
                 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++;
 
@@ -169,11 +207,8 @@ static int link_set_dhcp_routes(Link *link) {
                 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++;
         }
@@ -181,10 +216,32 @@ static int link_set_dhcp_routes(Link *link) {
         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);
@@ -192,12 +249,19 @@ static int dhcp_remove_routes(Link *link, struct in_addr *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);
 
@@ -215,6 +279,11 @@ static int dhcp_remove_routes(Link *link, struct in_addr *address) {
                 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);
         }
@@ -222,7 +291,7 @@ static int dhcp_remove_routes(Link *link, struct in_addr *address) {
         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;
@@ -234,7 +303,7 @@ static int dhcp_remove_router(Link *link, struct in_addr *address) {
         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;
@@ -278,7 +347,7 @@ static int dhcp_remove_router(Link *link, struct in_addr *address) {
         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;
@@ -296,7 +365,7 @@ static int dhcp_remove_address(Link *link, struct in_addr *address) {
         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);
@@ -365,9 +434,9 @@ static int dhcp_lease_lost(Link *link) {
         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);
 
@@ -382,20 +451,32 @@ static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, 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);
         }
 
@@ -464,18 +545,15 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *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;
 }
@@ -508,6 +586,12 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
 
         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");
@@ -581,16 +665,38 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) {
                 }
         }
 
-        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;
         }
 
@@ -649,7 +755,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
                                         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;
                         }
@@ -667,9 +773,7 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
 
                         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;
                         }
@@ -682,12 +786,17 @@ static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) {
                                 }
                         }
 
-                        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;
@@ -975,3 +1084,175 @@ int dhcp4_configure(Link *link) {
 
         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");
diff --git a/src/network/networkd-dhcp4.h b/src/network/networkd-dhcp4.h
new file mode 100644 (file)
index 0000000..117b711
--- /dev/null
@@ -0,0 +1,28 @@
+/* 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);
index afa3e2cfd68a883d06aa0247825585c337ee4f92..286b9071ae7c91e6678afd58ce23faf7d482df94 100644 (file)
@@ -13,6 +13,7 @@
 #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"
@@ -20,6 +21,9 @@
 #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)
@@ -90,7 +94,7 @@ static int dhcp6_pd_prefix_assign(Link *link, struct in6_addr *prefix,
         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;
 
@@ -104,7 +108,7 @@ static int dhcp6_route_remove_handler(sd_netlink *nl, sd_netlink_message *m, Lin
 
         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;
 }
@@ -202,7 +206,7 @@ static int dhcp6_pd_prefix_distribute(Link *dhcp6_link, Iterator *i,
                 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;
 
@@ -488,7 +492,7 @@ static void dhcp6_handler(sd_dhcp6_client *client, int event, void *userdata) {
                         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;
@@ -685,3 +689,123 @@ int dhcp6_configure(Link *link) {
 
         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;
+}
diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h
new file mode 100644 (file)
index 0000000..26d810f
--- /dev/null
@@ -0,0 +1,15 @@
+/* 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);
index 4ae511fc7aeb3f9a3f4bfe5148d0af5d69f1c512..e3333698f8aea9766e739a582a73b5791eac3aed 100644 (file)
@@ -124,6 +124,11 @@ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
         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)
@@ -169,7 +174,7 @@ int fdb_entry_configure(Link *link, FdbEntry *fdb_entry) {
 
         link_ref(link);
 
-        return 0;
+        return 1;
 }
 
 /* remove and FDB entry. */
index 9df5646a7e61c0186375383f7b42c8ea4ee2334d..d76f02d56397c944efc4cecb9689e2ac770cfee7 100644 (file)
@@ -5,8 +5,10 @@
 
 #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;
@@ -210,7 +212,7 @@ int ipv4ll_configure(Link *link) {
         }
 
         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;
@@ -234,3 +236,45 @@ int ipv4ll_configure(Link *link) {
 
         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;
+}
diff --git a/src/network/networkd-ipv4ll.h b/src/network/networkd-ipv4ll.h
new file mode 100644 (file)
index 0000000..49b6fb5
--- /dev/null
@@ -0,0 +1,12 @@
+/* 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);
index 2f414cb11652d124e93ab7283d1b265c3a07f555..eda0325c2b94ff2c2560e2e52185fc874d5a1f14 100644 (file)
@@ -3,12 +3,15 @@
 #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(
@@ -22,7 +25,8 @@ 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);
@@ -40,32 +44,34 @@ static int property_get_bit_rates(
                 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;
@@ -144,17 +150,15 @@ int link_object_find(sd_bus *bus, const char *path, const char *interface, void
         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)
@@ -164,5 +168,13 @@ int link_send_changed(Link *link, const char *property, ...) {
                         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);
 }
diff --git a/src/network/networkd-link-bus.h b/src/network/networkd-link-bus.h
new file mode 100644 (file)
index 0000000..58005a4
--- /dev/null
@@ -0,0 +1,20 @@
+/* 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);
index 6613992a091f5fe043a7c24d676dc568f9f1498b..59f3f1dd31fb1058811d078393c22d890320fbcf 100644 (file)
 #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"
@@ -62,6 +68,27 @@ DUID* link_get_duid(Link *link) {
                 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);
 
@@ -80,7 +107,7 @@ static bool link_dhcp6_enabled(Link *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;
@@ -132,7 +159,9 @@ bool link_ipv4ll_enabled(Link *link, AddressFamilyBoolean mask) {
         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. */
@@ -142,9 +171,6 @@ bool link_ipv4ll_enabled(Link *link, AddressFamilyBoolean mask) {
         if (link->network->bond)
                 return false;
 
-        if (link->network->bond)
-                return false;
-
         return link->network->link_local & mask;
 }
 
@@ -166,7 +192,7 @@ static bool link_ipv6ll_enabled(Link *link) {
         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;
@@ -181,7 +207,7 @@ static bool link_ipv6_enabled(Link *link) {
         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"))
@@ -230,7 +256,7 @@ static bool link_ipv6_forward_enabled(Link *link) {
         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;
@@ -344,69 +370,110 @@ static void link_update_master_operstate(Link *link, NetDev *netdev) {
 
 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);
@@ -541,6 +608,7 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) {
                 .rtnl_extended_attrs = true,
                 .ifindex = ifindex,
                 .iftype = iftype,
+                .sysctl_ipv6_enabled = -1,
         };
 
         link->ifname = strdup(ifname);
@@ -680,14 +748,17 @@ static void link_enter_unmanaged(Link *link) {
         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");
@@ -731,40 +802,11 @@ void link_enter_failed(Link *link) {
 
         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;
@@ -821,22 +863,21 @@ static int link_request_set_routing_policy_rule(Link *link) {
         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);
@@ -910,8 +951,8 @@ int link_request_set_routes(Link *link) {
                                 link_enter_failed(link);
                                 return r;
                         }
-
-                        link->route_messages++;
+                        if (r > 0)
+                                link->route_messages++;
                 }
 
         if (link->route_messages == 0) {
@@ -957,23 +998,26 @@ void link_check_ready(Link *link) {
         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);
@@ -1041,106 +1085,6 @@ static int address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *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;
@@ -1191,8 +1135,8 @@ static int link_request_set_addresses(Link *link) {
                         link_enter_failed(link);
                         return r;
                 }
-
-                link->address_messages++;
+                if (r > 0)
+                        link->address_messages++;
         }
 
         LIST_FOREACH(labels, label, link->network->address_labels) {
@@ -1209,118 +1153,11 @@ static int link_request_set_addresses(Link *link) {
         /* 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");
         }
 
@@ -2485,6 +2322,33 @@ static bool link_is_static_route_configured(Link *link, Route *route) {
         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;
@@ -2496,6 +2360,12 @@ static int link_drop_foreign_config(Link *link) {
                 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)
@@ -2512,6 +2382,14 @@ static int link_drop_foreign_config(Link *link) {
                 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)
@@ -2578,7 +2456,8 @@ static int link_configure(Link *link) {
 
         /* 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;
@@ -3264,7 +3143,7 @@ static int link_carrier_lost(Link *link) {
         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;
@@ -3501,7 +3380,7 @@ static void print_link_hashmap(FILE *f, const char *prefix, Hashmap* h) {
 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;
@@ -3525,6 +3404,12 @@ int link_save(Link *link) {
         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;
@@ -3534,8 +3419,10 @@ int link_save(Link *link) {
         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;
@@ -3587,7 +3474,7 @@ int link_save(Link *link) {
                                         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);
@@ -3630,7 +3517,7 @@ int link_save(Link *link) {
                                         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;
 
index d0290f7c7d56aae2d9454b0f2a5002d01ad3dc61..43efa42312412ede531cc4c299f64746fc0d915c 100644 (file)
@@ -60,6 +60,8 @@ typedef struct Link {
 
         LinkState state;
         LinkOperationalState operstate;
+        LinkCarrierState carrier_state;
+        LinkAddressState address_state;
 
         unsigned address_messages;
         unsigned address_label_messages;
@@ -78,7 +80,7 @@ typedef struct Link {
         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;
@@ -126,6 +128,8 @@ typedef struct Link {
         /* 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*);
@@ -164,33 +168,20 @@ int link_ipv6ll_gained(Link *link, const struct in6_addr *address);
 
 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, \
index 8c527837f8e6eb17e336077a0f50e2643d4091c1..a40220d108da4d5b2f0dabbb7629056332492761 100644 (file)
@@ -2,32 +2,30 @@
 
 #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);
 }
index 6984c5b9679421ccd773088d0de41330fd0183de..33b7ec1d6c33f7266f274e10d3d48058c4206fb9 100644 (file)
 #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"
@@ -279,7 +282,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
         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;
         }
@@ -289,7 +292,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
                 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;
         }
 
@@ -298,31 +301,31 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
                 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;
         }
 
@@ -330,25 +333,25 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
         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;
                 }
 
@@ -357,32 +360,32 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
         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;
         }
 
@@ -447,7 +450,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
 
                 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));
         }
@@ -458,7 +461,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
                         /* 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;
                         }
                 }
@@ -472,7 +475,7 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo
                 break;
 
         default:
-                assert_not_reached("Received invalid RTNL message type");
+                assert_not_reached("Received route message with invalid RTNL message type");
         }
 
         return 1;
@@ -498,7 +501,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
         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;
         }
@@ -508,49 +511,49 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
                 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;
         }
 
@@ -558,7 +561,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
         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;
                 }
 
@@ -567,7 +570,7 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
         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;
                 }
 
@@ -578,10 +581,8 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
         }
 
         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) {
@@ -597,38 +598,40 @@ int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message,
         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");
         }
@@ -651,7 +654,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
         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;
         }
@@ -661,16 +664,16 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
                 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;
         }
 
@@ -689,7 +692,7 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
                         /* 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;
                         }
                 }
@@ -698,14 +701,14 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
                         /* 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;
                 }
 
@@ -718,22 +721,19 @@ static int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *messa
                 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);
@@ -743,7 +743,7 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
         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;
         }
@@ -753,41 +753,47 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
                 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;
                         }
                 }
@@ -795,26 +801,26 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
                 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;
                         }
                 }
@@ -822,25 +828,44 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
                 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;
@@ -851,39 +876,45 @@ int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, voi
                 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;
                         }
                 }
@@ -1077,9 +1108,12 @@ static int manager_save(Manager *m) {
         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);
@@ -1109,6 +1143,12 @@ static int manager_save(Manager *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;
 
@@ -1180,9 +1220,18 @@ static int manager_save(Manager *m) {
                 }
         }
 
+        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;
@@ -1191,7 +1240,10 @@ static int manager_save(Manager *m) {
 
         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);
@@ -1213,9 +1265,26 @@ static int manager_save(Manager *m) {
 
         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;
@@ -1246,126 +1315,6 @@ static int manager_dirty_handler(sd_event_source *s, void *userdata) {
         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;
@@ -1382,8 +1331,6 @@ int manager_new(Manager **ret) {
         if (!m->state_file)
                 return -ENOMEM;
 
-        m->sysctl_ipv6_enabled = -1;
-
         r = sd_event_default(&m->event);
         if (r < 0)
                 return r;
@@ -1440,14 +1387,14 @@ void manager_free(Manager *m) {
         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);
         }
@@ -1886,18 +1833,3 @@ int manager_request_product_uuid(Manager *m, Link *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;
-}
index c816d86d1877c4fcbd19a1cf6dd8b19ab25d8172..9b3b05bccc3a1b00bb3a59e5de00841b697751f7 100644 (file)
@@ -33,6 +33,8 @@ struct Manager {
 
         char *state_file;
         LinkOperationalState operational_state;
+        LinkCarrierState carrier_state;
+        LinkAddressState address_state;
 
         Hashmap *links;
         Hashmap *netdevs;
@@ -55,8 +57,6 @@ struct Manager {
         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;
@@ -85,7 +85,7 @@ int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, vo
 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);
@@ -96,10 +96,4 @@ int manager_set_hostname(Manager *m, const char *hostname);
 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);
index 3016f3448bddc9644767010f21f0246ae62e71d4..78f89e026e4f035e35398145472abbcfb35c0a4f 100644 (file)
@@ -9,6 +9,7 @@
 #include "sd-ndisc.h"
 
 #include "missing_network.h"
+#include "networkd-dhcp6.h"
 #include "networkd-ndisc.h"
 #include "networkd-route.h"
 #include "strv.h"
@@ -121,8 +122,8 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
                 link_enter_failed(link);
                 return r;
         }
-
-        link->ndisc_messages++;
+        if (r > 0)
+                link->ndisc_messages++;
 
         return 0;
 }
@@ -209,8 +210,8 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
                 link_enter_failed(link);
                 return r;
         }
-
-        link->ndisc_messages++;
+        if (r > 0)
+                link->ndisc_messages++;
 
         return 0;
 }
@@ -259,8 +260,8 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
                 link_enter_failed(link);
                 return r;
         }
-
-        link->ndisc_messages++;
+        if (r > 0)
+                link->ndisc_messages++;
 
         return 0;
 }
@@ -320,8 +321,8 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
                 link_enter_failed(link);
                 return r;
         }
-
-        link->ndisc_messages++;
+        if (r > 0)
+                link->ndisc_messages++;
 
         return 0;
 }
@@ -763,6 +764,9 @@ int config_parse_ndisc_black_listed_prefix(
                         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();
@@ -773,12 +777,8 @@ int config_parse_ndisc_black_listed_prefix(
 
                 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;
                 }
 
index e3ba148ce1bf744d171fb5f68145436494bb89bc..a895f7268882f9de14d3e19d9e8c70e82aa932d5 100644 (file)
@@ -3,6 +3,7 @@
 #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"
 
diff --git a/src/network/networkd-network-bus.h b/src/network/networkd-network-bus.h
new file mode 100644 (file)
index 0000000..650e145
--- /dev/null
@@ -0,0 +1,11 @@
+/* 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);
index 9ef07ea372be1927393cc95028d4f5284b51aa0d..ed8c061efb525e058a5dc63bcbbc32187c035b88 100644 (file)
@@ -6,6 +6,10 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"")
 #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"
@@ -22,10 +26,11 @@ struct ConfigPerfItem;
 %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)
@@ -50,6 +55,7 @@ Network.VXLAN,                          config_parse_stacked_netdev,
 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)
@@ -87,6 +93,7 @@ Network.IPv6ProxyNDPAddress,            config_parse_ipv6_proxy_ndp_address,
 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
@@ -132,32 +139,33 @@ Route.InitialAdvertisedReceiveWindow,   config_parse_tcp_window,
 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)
@@ -218,9 +226,32 @@ CAN.RestartSec,                         config_parse_sec,
 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)
index a5e7cad58a4c9b910d4bd647e8397fe26912a424..097938057bb210d9a8d7be1634010e6b6b258998 100644 (file)
@@ -157,7 +157,8 @@ int network_verify(Network *network) {
 
         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.",
@@ -239,6 +240,22 @@ int network_verify(Network *network) {
                 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);
@@ -324,6 +341,7 @@ int network_load_one(Manager *manager, const char *filename) {
                 .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,
@@ -343,6 +361,9 @@ int network_load_one(Manager *manager, const char *filename) {
                 .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,
@@ -392,6 +413,8 @@ int network_load_one(Manager *manager, const char *filename) {
                 .ipv6_accept_ra_route_table = RT_TABLE_MAIN,
                 .ipv6_accept_ra_route_table_set = false,
 
+                .keep_configuration = _KEEP_CONFIGURATION_INVALID,
+
                 .can_triple_sampling = -1,
         };
 
@@ -406,6 +429,7 @@ int network_load_one(Manager *manager, const char *filename) {
                               "Route\0"
                               "DHCP\0"
                               "DHCPv4\0" /* compat */
+                              "DHCPv6\0"
                               "DHCPServer\0"
                               "IPv6AcceptRA\0"
                               "IPv6NDPProxyAddress\0"
@@ -488,6 +512,7 @@ static Network *network_free(Network *network) {
         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);
@@ -588,26 +613,16 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret) {
 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;
@@ -682,7 +697,8 @@ int config_parse_stacked_netdev(const char *unit,
         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,
@@ -792,110 +808,6 @@ int config_parse_domains(
         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,
@@ -1076,220 +988,6 @@ int config_parse_timezone(
         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,
@@ -1466,250 +1164,6 @@ int config_parse_ntp(
         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,
@@ -1752,3 +1206,16 @@ int config_parse_required_for_online(
 
         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);
index d2a0b8c5f14ee6d59e637d4033d7da34390ae7a7..82fad492a8f30b24db055888f964febbfbabc833 100644 (file)
@@ -6,13 +6,14 @@
 
 #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,
@@ -52,31 +35,16 @@ typedef enum IPv6PrivacyExtensions {
         _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;
 
@@ -93,6 +61,7 @@ struct Network {
         char **match_driver;
         char **match_type;
         char **match_name;
+        char **match_property;
         LIST_HEAD(Condition, conditions);
 
         char *description;
@@ -100,6 +69,7 @@ struct Network {
         NetDev *bridge;
         NetDev *bond;
         NetDev *vrf;
+        NetDev *xfrm;
         Hashmap *stacked_netdevs;
         char *bridge_name;
         char *bond_name;
@@ -119,7 +89,7 @@ struct Network {
         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;
@@ -132,6 +102,10 @@ struct Network {
         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;
@@ -227,6 +201,7 @@ struct Network {
         bool unmanaged;
         bool configure_without_carrier;
         bool ignore_carrier_loss;
+        KeepConfiguration keep_configuration;
         uint32_t iaid;
         DUID duid;
 
@@ -296,43 +271,22 @@ void network_apply_anonymize_if_set(Network *network);
 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_;
index fdbf7cac62880f7105bfe5ca4e2b8576bf6d9ea6..25321aefed3a598432054faef7b41793c3b02b28 100644 (file)
@@ -6,6 +6,7 @@
 #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;
@@ -515,3 +442,185 @@ int radv_configure(Link *link) {
 
         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;
+}
index 3192bb8b6c8db04cd64c03be46f2186af07c9e17..45be083bfefec928bd51cd6aa144ad32ac0187ac 100644 (file)
 
 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;
@@ -26,11 +35,16 @@ void prefix_free(Prefix *prefix);
 
 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);
index b21e7dfd86f2a2c3be846b402111ba7b86a8204e..f79d5b2057071560cd4157e73418217581de6148 100644 (file)
@@ -7,6 +7,7 @@
 #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"
@@ -500,6 +501,11 @@ int route_configure(
         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),
@@ -688,7 +694,7 @@ int route_configure(
         sd_event_source_unref(route->expire);
         route->expire = TAKE_PTR(expire);
 
-        return 0;
+        return 1;
 }
 
 int network_add_ipv4ll_route(Network *network) {
index f6253215ed8869885663811917d636312d2a8c66..4715850a9b4f247fe94436b30d61de8378e6e332 100644 (file)
@@ -46,8 +46,10 @@ void routing_policy_rule_free(RoutingPolicyRule *rule) {
         }
 
         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);
@@ -71,8 +73,12 @@ static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct
                 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);
@@ -110,14 +116,26 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
                 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;
@@ -156,50 +174,20 @@ static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const Ro
 
 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;
@@ -227,37 +215,23 @@ int routing_policy_rule_make_local(Manager *m, RoutingPolicyRule *rule) {
         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;
         }
@@ -267,19 +241,22 @@ static int routing_policy_rule_add_internal(Manager *m,
                 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)
@@ -292,47 +269,16 @@ static int routing_policy_rule_add_internal(Manager *m,
         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) {
@@ -474,7 +420,7 @@ static int routing_policy_rule_handler(sd_netlink *rtnl, sd_netlink_message *m,
         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;
 
@@ -484,6 +430,11 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netl
         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");
@@ -588,12 +539,11 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netl
 
         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) {
index 4ee0b5489ef7002c5ae3fa90bb5de9c53898e8ad..512af3dfdd41059537cdccd8b19093c9f13f9dc6 100644 (file)
@@ -57,18 +57,11 @@ void routing_policy_rule_free(RoutingPolicyRule *rule);
 
 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);
index 5fd30f3df889c987f5306ca06216573c8bfe4b86..c4fc56b019f9afd363ee9efc819dc7ce856a7909 100644 (file)
@@ -5,6 +5,7 @@
 #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"
index dfb41f801b359d1b65063cc57e06b9c6ef6967a6..07ca127654ff8ac36f3b399f278e52e821885694 100644 (file)
@@ -174,7 +174,7 @@ static void test_config_parse_address_one(const char *rvalue, int family, unsign
         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);
@@ -215,6 +215,27 @@ static void test_config_parse_address(void) {
         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();
@@ -223,6 +244,8 @@ int main(int argc, char **argv) {
         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;
 }
index f84bb3979628a8bdb4560844c7b78c5ffcef8d9c..0462b4641307f1fb84a5813c7db4300d50579013 100644 (file)
@@ -370,7 +370,7 @@ static int mount_legacy_cgns_supported(
                         if (streq(controller, tok))
                                 break;
 
-                        target = prefix_root("/sys/fs/cgroup/", tok);
+                        target = path_join("/sys/fs/cgroup/", tok);
                         if (!target)
                                 return log_oom();
 
@@ -451,7 +451,7 @@ static int mount_legacy_cgns_unsupported(
                 if (!controller)
                         break;
 
-                origin = prefix_root("/sys/fs/cgroup/", controller);
+                origin = path_join("/sys/fs/cgroup/", controller);
                 if (!origin)
                         return log_oom();
 
@@ -468,7 +468,7 @@ static int mount_legacy_cgns_unsupported(
                 else {
                         _cleanup_free_ char *target = NULL;
 
-                        target = prefix_root(dest, origin);
+                        target = path_join(dest, origin);
                         if (!target)
                                 return log_oom();
 
index 5a1bce4abc400fc273b82bb014a034fcb947e7d1..82b7068fcd1c554a3341a7a453730f4e1b7f61f2 100644 (file)
@@ -97,7 +97,7 @@ static char *resolve_source_path(const char *dest, const char *source) {
                 return NULL;
 
         if (source[0] == '+')
-                return prefix_root(dest, source + 1);
+                return path_join(dest, source + 1);
 
         return strdup(source);
 }
@@ -145,7 +145,7 @@ int custom_mount_prepare_all(const char *dest, CustomMount *l, size_t n) {
                                         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();
 
@@ -433,11 +433,11 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) {
         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();
 
@@ -1190,7 +1190,9 @@ int setup_pivot_root(const char *directory, const char *pivot_root_new, const ch
          * 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);
@@ -1204,7 +1206,11 @@ int setup_pivot_root(const char *directory, const char *pivot_root_new, const ch
                 }
 
                 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)
index 25184e11e9e30fc76d16075451475caaa5844265..a563c026dd6edb9646c876002c94f21b27d22c0c 100644 (file)
@@ -1895,11 +1895,11 @@ static int copy_devnodes(const char *dest) {
                 _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();
 
@@ -1934,7 +1934,7 @@ static int copy_devnodes(const char *dest) {
                         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();
 
@@ -1945,11 +1945,11 @@ static int copy_devnodes(const char *dest) {
                         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();
 
@@ -1972,7 +1972,7 @@ static int make_extra_nodes(const char *dest) {
                 _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();
 
@@ -2037,32 +2037,41 @@ static int setup_pts(const char *dest) {
         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) {
@@ -2308,7 +2317,11 @@ static int drop_capabilities(uid_t uid) {
 
                 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,
@@ -2317,6 +2330,13 @@ static int drop_capabilities(uid_t uid) {
                         .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);
 }
 
@@ -2576,7 +2596,7 @@ static int determine_names(void) {
                  * 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();
         }
@@ -2775,6 +2795,7 @@ static int inner_child(
                 bool secondary,
                 int kmsg_socket,
                 int rtnl_socket,
+                int master_pty_socket,
                 FDSet *fds) {
 
         _cleanup_free_ char *home = NULL;
@@ -2908,6 +2929,29 @@ static int inner_child(
                 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;
@@ -3129,7 +3173,6 @@ static int setup_sd_notify_child(void) {
 static int outer_child(
                 Barrier *barrier,
                 const char *directory,
-                const char *console,
                 DissectedImage *dissected_image,
                 bool secondary,
                 int pid_socket,
@@ -3138,6 +3181,7 @@ static int outer_child(
                 int kmsg_socket,
                 int rtnl_socket,
                 int uid_shift_socket,
+                int master_pty_socket,
                 int unified_cgroup_hierarchy_socket,
                 FDSet *fds,
                 int netns_fd) {
@@ -3157,6 +3201,7 @@ static int outer_child(
         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.");
@@ -3164,25 +3209,6 @@ static int outer_child(
         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;
@@ -3337,10 +3363,6 @@ static int outer_child(
         if (r < 0)
                 return r;
 
-        r = setup_dev_console(directory, console);
-        if (r < 0)
-                return r;
-
         r = setup_keyring();
         if (r < 0)
                 return r;
@@ -3415,7 +3437,7 @@ static int outer_child(
                                 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);
 
@@ -3438,11 +3460,12 @@ static int outer_child(
 
         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);
@@ -3968,7 +3991,7 @@ static int load_settings(void) {
         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();
 
@@ -4042,14 +4065,13 @@ static int load_oci_bundle(void) {
         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,
@@ -4065,9 +4087,10 @@ static int run_container(int master,
                 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;
@@ -4115,6 +4138,9 @@ static int run_container(int master,
         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");
@@ -4158,13 +4184,12 @@ static int run_container(int master,
                 /* 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]);
 
@@ -4173,7 +4198,6 @@ static int run_container(int master,
 
                 r = outer_child(&barrier,
                                 arg_directory,
-                                console,
                                 dissected_image,
                                 secondary,
                                 pid_socket_pair[1],
@@ -4182,6 +4206,7 @@ static int run_container(int master,
                                 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);
@@ -4200,6 +4225,7 @@ static int run_container(int master,
         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]);
 
@@ -4474,17 +4500,40 @@ static int run_container(int master,
 
         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);
@@ -4614,7 +4663,6 @@ static int initialize_rlimits(void) {
 }
 
 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;
@@ -4929,31 +4977,6 @@ static int run(int argc, char *argv[]) {
         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);
@@ -4966,13 +4989,11 @@ static int run(int argc, char *argv[]) {
         }
 
         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;
index ce86f96e13dd96d622b25e67f500983c3df6c329..9e7178ac45897ed95da7aea93d3f56694d1c2b82 100644 (file)
@@ -129,7 +129,8 @@ static int maybe_resize_slave_device(const char *mountpath, dev_t main_devno) {
 
 #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);
index 7d39d7b5f38e6bb3789665715e43a622bb67dac1..4956ae7310a5843e00286b5efa21c33285e61b0f 100644 (file)
@@ -325,7 +325,7 @@ static int extract_now(
                                 return -ENOMEM;
                         fd = -1;
 
-                        m->source = strjoin(resolved, "/", de->d_name);
+                        m->source = path_join(resolved, de->d_name);
                         if (!m->source)
                                 return -ENOMEM;
 
@@ -653,10 +653,10 @@ static int portable_changes_add_with_prefix(
                 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);
@@ -691,7 +691,7 @@ static int install_chroot_dropin(
         assert(m);
         assert(dropin_dir);
 
-        dropin = strjoin(dropin_dir, "/20-portable.conf");
+        dropin = path_join(dropin_dir, "20-portable.conf");
         if (!dropin)
                 return -ENOMEM;
 
@@ -778,7 +778,7 @@ static int install_profile_dropin(
                 return 0;
         }
 
-        dropin = strjoin(dropin_dir, "/10-profile.conf");
+        dropin = path_join(dropin_dir, "10-profile.conf");
         if (!dropin)
                 return -ENOMEM;
 
@@ -847,7 +847,7 @@ static int attach_unit_file(
         } 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;
index 5464360f894cc9eb37c3926dcbcc000b85c77c50..c74ec429627b721a6b8d85d857437d5eea2cbf2e 100644 (file)
@@ -134,10 +134,8 @@ static int run(int argc, char *argv[]) {
 
         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);
 
index f8f6c2d3ec12573c2a10d399ed42644009721323..1a8a9a6d623911f3ff6d354552aad214bc9f15b3 100644 (file)
@@ -7,6 +7,7 @@
 #include <sys/un.h>
 
 #include "alloc-util.h"
+#include "main-func.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "log.h"
@@ -34,66 +35,49 @@ static int send_on_socket(int fd, const char *socket_name, const void *packet, s
         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);
index d1f65056d0b2c92a0cdabc0a1329d62d82a0e3a9..8cd14711dff7b5aa23b15d57d16bc14c7472e3e1 100644 (file)
@@ -82,7 +82,7 @@ static int parse_search_domain(const char *string) {
         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)
index 6c5b58994423c73e59fce0e1b024a63b2d3e57a1..94ec8be896ee4eb411d1c677e06f021f08dde24e 100644 (file)
@@ -1229,7 +1229,7 @@ static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message
                 return r;
 
         for (;;) {
-                char *pretty = NULL;
+                _cleanup_free_ char *pretty = NULL;
 
                 r = read_dns_server_one(m, false, &pretty);
                 if (r < 0)
@@ -1240,7 +1240,7 @@ static int map_link_dns_servers(sd_bus *bus, const char *member, sd_bus_message
                 if (isempty(pretty))
                         continue;
 
-                r = strv_consume(l, pretty);
+                r = strv_consume(l, TAKE_PTR(pretty));
                 if (r < 0)
                         return r;
         }
@@ -1306,7 +1306,7 @@ static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m,
                 return r;
 
         for (;;) {
-                char *pretty = NULL;
+                _cleanup_free_ char *pretty = NULL;
 
                 r = read_domain_one(m, false, &pretty);
                 if (r < 0)
@@ -1317,7 +1317,7 @@ static int map_link_domains(sd_bus *bus, const char *member, sd_bus_message *m,
                 if (isempty(pretty))
                         continue;
 
-                r = strv_consume(l, pretty);
+                r = strv_consume(l, TAKE_PTR(pretty));
                 if (r < 0)
                         return r;
         }
@@ -1536,7 +1536,7 @@ static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_messag
                 return r;
 
         for (;;) {
-                char *pretty = NULL;
+                _cleanup_free_ char *pretty = NULL;
 
                 r = read_dns_server_one(m, true, &pretty);
                 if (r < 0)
@@ -1547,7 +1547,7 @@ static int map_global_dns_servers(sd_bus *bus, const char *member, sd_bus_messag
                 if (isempty(pretty))
                         continue;
 
-                r = strv_consume(l, pretty);
+                r = strv_consume(l, TAKE_PTR(pretty));
                 if (r < 0)
                         return r;
         }
@@ -1580,7 +1580,7 @@ static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m
                 return r;
 
         for (;;) {
-                char *pretty = NULL;
+                _cleanup_free_ char *pretty = NULL;
 
                 r = read_domain_one(m, true, &pretty);
                 if (r < 0)
@@ -1591,7 +1591,7 @@ static int map_global_domains(sd_bus *bus, const char *member, sd_bus_message *m
                 if (isempty(pretty))
                         continue;
 
-                r = strv_consume(l, pretty);
+                r = strv_consume(l, TAKE_PTR(pretty));
                 if (r < 0)
                         return r;
         }
index 79e388f8a2d7cefec1f6bbb62d9c81d8fa9ffaa7..0c8d2a679ca5964f0ea4e6a2427bd30a9c30a637 100644 (file)
@@ -119,7 +119,7 @@ int manager_parse_search_domains_and_warn(Manager *m, const char *string) {
         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)
@@ -308,7 +308,7 @@ int config_parse_dnssd_txt(const char *unit, const char *filename, unsigned line
                 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)
@@ -394,7 +394,7 @@ int manager_parse_config_file(Manager *m) {
 
 #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
index 4cbb9723e21f69a1247215327cc114a766b6eb69..6ba26a24b2c3f2cc02d6f66851831e0a7844f831 100644 (file)
@@ -7,6 +7,7 @@
 #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"
@@ -555,7 +556,7 @@ int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const u
 
 #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;
index cd6e42e5945f100a0d320bbc2832188e62cf440f..78e5953b3001095e74077678ace923e6e639fcde 100644 (file)
@@ -82,10 +82,6 @@ int dns_server_new(
 
         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 */
@@ -423,7 +419,7 @@ DnsServerFeatureLevel dns_server_possible_feature_level(DnsServer *s) {
                         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 */
index 3c4627bca5a3d0d0c86e867667ca63717dd6d8b6..2c0457243bc0d7cefe9e08841fcf612216ca1d08 100644 (file)
@@ -35,11 +35,9 @@ typedef enum DnsServerFeatureLevel {
 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;
index 780051b38a419ae5e6e25cff5adf4b7b7de2fb52..1013f6e45e6a0dc0cdff39ceae9ef6e70462024f 100644 (file)
@@ -16,10 +16,8 @@ typedef enum DnsStreamType {
 
 #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
 
index 2ef02348064bf8cf9488a12d9b8fa2373f330869..deb46fae7a19a6262b4b22c41dcb31f4304acc62 100644 (file)
 #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
index 3e4cc331d9a5585e37107c5dd2c37a33f2a2c116..89e48403f090223d128b2b1a77b41322ca1c1548 100644 (file)
@@ -216,7 +216,7 @@ static int dns_trust_anchor_load_positive(DnsTrustAnchor *d, const char *path, u
         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);
 
@@ -386,7 +386,7 @@ static int dns_trust_anchor_load_negative(DnsTrustAnchor *d, const char *path, u
         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);
 
index 4da5121e6256a18e1c0bee70415bf51528626244..06d635fcc444f77302398131a50d660846269965 100644 (file)
@@ -9,6 +9,11 @@
 #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) {
@@ -37,11 +42,11 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
                 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;
 
@@ -54,6 +59,9 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
                 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);
@@ -187,19 +195,31 @@ ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
         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);
 }
index 41c89f273751eb0c366af65b246898ae747fdfd9..af52f04fdf28bb1c3c4d25462b29f41e0a1b5ed2 100644 (file)
@@ -8,8 +8,11 @@
 #include <gnutls/gnutls.h>
 #include <stdbool.h>
 
-struct DnsTlsServerData {
+struct DnsTlsManagerData {
         gnutls_certificate_credentials_t cert_cred;
+};
+
+struct DnsTlsServerData {
         gnutls_datum_t session_data;
 };
 
index f269e4d6487fd6dd0621c3aa41767bc52459404f..85e202ff741d46a09911c1247577ef980f6a2952 100644 (file)
@@ -20,12 +20,12 @@ static int dnstls_flush_write_buffer(DnsStream *stream) {
         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)
@@ -33,12 +33,14 @@ static int dnstls_flush_write_buffer(DnsStream *stream) {
 
                         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;
                         }
                 }
         }
@@ -52,6 +54,7 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
         int error, r;
 
         assert(stream);
+        assert(stream->manager);
         assert(server);
 
         rb = BIO_new_socket(stream->fd, 0);
@@ -63,8 +66,9 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
                 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;
 
@@ -72,6 +76,17 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
         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) {
@@ -86,12 +101,13 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) {
         }
 
         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;
 }
@@ -332,22 +348,36 @@ ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) {
         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);
 }
index f0dccf32e65fa363c67d0cf78d894c6b1e8e9713..8100d2ba6b027bcf7e859bdca2c79bc548553591 100644 (file)
@@ -8,8 +8,11 @@
 #include <openssl/ssl.h>
 #include <stdbool.h>
 
-struct DnsTlsServerData {
+struct DnsTlsManagerData {
         SSL_CTX *ctx;
+};
+
+struct DnsTlsServerData {
         SSL_SESSION *session;
 };
 
@@ -18,4 +21,5 @@ struct DnsTlsStreamData {
         bool shutdown;
         SSL *ssl;
         BUF_MEM *write_buffer;
+        size_t buffer_offset;
 };
index fdd85eece6a8903078db9d7f7ad2c3ad6c3f1c63..1b9121171e98d55f3fa6c04786170857943e5097 100644 (file)
@@ -1,10 +1,9 @@
 /* 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;
 
@@ -18,6 +17,7 @@ typedef struct DnsTlsStreamData DnsTlsStreamData;
 
 #include "resolved-dns-stream.h"
 #include "resolved-dns-transaction.h"
+#include "resolved-manager.h"
 
 #define DNSTLS_STREAM_CLOSED 1
 
@@ -28,5 +28,9 @@ int dnstls_stream_shutdown(DnsStream *stream, int error);
 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 */
index dd8b5a574b003f5ae92902689087f4c790a75ede..96ebb4d23d360020a39eeb98d6f2a7a619d4c1a6 100644 (file)
@@ -384,7 +384,7 @@ void link_set_dns_over_tls_mode(Link *l, DnsOverTlsMode mode) {
 
 #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
 
index 5f63a21e01d1b5e6501f391dc88ab67a72ff3a31..02153b929fb9e19fa78d1dbb47616d74a1333ebc 100644 (file)
@@ -597,6 +597,12 @@ int manager_new(Manager **ret) {
         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;
@@ -677,6 +683,10 @@ Manager *manager_free(Manager *m) {
         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);
 
index 72171f8c975b1dc8cb9292d997eab2011c821aab..0f7d8ee5ee1b13bf95b43a689c38e47b122e10d1 100644 (file)
@@ -18,6 +18,7 @@ typedef struct Manager Manager;
 #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
@@ -39,6 +40,10 @@ struct Manager {
         bool enable_cache;
         DnsStubListenerMode dns_stub_listener_mode;
 
+#if ENABLE_DNS_OVER_TLS
+        DnsTlsManagerData dnstls_data;
+#endif
+
         /* Network */
         Hashmap *links;
 
index 05c1552a8b7ff74bb36aa8b5c7d9e9c413133c61..c11b7f57ffa154f334f3ecf05df87a199a51819c 100644 (file)
@@ -1266,8 +1266,8 @@ static int start_transient_service(
                         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));
index 6c0a36990291832e7c2c36953515c5ecb91448c2..00c41a0382082bc585e14698a9a61636846669b7 100644 (file)
@@ -63,13 +63,16 @@ static int lookup_key(const char *keyname, key_serial_t *ret) {
 }
 
 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;
@@ -77,33 +80,28 @@ static int retrieve_key(key_serial_t serial, char ***ret) {
                 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;
@@ -131,7 +129,6 @@ static int add_to_keyring(const char *keyname, AskPasswordFlags flags, char **pa
                 return r;
 
         serial = add_key("user", keyname, p, n, KEY_SPEC_USER_KEYRING);
-        explicit_bzero_safe(p, n);
         if (serial == -1)
                 return -errno;
 
index 40bc9645bed3094e5658e74301213bd5deff84f9..93f8db3f34cb4113d2c6aa30df45b66b1433d003 100644 (file)
@@ -94,6 +94,25 @@ int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size) {
         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;
index c21eb2f72a385c451c80725428a18e222a8255b5..a21589eb1f63a12c46b6946da424547b11f56379 100644 (file)
@@ -31,6 +31,7 @@ BPFProgram *bpf_program_ref(BPFProgram *p);
 
 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);
index fb86391975373949f986e2ff7091dc4a4fea284c..6a7358e3015f4be19aa4cb221a914cf70f28ab31 100644 (file)
@@ -9,6 +9,7 @@
 #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"
@@ -244,19 +245,21 @@ static int bus_append_parse_size(sd_bus_message *m, const char *field, const cha
 }
 
 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;
@@ -270,11 +273,36 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
                         }
                         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;
@@ -282,13 +310,27 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
                 }
         } 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");
 
@@ -296,21 +338,21 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
         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);
 
@@ -322,7 +364,7 @@ static int bus_append_exec_command(sd_bus_message *m, const char *field, const c
                 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);
 
@@ -398,13 +440,13 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
 
         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)
@@ -716,6 +758,18 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
                 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;
 }
 
@@ -765,7 +819,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                        "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"))
 
@@ -827,7 +881,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
 
         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")) {
 
@@ -1007,6 +1061,34 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                 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;
@@ -1043,7 +1125,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                 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)
@@ -1130,7 +1212,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                         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)
@@ -1143,7 +1225,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                         }
 
                         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)
@@ -1156,7 +1238,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                                 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");
 
@@ -1215,7 +1297,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
                         _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)
@@ -1351,8 +1433,8 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
 
         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")) {
@@ -1364,7 +1446,7 @@ static int bus_append_service_property(sd_bus_message *m, const char *field, con
                         _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)
@@ -1484,7 +1566,7 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons
 
         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"))
 
@@ -1602,7 +1684,7 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const
         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)
index c2440271feb13226dd7cf1e03b376cac89872937..b3b05872e9590f316cb11001497321d758901136 100644 (file)
@@ -31,6 +31,7 @@
 #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"
@@ -1552,7 +1553,7 @@ int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id,
         if (!external_label)
                 return -ENOMEM;
 
-        p = strjoin(prefix, "/", sender_label, "/", external_label);
+        p = path_join(prefix, sender_label, external_label);
         if (!p)
                 return -ENOMEM;
 
@@ -1767,3 +1768,13 @@ int bus_reply_pair_array(sd_bus_message *m, char **l) {
 
         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,
+};
index 59bfdb239813ab44dfe90a7879772d520de1f801..3216b0c37a1bf0da613923b33ae8973caba3ca1f 100644 (file)
@@ -179,3 +179,5 @@ static inline int bus_open_system_watch_bind(sd_bus **ret) {
 }
 
 int bus_reply_pair_array(sd_bus_message *m, char **l);
+
+extern const struct hash_ops bus_message_hash_ops;
diff --git a/src/shared/bus-wait-for-units.c b/src/shared/bus-wait-for-units.c
new file mode 100644 (file)
index 0000000..d07f491
--- /dev/null
@@ -0,0 +1,434 @@
+/* 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;
+}
diff --git a/src/shared/bus-wait-for-units.h b/src/shared/bus-wait-for-units.h
new file mode 100644 (file)
index 0000000..a20f3d8
--- /dev/null
@@ -0,0 +1,35 @@
+/* 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);
index e6fdcfa277deddfbfca7c1001b57058b72ad3ab4..465e38c4fbf2a5e3f6d52836e950e7c590710992 100644 (file)
@@ -148,7 +148,7 @@ int show_cgroup_by_path(
         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;
index 2d521bc8c69aa0c277e7c02f1cffce7e7bdc2f5c..e5e6c6cc13082632863db14c989afe540ce08d2b 100644 (file)
@@ -114,7 +114,7 @@ static int condition_test_kernel_command_line(Condition *c) {
                 _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)
@@ -207,6 +207,7 @@ static int condition_test_kernel_version(Condition *c) {
         OrderOperator order;
         struct utsname u;
         const char *p;
+        bool first = true;
 
         assert(c);
         assert(c->parameter);
@@ -215,13 +216,49 @@ static int condition_test_kernel_version(Condition *c) {
         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) {
@@ -684,31 +721,31 @@ static int condition_test_null(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;
@@ -747,20 +784,23 @@ bool condition_test_list(Condition *first, const char *(*to_string)(ConditionTyp
                 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));
                 }
 
index 2df09edaf57cb733dec20e0000cce20c88429435..c27c499edaa2b44963b16bf21ed7e15d0e2097c6 100644 (file)
@@ -322,7 +322,8 @@ int config_parse(const char *unit,
                         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;
@@ -757,7 +758,7 @@ int config_parse_strv(
         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)
index b0036c7f619bea6736e6496265413e25e392ec1e..219314ef581dc294ff16ea327598e504db2fd1cf 100644 (file)
@@ -7,12 +7,20 @@
 
 #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;
@@ -147,7 +155,7 @@ int parse_cpu_set_full(
                 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)
@@ -287,3 +295,88 @@ int cpu_set_from_dbus(const uint8_t *bits, size_t size, CPUSet *set) {
         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);
index fd6a15f4468fa4becce701bfa8b4ec584e8a22bf..27812dfd5923606675a3b73bb0f295053ae9bce3 100644 (file)
@@ -4,6 +4,7 @@
 #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 {
@@ -48,3 +49,30 @@ int cpu_set_to_dbus(const CPUSet *set, uint8_t **ret, size_t *allocated);
 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_;
index 12e80e4b98f128cba183eea36edc687778990a57..071ff7b30cff002e53f80ebc8ab2d3c4735029cc 100644 (file)
@@ -36,7 +36,7 @@ int dev_setup(const char *prefix, uid_t uid, gid_t gid) {
                 }
 
                 if (prefix) {
-                        link_name = prefix_root(prefix, k);
+                        link_name = path_join(prefix, k);
                         if (!link_name)
                                 return -ENOMEM;
 
@@ -91,7 +91,7 @@ int make_inaccessible_nodes(const char *root, uid_t uid, gid_t gid) {
         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();
 
index 95ee7efa92af6fa453f939d8c907b812ca8c1006..030e12e4ee2e688aa07167838348b26973491e32 100644 (file)
@@ -19,6 +19,7 @@
 #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"
@@ -994,7 +995,7 @@ static int make_dm_name_and_node(const void *original_node, const char *suffix,
         if (!filename_is_valid(name))
                 return -EINVAL;
 
-        node = strjoin(crypt_get_dir(), "/", name);
+        node = path_join(crypt_get_dir(), name);
         if (!node)
                 return -ENOMEM;
 
@@ -1225,40 +1226,6 @@ int dissected_image_decrypt_interactively(
         }
 }
 
-#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
@@ -1278,7 +1245,7 @@ int decrypted_image_relinquish(DecryptedImage *d) {
                 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);
 
diff --git a/src/shared/dm-util.c b/src/shared/dm-util.c
new file mode 100644 (file)
index 0000000..f73bf93
--- /dev/null
@@ -0,0 +1,42 @@
+#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;
+}
diff --git a/src/shared/dm-util.h b/src/shared/dm-util.h
new file mode 100644 (file)
index 0000000..6c78bfe
--- /dev/null
@@ -0,0 +1,4 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#pragma once
+
+int dm_deferred_remove(const char *name);
index 1346da9323d8585173ee5744ab2b05e5aaceae70..fad129794d2fa52088c1329edc3bb9c1820f3aa3 100644 (file)
@@ -10,6 +10,7 @@
 
 #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);
similarity index 66%
rename from src/udev/net/ethtool-util.c
rename to src/shared/ethtool-util.c
index c94977e7be50704ce78516d8357278bc496dcfe6..d823931c76096b06a6935be54df2c94ac7901859 100644 (file)
@@ -7,7 +7,7 @@
 
 #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"
@@ -57,72 +57,88 @@ static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = {
 };
 
 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;
 
@@ -140,9 +156,9 @@ int ethtool_get_driver(int *fd, const char *ifname, char **ret) {
         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);
@@ -159,6 +175,49 @@ int ethtool_get_driver(int *fd, const char *ifname, char **ret) {
         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
@@ -173,9 +232,9 @@ int ethtool_set_speed(int *fd, const char *ifname, unsigned speed, Duplex duplex
                 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);
@@ -231,9 +290,9 @@ int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) {
                 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);
@@ -368,16 +427,16 @@ int ethtool_set_features(int *fd, const char *ifname, int *features) {
         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;
@@ -389,7 +448,7 @@ int ethtool_set_features(int *fd, const char *ifname, int *features) {
 
                         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;
                         }
 
@@ -409,7 +468,7 @@ int ethtool_set_features(int *fd, const char *ifname, int *features) {
 
         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;
 }
@@ -453,11 +512,13 @@ static int get_glinksettings(int fd, struct ifreq *ifr, struct ethtool_link_uset
         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);
@@ -486,23 +547,24 @@ static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **u
         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;
 
@@ -578,20 +640,27 @@ static int set_sset(int fd, struct ifreq *ifr, const struct ethtool_link_usettin
  * 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);
@@ -600,26 +669,26 @@ int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *l
         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)
@@ -627,59 +696,11 @@ int ethtool_set_glinksettings(int *fd, const char *ifname, struct link_config *l
         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
@@ -692,9 +713,9 @@ int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels)
         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);
@@ -734,6 +755,54 @@ int ethtool_set_channels(int *fd, const char *ifname, netdev_channels *channels)
         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,
@@ -744,7 +813,7 @@ int config_parse_advertise(const char *unit,
                            const char *rvalue,
                            void *data,
                            void *userdata) {
-        link_config *config = data;
+        uint32_t *advertise = data;
         const char *p;
         int r;
 
@@ -756,7 +825,7 @@ int config_parse_advertise(const char *unit,
 
         if (isempty(rvalue)) {
                 /* Empty string resets the value. */
-                zero(config->advertise);
+                memzero(advertise, sizeof(uint32_t) * N_ADVERTISE);
                 return 0;
         }
 
@@ -782,7 +851,7 @@ int config_parse_advertise(const char *unit,
                         continue;
                 }
 
-                config->advertise[mode / 32] |= 1UL << (mode % 32);
+                advertise[mode / 32] |= 1UL << (mode % 32);
         }
 
         return 0;
similarity index 87%
rename from src/udev/net/ethtool-util.h
rename to src/shared/ethtool-util.h
index 7ca703d22cc74831dbadb3affef31fec486db7b3..8b32b243f3ae5993e17be3fe61a3d523f651a804 100644 (file)
@@ -6,7 +6,7 @@
 
 #include "conf-parser.h"
 
-struct link_config;
+#define N_ADVERTISE 3
 
 /* we can't use DUPLEX_ prefix, as it
  * clashes with <linux/ethtool.h> */
@@ -79,13 +79,16 @@ typedef struct netdev_channels {
         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_;
index add7fe4c141f45d82efc25a700cb0eb5792f9cd8..8fb936dcce45ac3effd6f7cadd13040a15410c11 100644 (file)
@@ -22,6 +22,7 @@
 #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"
@@ -365,8 +366,81 @@ static int gather_environment_consume(int fd, void *arg) {
         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;
+}
index 5b75a40229bf9a64593354e6e1372fdad51215b6..9fe9012516cbb752e18123dbc5b7222cf14358f1 100644 (file)
@@ -20,6 +20,15 @@ typedef enum {
         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,
@@ -29,4 +38,10 @@ int execute_directories(
                 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);
index 26b3060d9b939198a3240954ca7af1ae7a6b5df2..58ebc3ca4d6558cf1d28ba544f9e9bcf81c43da0 100644 (file)
@@ -157,6 +157,9 @@ const char* exit_status_to_string(int status, ExitStatusLevel level) {
                 case EXIT_CONFIGURATION_DIRECTORY:
                         return "CONFIGURATION_DIRECTORY";
 
+                case EXIT_NUMA_POLICY:
+                        return "NUMA_POLICY";
+
                 case EXIT_EXCEPTION:
                         return "EXCEPTION";
                 }
index 510eb319cf6b2c06ce93d5074e724a5f82624e0f..5637e6aa04ddd9df731caf843baebc5b29667539 100644 (file)
@@ -69,6 +69,7 @@ enum {
         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 */
 };
index cba52fb41914c90d1018c0ac21014cc3a7a57521..93bdd8c7f1241ebba44a4052fd45c47f3b98d6a2 100644 (file)
@@ -90,6 +90,7 @@ int fw_add_masquerade(
                 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;
@@ -133,15 +134,15 @@ int fw_add_masquerade(
         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;
 
@@ -169,6 +170,7 @@ int fw_add_local_dnat(
                 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;
@@ -266,20 +268,20 @@ int fw_add_local_dnat(
         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;
                 }
 
@@ -287,7 +289,7 @@ int fw_add_local_dnat(
                 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;
                         }
@@ -305,11 +307,11 @@ int fw_add_local_dnat(
                                 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;
                         }
 
@@ -317,14 +319,14 @@ int fw_add_local_dnat(
                         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;
                 }
@@ -336,7 +338,7 @@ int fw_add_local_dnat(
                                 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;
                         }
index d6612a76d35f39605fe20a5bb223ed329790b7e5..54ca1972bde65cf0d1bfc9b382b795a655211691 100644 (file)
@@ -15,6 +15,7 @@
 #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"
@@ -235,6 +236,7 @@ static size_t table_data_size(TableDataType type, const void *data) {
         case TABLE_SIZE:
         case TABLE_INT64:
         case TABLE_UINT64:
+        case TABLE_BPS:
                 return sizeof(uint64_t);
 
         case TABLE_INT32:
@@ -723,6 +725,7 @@ int table_add_many_internal(Table *t, TableDataType first_type, ...) {
                         break;
 
                 case TABLE_SIZE:
+                case TABLE_BPS:
                         buffer.size = va_arg(ap, uint64_t);
                         data = &buffer.size;
                         break;
@@ -885,6 +888,7 @@ static int cell_data_compare(TableData *a, size_t index_a, TableData *b, size_t
                         return CMP(a->timespan, b->timespan);
 
                 case TABLE_SIZE:
+                case TABLE_BPS:
                         return CMP(a->size, b->size);
 
                 case TABLE_INT:
@@ -1023,6 +1027,24 @@ static const char *table_data_format(TableData *d) {
                 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;
 
@@ -1622,6 +1644,7 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) {
                 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);
 
index ec2bbba29272e15def977bedb04d99ad72a20f6b..ada59f34238d2827c5f85a452458ab5a30c63e2a 100644 (file)
@@ -15,6 +15,7 @@ typedef enum TableDataType {
         TABLE_TIMESTAMP,
         TABLE_TIMESPAN,
         TABLE_SIZE,
+        TABLE_BPS,
         TABLE_INT,
         TABLE_INT32,
         TABLE_INT64,
diff --git a/src/shared/fsck-util.h b/src/shared/fsck-util.h
new file mode 100644 (file)
index 0000000..78ec18d
--- /dev/null
@@ -0,0 +1,14 @@
+/* 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,
+};
index 0a5413ce04e4d64f6cad4fbe79d802d15ca62c50..5b571681f28469fb518ac871b1ab552824d34a27 100644 (file)
@@ -31,7 +31,7 @@ int generator_open_unit_file(
         FILE *f;
         int r;
 
-        unit = strjoina(dest, "/", name);
+        unit = prefix_roota(dest, name);
 
         r = fopen_unlocked(unit, "wxe", &f);
         if (r < 0) {
@@ -156,15 +156,15 @@ int generator_write_fsck_deps(
         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);
@@ -172,7 +172,15 @@ int generator_write_fsck_deps(
                                 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");
@@ -181,9 +189,9 @@ int generator_write_fsck_deps(
                 }
 
                 fprintf(f,
-                        "Requires=%1$s\n"
-                        "After=%1$s\n",
-                        fsck);
+                        "%1$s=%2$s\n"
+                        "After=%2$s\n",
+                        dep, fsck);
         }
 
         return 0;
@@ -319,7 +327,7 @@ int generator_hook_up_mkswap(
                 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);
@@ -394,7 +402,7 @@ int generator_hook_up_mkfs(
                 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);
@@ -466,7 +474,7 @@ int generator_hook_up_growfs(
                 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");
index 5391ac702beac7dcc5873a06840f4f9051dba5c1..105412bc8fd799883196735e60ef96a561664404 100644 (file)
@@ -794,7 +794,7 @@ static int find_symlinks_fd(
                         if (!path_is_absolute(dest)) {
                                 char *x;
 
-                                x = prefix_root(root_dir, dest);
+                                x = path_join(root_dir, dest);
                                 if (!x)
                                         return -ENOMEM;
 
@@ -1377,7 +1377,7 @@ static int unit_file_load_or_readlink(
 
                 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);
@@ -1424,7 +1424,7 @@ static int unit_file_search(
         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;
 
@@ -1447,7 +1447,7 @@ static int unit_file_search(
                 STRV_FOREACH(p, paths->search_path) {
                         _cleanup_free_ char *path = NULL;
 
-                        path = strjoin(*p, "/", template);
+                        path = path_join(*p, template);
                         if (!path)
                                 return -ENOMEM;
 
@@ -1840,7 +1840,7 @@ static int install_info_symlink_link(
         if (r > 0)
                 return 0;
 
-        path = strjoin(config_path, "/", i->name);
+        path = path_join(config_path, i->name);
         if (!path)
                 return -ENOMEM;
 
@@ -2188,7 +2188,7 @@ int unit_file_link(
                 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;
 
@@ -2379,7 +2379,7 @@ int unit_file_revert(
                 STRV_FOREACH(j, fs) {
                         _cleanup_free_ char *t = NULL;
 
-                        t = strjoin(*i, "/", *j);
+                        t = path_join(*i, *j);
                         if (!t)
                                 return -ENOMEM;
 
index 8dc2c42ad15d335c39100de5ccef14198f4a50e2..44032548ad105bb312add30e0c95870202722e88 100644 (file)
@@ -4,6 +4,7 @@
 #include <unistd.h>
 
 #include "alloc-util.h"
+#include "errno-util.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "io-util.h"
@@ -22,37 +23,6 @@ enum {
         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) {
@@ -62,7 +32,7 @@ void journal_importer_cleanup(JournalImporter *imp) {
 
         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) {
@@ -453,7 +423,7 @@ int journal_importer_push_data(JournalImporter *imp, const char *data, size_t si
                                        "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;
@@ -466,7 +436,7 @@ void journal_importer_drop_iovw(JournalImporter *imp) {
 
         /* 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;
index 7914c0cf5fbc88176fc25c632150f74b1f73d18c..b2e3c817f5a3a3fee8e305fca201740ff131fbec 100644 (file)
@@ -6,8 +6,8 @@
 #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;
@@ -53,6 +45,9 @@ typedef struct JournalImporter {
         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);
index baea31fed93bada63202267c7e54236ffcb0aa42..f1bb50cfa2c8ce84df08ac44799b77a9fabe2596 100644 (file)
@@ -279,7 +279,8 @@ static int json_variant_new(JsonVariant **ret, JsonVariantType type, size_t spac
 
         assert_return(ret, -EINVAL);
 
-        v = malloc0(offsetof(JsonVariant, value) + space);
+        v = malloc0(MAX(sizeof(JsonVariant),
+                        offsetof(JsonVariant, value) + space));
         if (!v)
                 return -ENOMEM;
 
@@ -1664,7 +1665,8 @@ static int json_variant_copy(JsonVariant **nv, JsonVariant *v) {
         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;
 
@@ -1677,7 +1679,8 @@ static int json_variant_copy(JsonVariant **nv, JsonVariant *v) {
                 return 0;
         }
 
-        c = malloc0(offsetof(JsonVariant, value) + k);
+        c = malloc0(MAX(sizeof(JsonVariant),
+                        offsetof(JsonVariant, value) + k));
         if (!c)
                 return -ENOMEM;
 
@@ -3407,7 +3410,7 @@ int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFla
 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);
@@ -3421,11 +3424,7 @@ int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags
         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.");
 
index cc2886b0c0b141251d5616978c465b511d5e8f4f..d78082905a51fa74519a77f6f69f3006bfcb2aa8 100644 (file)
@@ -271,7 +271,7 @@ int json_log_internal(JsonVariant *variant, int level, int error, const char *fi
         ({                                                              \
                 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);                             \
         })
 
index 34f90486bb821ac6041b0735a95bce3bd9c40c20..46d554764d3edbf191c26196e3383d86b132fda8 100644 (file)
@@ -1,8 +1,8 @@
-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()
index e88459fdaae5a563a58247794c6c41918507d498..d8d9fb220cbc58beafc4b4d59d0e3c8c27706d77 100644 (file)
@@ -164,6 +164,16 @@ struct bpf_insn;
                .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)                         \
@@ -174,6 +184,16 @@ struct bpf_insn;
                .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,                      \
index 8faa188c358aeaab0e6e83c8ef7f21f3fc48b53b..b3aeec70f9a31280fddc02615f61b73957a6099f 100644 (file)
@@ -272,7 +272,7 @@ enum {
 #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 */
index f6c45a36c92e9dee10e8383b7e4cb35c54f3916b..acf346be41e42149cb20005a9be5b940fd18f0d8 100644 (file)
@@ -126,7 +126,7 @@ static inline void ethtool_cmd_speed_set(struct ethtool_cmd *ep,
 
 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
@@ -256,9 +256,17 @@ struct ethtool_tunable {
 #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
@@ -1436,6 +1444,13 @@ enum ethtool_link_mode_bit_indices {
        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,
@@ -1457,15 +1472,24 @@ enum ethtool_link_mode_bit_indices {
        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)     \
@@ -1573,12 +1597,13 @@ enum ethtool_link_mode_bit_indices {
 #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. */
@@ -1691,6 +1716,9 @@ static inline int ethtool_validate_duplex(__u8 duplex)
 #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
index 99efc0cbabffc2c50107741d3e1f13eab9d158fc..2d0380269053e7aa5b6c5228fb2dc8f42707556e 100644 (file)
@@ -17,8 +17,8 @@
 #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__)
index aa5bc5b4ed78e17e21ec5c82ff792454fe4e0cb0..21b325103404c4c8f543bb27751d9c11b33dbd02 100644 (file)
@@ -29,6 +29,7 @@
 #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"
@@ -375,8 +376,8 @@ static int output_short(
         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[] = {
@@ -390,6 +391,7 @@ static int output_short(
                 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),
         };
@@ -451,7 +453,8 @@ static int output_short(
                 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;
@@ -485,6 +488,34 @@ static int output_short(
                 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,
@@ -556,9 +587,11 @@ static int output_verbose(
                 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)
@@ -569,20 +602,28 @@ static int output_verbose(
                 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 {
@@ -1040,21 +1081,21 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
                 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(
index 55e5f08f91e44a70dbe0606a48fe9904a5f67475..ee086e35b47b342f63e789186234d1f757c5b635 100644 (file)
@@ -146,10 +146,7 @@ static int image_new(
         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;
 
index aa0423ccad73af5c70c941196ae65676d36031fc..bdd823bbd1565e42f434af8d3d76f5003b16c8c4 100644 (file)
@@ -29,6 +29,8 @@ shared_sources = files('''
         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
@@ -50,6 +52,8 @@ shared_sources = files('''
         dev-setup.h
         dissect-image.c
         dissect-image.h
+        dm-util.c
+        dm-util.h
         dns-domain.c
         dns-domain.h
         dropin.c
@@ -59,6 +63,8 @@ shared_sources = files('''
         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
@@ -70,6 +76,7 @@ shared_sources = files('''
         firewall-util.h
         format-table.c
         format-table.h
+        fsck-util.h
         fstab-util.c
         fstab-util.h
         generator.c
index 9129ad5f6b851e5b6ba5c07dec053b369f051834..de9e35b1282795e737f38353d7471d311b4251be 100644 (file)
@@ -505,7 +505,7 @@ int mount_option_mangle(
                 _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)
index 442fde7b2d2db732158a0e0974c7183b091f3b34..6494210ee9e12c8f115cdf7d1b3b76a70219a748 100644 (file)
@@ -303,7 +303,7 @@ static int acquire_transient_dir(
                 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
@@ -456,13 +456,11 @@ static int patch_root_prefix(char **p, const char *root_dir) {
         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;
 }
 
index a5d4a14344f8a160b26c1f487d786815d9b52bb9..b433cd24bca9071bf76ed6a07a8ec3df23dde993 100644 (file)
@@ -25,5 +25,6 @@ DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(dnssec_mode, DnssecMode, DNSSEC_YES);
 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);
index 5883342e653c2f054c11d277e71ff45bb05bd1e5..6ce250846609982b1a8d462f0411967b5ce0cca8 100644 (file)
@@ -42,6 +42,9 @@ enum DnsOverTlsMode {
          * 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
 };
index 6d31dfeff0a28e55522d281d6cb3c53b00f65f28..b2abaf4f526f92ef2d23ec456e870e6aa8defdf5 100644 (file)
@@ -42,7 +42,7 @@ int secure_bits_from_string(const char *s) {
         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)
index 4026655809ae44524ef6223fe6f4343f4b8641df..0efbd7c7bed68c6c0f3c4f3645f94db22e9a3fff 100644 (file)
@@ -178,6 +178,7 @@ int find_hibernate_location(char **device, char **type, size_t *size, size_t *us
 
         (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;
index 482d914d0785823dbcd76d986b2fe387042b2622..ea96f5b49dccc428de07869156fb8e6c78fad35e 100644 (file)
@@ -57,25 +57,29 @@ int udev_parse_config_full(
                  * 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) {
@@ -83,7 +87,8 @@ int udev_parse_config_full(
 
                 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;
         }
index dea8e4363f2d20e6ef735fefb37594a30a33c47b..54d48991e855a6e2af209fdca76ac368f3d95de2 100644 (file)
@@ -1002,7 +1002,6 @@ int varlink_wait(Varlink *v, usec_t timeout) {
         usec_t t;
 
         assert_return(v, -EINVAL);
-        assert_return(!v->server, -ENOTTY);
 
         if (v->state == VARLINK_DISCONNECTED)
                 return -ENOTCONN;
@@ -2053,12 +2052,14 @@ int varlink_server_add_connection(VarlinkServer *server, int fd, Varlink **ret)
 
         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)
index 7c5dcb02a245d7dba3cbebc9f51783dbb80dd344..c87b496736e6caf88ca9c5e0d54da612e27fde9b 100644 (file)
@@ -50,7 +50,7 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
         const Verb *verb;
         const char *name;
         unsigned i;
-        int left, r;
+        int left;
 
         assert(verbs);
         assert(verbs[0].dispatch);
@@ -109,12 +109,6 @@ int dispatch_verb(int argc, char *argv[], const Verb verbs[], void *userdata) {
                 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 {
index 010c0df3fdc97f7d05bc9d9f8030e8cd34319be7..c5fe6cc7c58b75c5065d96734b139ae2c76a4b2c 100644 (file)
@@ -8,7 +8,6 @@
 typedef enum VerbFlags {
         VERB_DEFAULT      = 1 << 0,
         VERB_ONLINE_ONLY  = 1 << 1,
-        VERB_MUST_BE_ROOT = 1 << 2,
 } VerbFlags;
 
 typedef struct {
index 2f9df7dd1b74eb394a642aa33ed3c7803c6880c3..a4b42df85ba8fbd7698632a072796017f2f06c90 100644 (file)
@@ -22,6 +22,22 @@ int parse_vlanid(const char *p, uint16_t *ret) {
         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,
index ebe4331ed4fe226f9d5b306c0002450f41a45cad..c55adee3c07ab7487eb64e79d499db1123ad6658 100644 (file)
@@ -15,6 +15,7 @@ static inline bool vlanid_is_valid(uint16_t id) {
 }
 
 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);
index 11757f2efae16a1b37f6a46490fa8eb33b241147..b9fe96635d22bc430b411310afb4f76377f774e9 100644 (file)
@@ -17,6 +17,7 @@
 
 #include "sd-messages.h"
 
+#include "btrfs-util.h"
 #include "def.h"
 #include "exec-util.h"
 #include "fd-util.h"
@@ -35,6 +36,8 @@
 
 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;
@@ -78,6 +81,14 @@ static int write_hibernate_location_info(void) {
         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);
@@ -91,11 +102,15 @@ static int write_hibernate_location_info(void) {
         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;
 }
 
@@ -141,6 +156,32 @@ static int write_state(FILE **f, char **states) {
         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,
@@ -166,9 +207,10 @@ static int execute(char **modes, char **states) {
 
         /* 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");;
@@ -317,7 +359,9 @@ static int parse_argv(int argc, char *argv[]) {
                                        "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),
index 77e265d71076ca7328e7c5c6e2a9f37e5c5af387..6ec4986c10ce70b21345f4afb5b325fdecaebfdc 100644 (file)
@@ -57,9 +57,10 @@ static int run(const char *dest, const char *dest_early, const char *dest_late)
         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");
index cca150830979a317eca98b14434bae6eb1c8929a..7b39f5d95b5640b763680323792ec9a2b0c07429 100644 (file)
@@ -28,6 +28,7 @@
 #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"
@@ -36,6 +37,7 @@
 #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"
@@ -161,12 +163,14 @@ static const char *arg_boot_loader_entry = NULL;
 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);
@@ -1227,7 +1231,7 @@ static usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) {
         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)
@@ -1235,7 +1239,7 @@ static usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) {
                 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;
@@ -1941,6 +1945,7 @@ static void output_machines_list(struct machine_info *machine_infos, unsigned n)
                 statelen = STRLEN("STATE"),
                 failedlen = STRLEN("FAILED"),
                 jobslen = STRLEN("JOBS");
+        bool state_missing = false;
 
         assert(machine_infos || n == 0);
 
@@ -1951,7 +1956,7 @@ static void output_machines_list(struct machine_info *machine_infos, unsigned n)
                 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;
         }
 
@@ -1990,9 +1995,12 @@ static void output_machines_list(struct machine_info *machine_infos, unsigned n)
                 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,
@@ -2005,8 +2013,12 @@ static void output_machines_list(struct machine_info *machine_infos, unsigned n)
                                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) {
@@ -2499,13 +2511,14 @@ static int unit_find_paths(
         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);
@@ -2540,7 +2553,7 @@ static int unit_find_paths(
                         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(
@@ -2780,158 +2793,6 @@ static const char *verb_to_job_type(const char *verb) {
        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 */
@@ -2940,7 +2801,7 @@ static int start_unit_one(
                 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;
@@ -2952,12 +2813,6 @@ static int start_unit_one(
         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);
@@ -3046,6 +2901,12 @@ static int start_unit_one(
                         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:
@@ -3177,8 +3038,8 @@ static const char** make_extra_args(const char *extra_args[static 4]) {
 }
 
 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;
@@ -3266,19 +3127,15 @@ static int start_unit(int argc, char *argv[], void *userdata) {
                 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);
 
@@ -3303,11 +3160,11 @@ static int start_unit(int argc, char *argv[], void *userdata) {
                                 (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;
         }
 
@@ -3941,6 +3798,101 @@ static int kill_unit(int argc, char *argv[], void *userdata) {
         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;
 
@@ -3955,6 +3907,8 @@ typedef struct ExecStatusInfo {
         int code;
         int status;
 
+        ExecCommandFlags flags;
+
         LIST_FIELDS(struct ExecStatusInfo, exec);
 } ExecStatusInfo;
 
@@ -3967,7 +3921,8 @@ static void exec_status_info_free(ExecStatusInfo *i) {
         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;
@@ -3977,7 +3932,7 @@ static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) {
         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)
@@ -3995,9 +3950,12 @@ static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) {
         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,
@@ -4005,7 +3963,15 @@ static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) {
         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;
@@ -4477,7 +4443,7 @@ static void print_status_info(
         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];
@@ -4749,9 +4715,10 @@ static int map_exec(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_e
         _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;
 
@@ -4761,7 +4728,7 @@ static int map_exec(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_e
 
         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)
@@ -4814,6 +4781,16 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
                         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;
@@ -5092,29 +5069,51 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
 
                 } 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);
@@ -5405,7 +5404,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
                                 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;
@@ -5417,7 +5416,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
 
                         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)
@@ -5459,82 +5458,85 @@ static int show_one(
                 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                                                           },
                 {}
         };
 
@@ -6228,8 +6230,8 @@ static int switch_root(int argc, char *argv[], void *userdata) {
         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. */
@@ -7280,12 +7282,12 @@ static int get_file_to_edit(
         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();
         }
@@ -7652,6 +7654,7 @@ static int systemctl_help(void) {
                "                      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"
@@ -7700,6 +7703,8 @@ static int systemctl_help(void) {
                "                                      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"
@@ -8003,6 +8008,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 ARG_NOW,
                 ARG_MESSAGE,
                 ARG_WAIT,
+                ARG_WHAT,
         };
 
         static const struct option options[] = {
@@ -8054,6 +8060,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                 { "now",                 no_argument,       NULL, ARG_NOW                 },
                 { "message",             required_argument, NULL, ARG_MESSAGE             },
                 { "show-transaction",    no_argument,       NULL, 'T'                     },
+                { "what",                required_argument, NULL, ARG_WHAT                },
                 {}
         };
 
@@ -8406,6 +8413,38 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
                         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);
@@ -8835,7 +8874,7 @@ static int systemctl_main(int argc, char *argv[]) {
                 { "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           },
@@ -8852,6 +8891,7 @@ static int systemctl_main(int argc, char *argv[]) {
                 { "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    },
index 62b0f723c7a1a316c0d496822c5b29608645e87a..860961e2ba4c996c6c6beb7047d18a781f98b7bd 100644 (file)
@@ -260,7 +260,7 @@ int sd_notify(int unset_environment, const char *state);
 
      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.
index d27e0ad2019cb9b5016404ab1e24d3c360777e3b..b0a51185e9c19fc8dfe9f8a7119fc2044e96b40f 100644 (file)
@@ -105,6 +105,7 @@ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short typ
 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);
index 166c30185a1e67515bb0cf0cd916fcd83abd693a..c78f7c86bb49617b53d21a41f13c59688c6f6917 100644 (file)
@@ -49,6 +49,8 @@ _SD_BEGIN_DECLARATIONS;
  *   -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 */
@@ -90,6 +92,8 @@ int sd_network_link_get_setup_state(int ifindex, char **state);
  */
 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:
@@ -127,7 +131,7 @@ int sd_network_link_get_mdns(int ifindex, char **mdns);
 
 /* 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
  */
index 843c3837d1f45385ccb95a8c15d00c46fc00c180..990a2f927b76cab6a02f3486f90d1025ba68d479 100644 (file)
@@ -1390,7 +1390,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) {
 
         /* 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);
index 514b6e01695206ff0b0a1edc504fe005929feecd..175e4a23931813d43b0fd8bae261f897ce68a4eb 100644 (file)
@@ -87,7 +87,7 @@ static int add_alias(const char *service, const char *alias) {
         assert(service);
         assert(alias);
 
-        link = strjoina(arg_dest, "/", alias);
+        link = prefix_roota(arg_dest, alias);
 
         r = symlink(service, link);
         if (r < 0) {
@@ -116,7 +116,7 @@ static int generate_unit_file(SysvStub *s) {
         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,
@@ -322,7 +322,7 @@ static int handle_provides(SysvStub *s, unsigned line, const char *full_text, co
         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)
@@ -391,7 +391,7 @@ static int handle_dependencies(SysvStub *s, unsigned line, const char *full_text
                 _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)
@@ -784,7 +784,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) {
                                 continue;
                         }
 
-                        fpath = strjoin(*path, "/", de->d_name);
+                        fpath = path_join(*path, de->d_name);
                         if (!fpath)
                                 return log_oom();
 
@@ -829,7 +829,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
                         _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;
@@ -859,7 +859,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic
                                 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;
index ee6cdb6d54b1ce83b74ce302da3e3f5647ffec04..34dd5cbc4560c88615418433add51fa6d0fceaec 100644 (file)
@@ -216,6 +216,10 @@ tests += [
          [],
          []],
 
+        [['src/test/test-format-util.c'],
+         [],
+         []],
+
         [['src/test/test-ratelimit.c'],
          [],
          []],
index e6b6d96d5a91b393ba4b2e2de80535d0e76b542e..cf69436d262945d63a650089a1c5a16113df1fdd 100644 (file)
@@ -6,6 +6,7 @@
 #include "alloc-util.h"
 #include "macro.h"
 #include "memory-util.h"
+#include "random-util.h"
 #include "tests.h"
 
 static void test_alloca(void) {
@@ -132,6 +133,20 @@ static void test_cleanup_order(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);
 
@@ -140,6 +155,7 @@ int main(int argc, char *argv[]) {
         test_memdup_multiply_and_greedy_realloc();
         test_bool_assign();
         test_cleanup_order();
+        test_auto_erase_memory();
 
         return 0;
 }
index 90ab15c5499f50c3358e1b09db85dc2049bcd82b..6a752215427bec8b03632e8baebfb0e5a241ec1f 100644 (file)
@@ -9,6 +9,7 @@
 #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"
@@ -42,7 +43,7 @@ static bool can_memlock(void) {
 
 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()
         };
 
@@ -54,6 +55,9 @@ int main(int argc, char *argv[]) {
         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);
 
@@ -88,14 +92,31 @@ int main(int argc, char *argv[]) {
                 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. */
@@ -175,5 +196,31 @@ int main(int argc, char *argv[]) {
         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;
 }
index 5e5638cd72d726bdee3a82dbf1dd5d45b8e487bb..5bd0e3458c45eee29d1b037837807f8300c7aab7 100644 (file)
@@ -5,8 +5,8 @@
 #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"
 
index 10c9f63b9b3213d5da6c899ae013178c06048758..46358ef9527824867183203f0b275626e8b3f617 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "alloc-util.h"
 #include "calendarspec.h"
+#include "errno-util.h"
 #include "string-util.h"
 #include "util.h"
 
@@ -23,7 +24,7 @@ static void test_one(const char *input, const char *output) {
 
         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);
@@ -56,7 +57,7 @@ static void test_next(const char *input, const char *new_tz, usec_t after, usec_
 
         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
@@ -103,10 +104,10 @@ static void test_hourly_bug_4031(void) {
         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);
index 4bbca2074f5869803fc5facfbd20c7147c6cede0..a79263a50bf94f4e78352c3ef51f19f90609fc58 100644 (file)
@@ -43,77 +43,77 @@ static void test_condition_test_path(void) {
 
         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);
 }
 
@@ -133,12 +133,12 @@ static void test_condition_test_control_group_controller(void) {
         /* 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);
@@ -151,23 +151,23 @@ static void test_condition_test_control_group_controller(void) {
                         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);
                 }
         }
@@ -177,12 +177,12 @@ static void test_condition_test_control_group_controller(void) {
 
         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);
 }
 
@@ -218,17 +218,17 @@ static void test_condition_test_host(void) {
 
         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();
@@ -240,7 +240,7 @@ static void test_condition_test_host(void) {
         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);
         }
 }
@@ -277,12 +277,12 @@ static void test_condition_test_kernel_command_line(void) {
 
         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);
 }
 
@@ -293,24 +293,26 @@ static void test_condition_test_kernel_version(void) {
 
         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);
@@ -318,59 +320,79 @@ static void test_condition_test_kernel_version(void) {
 
         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);
@@ -378,31 +400,31 @@ static void test_condition_test_kernel_version(void) {
         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);
 }
 
@@ -411,12 +433,12 @@ static void test_condition_test_null(void) {
 
         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);
 }
 
@@ -425,7 +447,7 @@ static void test_condition_test_security(void) {
 
         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);
index 9fd8b6b59055532c2fd925d2be5efaa3b06401c4..07b681cf43c187efe63af6e404dbf5dc6c8fa215 100644 (file)
@@ -102,11 +102,11 @@ static void test_conf_files_insert(const char *root) {
         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)));
index 1738938fce61230b15fbcf9b1c975415878d6500..597265efa676c947ad89d7902bc20e3b2846c0b1 100644 (file)
@@ -230,6 +230,13 @@ static const char* const config_file[] = {
         "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"
@@ -330,32 +337,32 @@ static void test_config_parse(unsigned i, const char *s) {
                          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;
index 701594fe538f45375e0a73520d28d244813f90b1..9809d408f641457ee2be26cc16a1dd28de422843 100644 (file)
@@ -9,12 +9,14 @@
 #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;
 
@@ -94,6 +96,30 @@ int main(int argc, char *argv[]) {
         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));
index f6f2e6317e55eb5e289a09bd079cfa6635cb4f8c..ec5189163473f0a46d9b04f0ccfed6b409f5c470 100644 (file)
@@ -402,6 +402,50 @@ static void test_error_catching(void) {
         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);
 
@@ -411,6 +455,8 @@ int main(int argc, char *argv[]) {
         test_stdout_gathering();
         test_environment_gathering();
         test_error_catching();
+        test_exec_command_flags_from_strv();
+        test_exec_command_flags_to_strv();
 
         return 0;
 }
index 30019382ae319c6830d364fcf008a988795d1381..f148b3e6f1e3231e721793fe1a835fae9f8944a8 100644 (file)
@@ -43,12 +43,12 @@ static void test_extract_first_word(void) {
         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));
@@ -64,7 +64,7 @@ static void test_extract_first_word(void) {
         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 = "\'";
@@ -74,7 +74,7 @@ static void test_extract_first_word(void) {
         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";
@@ -84,17 +84,17 @@ static void test_extract_first_word(void) {
         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));
@@ -106,7 +106,7 @@ static void test_extract_first_word(void) {
         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));
@@ -135,7 +135,7 @@ static void test_extract_first_word(void) {
         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));
@@ -169,7 +169,7 @@ static void test_extract_first_word(void) {
         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));
@@ -205,17 +205,17 @@ static void test_extract_first_word(void) {
         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));
@@ -265,12 +265,12 @@ static void test_extract_first_word(void) {
         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));
@@ -340,12 +340,12 @@ static void test_extract_first_word_and_warn(void) {
         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));
@@ -355,19 +355,19 @@ static void test_extract_first_word_and_warn(void) {
         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));
@@ -390,7 +390,7 @@ static void test_extract_first_word_and_warn(void) {
         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));
@@ -414,21 +414,21 @@ static void test_extract_first_word_and_warn(void) {
         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));
@@ -488,7 +488,7 @@ static void test_extract_many_words(void) {
         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"));
diff --git a/src/test/test-format-util.c b/src/test/test-format-util.c
new file mode 100644 (file)
index 0000000..6558ee2
--- /dev/null
@@ -0,0 +1,39 @@
+/* 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;
+}
index cddbfa7022416b65c21eb60d2d27ae5ec7ac8c63..7e898735c939e5259c57e43ebe1c1b9b9637a352 100644 (file)
@@ -21,7 +21,7 @@ static void assert_iovec_entry(const struct iovec *iovec, const char* content) {
         "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;
 
@@ -52,7 +52,7 @@ static void test_basic_parsing(void) {
 }
 
 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;
 
index 7878512465ed0650d4ddb94bece7e50657d29563..dcb5bcc254481b3da1d99805133903e5a475427d 100644 (file)
@@ -433,19 +433,20 @@ static void test_util_resolve_subsys_kernel(void) {
 }
 
 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"));
@@ -462,14 +463,14 @@ static void test_list(void) {
         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"));
@@ -487,7 +488,6 @@ static void test_list(void) {
         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) {
index 18ef56ac39176e86971672b11e004fa8eb32c24f..c1f2f8b09dd2a735e54a75737f053dcd3accb69c 100644 (file)
@@ -6,6 +6,7 @@
 #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))
@@ -26,6 +27,14 @@ assert_cc(!IS_SYNTHETIC_ERRNO(0));
 #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(),
@@ -68,6 +77,8 @@ static void test_log_syntax(void) {
 int main(int argc, char* argv[]) {
         int target;
 
+        test_file();
+
         for (target = 0; target < _LOG_TARGET_MAX; target++) {
                 log_set_target(target);
                 log_open();
index 182991695a4d72dc91856137233da43e8c489fc5..90a8d5f36c6f5f12367fde6fba65ef2da3631365 100644 (file)
@@ -388,12 +388,12 @@ static void test_prefix_root_one(const char *r, const char *p, const char *expec
         _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) {
@@ -403,6 +403,9 @@ 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");
@@ -461,7 +464,7 @@ static void test_path_extract_filename_one(const char *input, const char *output
         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);
 }
index 1d0612985bf9f4c480e8c754cdf8437969a79de4..662688e0f0d18c9f42b27dc514154c887c714fc2 100644 (file)
@@ -2,8 +2,8 @@
 
 #include <errno.h>
 
+#include "format-util.h"
 #include "log.h"
-#include "parse-util.h"
 #include "procfs-util.h"
 
 int main(int argc, char *argv[]) {
index 2a23bf2384b0de32fc43360c7ce19aee3eb3ba7d..014b1aa7a2f0eea88d54564cb61b9748159408c5 100644 (file)
@@ -7,6 +7,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "errno-util.h"
 #include "fd-util.h"
 #include "log.h"
 #include "memory-util.h"
@@ -95,13 +96,13 @@ static void test_sleep(void) {
 
         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[]) {
index 064a5abdac690e61542250db68d75b138a059bae..d84170bc22231cec0c7f3107bc90742cd9314d68 100644 (file)
@@ -9,29 +9,6 @@
 #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;
 
@@ -380,7 +357,7 @@ static void check(const char *test, char** expected, bool trailing) {
         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;
@@ -582,7 +559,6 @@ static void test_memory_startswith_no_case(void) {
 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();
index 18ad03669cd5e5b117301695f2e22c012922b4f8..903de18083c7d15d62772e2c389f4612ac0a4fea 100644 (file)
@@ -204,7 +204,7 @@ static void test_strv_unquote(const char *quoted, char **list) {
         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, " | ");
@@ -221,66 +221,66 @@ static void test_invalid_unquote(const char *quoted) {
         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);
index 21d56d9be68aae0704565b6ba6d8ca73b170f739..d7199124a3d9ed246ce173a2d392fc2ea5edf4a7 100644 (file)
@@ -1,5 +1,6 @@
 /* SPDX-License-Identifier: LGPL-2.1+ */
 
+#include <stdio.h>
 #include <string.h>
 
 #include "string-util.h"
@@ -78,6 +79,24 @@ static void test_strscpyl(void) {
         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();
@@ -85,5 +104,7 @@ int main(int argc, char *argv[]) {
         test_strscpy();
         test_strscpyl();
 
+        test_sd_event_code_migration();
+
         return 0;
 }
index 36ffd072b4475add024d02b9081dad4c339e08f0..02eecf3e3af0e8339a189e7de211cc602769376e 100644 (file)
@@ -461,38 +461,6 @@ static bool unix_socket_alive(const char *fn) {
         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;
 
@@ -557,13 +525,19 @@ static int dir_cleanup(
                 /* 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;
@@ -656,14 +630,16 @@ static int dir_cleanup(
                                 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)) {
@@ -947,7 +923,7 @@ static int parse_xattrs_from_arg(Item *i) {
         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)
@@ -2495,15 +2471,14 @@ static int patch_var_run(const char *fname, unsigned line, char **path) {
         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);
 
@@ -2526,7 +2501,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
         r = extract_many_words(
                         &buffer,
                         NULL,
-                        EXTRACT_QUOTES,
+                        EXTRACT_UNQUOTE,
                         &action,
                         &path,
                         &mode,
@@ -2744,7 +2719,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool
         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);
index 271d0bb90ce0638ccaa86da52a10cc85c159d0cd..843c302bf4ba1dba04336084cba99a535650d630 100644 (file)
@@ -238,13 +238,13 @@ finish:
 }
 
 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);
 
@@ -266,22 +266,14 @@ static int send_passwords(const char *socket_name, char **passwords) {
                 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) {
index 13068c039b5dd6e022efb315404ae8c4c63ca44f..511fe428b9f65a932ad72082fc920a2ba79e9627 100644 (file)
@@ -40,8 +40,6 @@ libudev_core_sources = '''
         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()
index dff849a34afb3d1d37060b5e80e84f6bf7ce4947..a3d7dec88cb0a1fdcd05aab7592acba198374360 100644 (file)
@@ -20,10 +20,11 @@ struct ConfigPerfItem;
 %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)
@@ -47,8 +48,8 @@ Link.TCP6SegmentationOffload,    config_parse_tristate,           0,
 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)
index a4e10ff988b7760da1001bfd94c164ca3d106714..9989e6ab65bdc623ba0894e5974a0e19f511b84a 100644 (file)
@@ -51,6 +51,7 @@ static void link_config_free(link_config *link) {
         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);
@@ -161,7 +162,7 @@ int link_load_one(link_config_ctx *ctx, const char *filename) {
 
         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.",
@@ -240,42 +241,29 @@ int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret)
         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;
@@ -318,10 +306,13 @@ static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr
         } 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);
         }
@@ -351,7 +342,9 @@ int link_config_apply(link_config_ctx *ctx, link_config *config,
         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)
index efe5f2ce3a9b6703556eeeb1daae7320f61b93b9..cd99cd54d4541381963e6c2962d63d5bc0822123 100644 (file)
@@ -40,6 +40,7 @@ struct link_config {
         char **match_driver;
         char **match_type;
         char **match_name;
+        char **match_property;
         LIST_HEAD(Condition, conditions);
 
         char *description;
@@ -52,7 +53,7 @@ struct link_config {
         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];
index 8223f9cda175d87cc1887756dce3a4337980fa1f..0d7f413e9e043880f39a4283621f5770ec909531 100644 (file)
@@ -8,6 +8,7 @@ static const NamingScheme naming_schemes[] = {
         { "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 … */
 };
index 4061eec99e7cdf8b8ba54ccf15d4302d387c4f59..38dfa75f9bf1205ae79316f8302752ef0ccc35cd 100644 (file)
  * 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;
index 6abc9bee39c2277e8d336ca044592e0d4ddd78b5..b82fd884972e1ca589063476aa036a1890b4868a 100644 (file)
@@ -139,7 +139,7 @@ static int get_virtfn_info(sd_device *dev, struct netnames *names, struct virtfn
                 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;
 
index c3dc213aa8c7c84bb339f3f379d9d1c196fd6260..b38370883f7dd0f8e269389071cc9d197858c87f 100644 (file)
@@ -391,7 +391,7 @@ int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout) {
         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;
index 09eb56b97de095d0ced8460e6ee12c190d3c86fd..dbdb9065dcd1c730416a721c20be24a87cedf57b 100644 (file)
@@ -157,24 +157,24 @@ static int get_subst_type(const char **str, bool strict, FormatSubstitutionType
         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;
                         }
@@ -184,7 +184,7 @@ static int get_subst_type(const char **str, bool strict, FormatSubstitutionType
                 /* 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;
 
@@ -200,7 +200,7 @@ static int get_subst_type(const char **str, bool strict, FormatSubstitutionType
                 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;
@@ -311,7 +311,7 @@ static ssize_t udev_event_subst_format(
                         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')
@@ -327,7 +327,7 @@ static ssize_t udev_event_subst_format(
                         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);
                         }
@@ -447,7 +447,7 @@ ssize_t udev_event_apply_format(UdevEvent *event,
         assert(dest);
         assert(size > 0);
 
-        while (s[0] != '\0') {
+        while (*s) {
                 FormatSubstitutionType type;
                 char attr[UTIL_PATH_SIZE];
                 ssize_t subst_len;
@@ -480,33 +480,48 @@ ssize_t udev_event_apply_format(UdevEvent *event,
         }
 
         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;
+                        }
                 }
         }
 
@@ -804,7 +819,6 @@ static int rename_netif(UdevEvent *event) {
 
 static int update_devnode(UdevEvent *event) {
         sd_device *dev = event->dev;
-        bool apply;
         int r;
 
         r = sd_device_get_devnum(dev, NULL);
@@ -819,17 +833,13 @@ static int update_devnode(UdevEvent *event) {
 
         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");
         }
 
@@ -837,21 +847,14 @@ static int update_devnode(UdevEvent *event) {
                 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(
index eca4ec8a9d021130a7946eaa1380edc03a210bfe..a05e1252595a5fd41893d82d307a3d9e483e64aa 100644 (file)
@@ -51,7 +51,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(UdevEvent*, udev_event_free);
 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,
index 7d2547857768e0e543e04ddd869af981731abfb0..7e3447f7fa355b763d6d61535f8effab7ad2287d 100644 (file)
@@ -26,6 +26,7 @@
 #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;
@@ -270,13 +271,14 @@ int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
         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);
 
@@ -297,23 +299,29 @@ static int node_permissions_apply(sd_device *dev, bool apply,
                 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);
 
index 6811a5ce4b775dcade9bc159bc4e1c8f88a965b5..303594fa51ee8b7446acf91e57fe829166459ee6 100644 (file)
@@ -14,6 +14,7 @@
 #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);
 }
@@ -91,13 +303,6 @@ UdevRules *udev_rules_free(UdevRules *rules) {
         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;
@@ -297,7 +502,7 @@ static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type,
                 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);
@@ -305,6 +510,27 @@ static int rule_line_add_token(UdevRuleLine *rule_line, UdevRuleTokenType type,
         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;
@@ -346,9 +572,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                         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);
@@ -369,8 +593,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                         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
@@ -392,8 +615,8 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                                 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);
@@ -406,8 +629,8 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                 }
 
                 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);
@@ -429,10 +652,9 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
 
                 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)) {
@@ -441,17 +663,14 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                 }
 
                 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)) {
@@ -460,9 +679,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                 }
 
                 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);
@@ -488,10 +705,9 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
 
                 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);
 
@@ -516,8 +732,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                         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);
 
@@ -525,8 +740,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
         } 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) {
@@ -541,8 +755,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
         } 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) {
@@ -644,8 +857,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
 
                         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);
@@ -673,8 +885,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
 
                         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);
@@ -695,15 +906,13 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
                 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) {
@@ -715,8 +924,7 @@ static int parse_token(UdevRules *rules, const char *key, char *attr, UdevRuleOp
         } 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")) {
index f5f5bdb060b54088d5d60f0cc5e4b6ee3d9925ee..9fff5da7b5d48e30969d9f5a122440a9c5b9a735 100644 (file)
@@ -1,46 +1,13 @@
 /* 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" */
@@ -49,118 +16,6 @@ typedef enum {
         _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);
@@ -170,57 +25,3 @@ int udev_rules_apply_to_event(UdevRules *rules, UdevEvent *event,
                               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)
index f9b3e95794a91628ac0c5b5c284c233056ade353..82e25149325ecbc6606bc18a09dbd892afb85c63 100644 (file)
@@ -73,10 +73,6 @@ int control_main(int argc, char *argv[], void *userdata) {
                 {}
         };
 
-        r = must_be_root();
-        if (r < 0)
-                return r;
-
         if (running_in_chroot() > 0) {
                 log_info("Running in chroot, ignoring request.");
                 return 0;
index 9078a3c2ce21aa81abec512b3605fc10b34c2132..2c8626ffad2f06d5dd1097f5dcce43a739bb164c 100644 (file)
@@ -467,9 +467,14 @@ int info_main(int argc, char *argv[], void *userdata) {
                         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)
index 8124951422add08fa7e213f5e311bd9461afe489..7ad9993f09e979fe84e5db396f5965f22de6d774 100644 (file)
@@ -33,7 +33,7 @@ static int help(void) {
                "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);
 
@@ -56,6 +56,11 @@ static int parse_argv(int argc, char *argv[]) {
                 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),
index b7dafb775575d7e6bbafc8d4a4413ba266750fcc..77d95e513fb3ae4ddef8ecefcd2494b237bb6e9f 100644 (file)
@@ -25,7 +25,7 @@ static bool arg_dry_run = false;
 
 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;
@@ -45,7 +45,10 @@ static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_se
 
                 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;
                 }
 
@@ -56,7 +59,7 @@ static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_se
                 }
         }
 
-        return 0;
+        return ret;
 }
 
 static int device_monitor_handler(sd_device_monitor *m, sd_device *dev, void *userdata) {
@@ -112,7 +115,7 @@ static int help(void) {
                "  -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"
@@ -202,6 +205,10 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                                 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);
 
@@ -301,12 +308,6 @@ int trigger_main(int argc, char *argv[], void *userdata) {
                 }
         }
 
-        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;
 
index 8e7402fd875b338aea2deba1c3c6264ed114826f..99efad5b06b66a204e30be1a4d4600c211e1c860 100644 (file)
@@ -1052,7 +1052,7 @@ static int on_ctrl_msg(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, co
                 }
 
                 eq++;
-                if (!isempty(eq)) {
+                if (isempty(eq)) {
                         log_debug("Received udev control message (ENV), unsetting '%s'", key);
 
                         r = hashmap_put(manager->properties, key, NULL);
@@ -1726,9 +1726,10 @@ static int run(int argc, char *argv[]) {
         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)
index 65a4e7b0fd558d5d200fb5998d89631ee23c79ec..f2b74f3dc14a55541864091feaf0bb37cb9c86d8 100644 (file)
@@ -16,6 +16,7 @@
 #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"
@@ -188,7 +189,7 @@ static int determine_devices(void) {
         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();
         }
@@ -196,7 +197,7 @@ static int determine_devices(void) {
         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();
         }
index ccd5c2cc568c3fbbb1a82346e44150b0ee8dd063..47bf84769334fa093a985ea98115cd6b7a75dadf 100644 (file)
@@ -9,4 +9,4 @@
 # 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
index 0f29ad7eda727ddcdb3ecaf3a3e9c8d72f030d18..a34e2adbe263b9119f987e30c980d9429a1f4ef7 100755 (executable)
@@ -29,7 +29,7 @@ Type=oneshot
 EOF
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root"
index aed1c591069b203f242ba7085b731173a03b99f0..9d25e942500fb3336d7963e1c9bca9dd87f2dc07 100755 (executable)
@@ -75,7 +75,7 @@ EOF
         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
index 89125109d894554b0c17caca30ac686a6d00f0e1..2a7df79a47868329e427303fdeb8fa9e12d70a80 100755 (executable)
@@ -44,7 +44,7 @@ EOF
         cp test-jobs.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root"
index f24a8f6be7a3dc94d8f5165100458a8e20768c7d..a5ba7c84349813cb0aa6889e06847ffa79d01816 100755 (executable)
@@ -46,7 +46,7 @@ EOF
         cp test-journal.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root"
index 3c29b2ee3fe0f6e17b3cdb46d566740c1f2f90e6..176014d270af1a7ebf789d077f126cbf23a4b639 100755 (executable)
@@ -42,7 +42,7 @@ EOF
         cp test-rlimits.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root"
index 2b49b3d5ae3551cba2c0871df54486da2341003a..5d26cb0f0a4bca756232db4d1b94590f087e5d30 100755 (executable)
@@ -92,7 +92,7 @@ EOF
         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
index 93ddb6f5fbb28d2b5af371999f6ae8632e36110e..e488cb4347f9bc81b28f0bd95898f8f62ecb13a6 100755 (executable)
@@ -40,7 +40,7 @@ EOF
         cp test-segfault.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root"
index ef2aba0e17e501c1a4f91b0d09c9f6d86e4c74b5..d455d5d555ca134c59a49f195692f886bf8a6b5b 100755 (executable)
@@ -60,7 +60,7 @@ ExecStart=/bin/systemctl reload /
 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
index 8e7402fcd46c20238ea785f43429180157f2bba6..bef31cd096e2faca96c585dc1dd16dda58425a2f 100755 (executable)
@@ -32,7 +32,7 @@ TimeoutStopSec=270s
 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
index dc7a9acaa102078589ddce1ac0e15abecd141c3a..081d22b44d23e561eb5a5b4ac946141f9a30c7d0 100755 (executable)
@@ -45,7 +45,7 @@ ExecStart=/bin/true
 EOF
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     # mask some services that we do not want to run in these tests
index 3419b78fe797e2469856fefdcbf6467828847372..267224c0429ec33eb06eb8ee95c2d67275bc271b 100755 (executable)
@@ -64,7 +64,7 @@ EOF
 
         chmod 0755 $initdir/test-fail-on-restart.sh
         setup_testsuite
-    ) || return 1
+    )
 
     ddebug "umount $TESTDIR/root"
     umount $TESTDIR/root
index 31cf1b471ae20d2a57b04e35b022b99e15568907..08b59ae165ca5e29953d0a724d6d8ca6c66a43ed 100755 (executable)
@@ -90,7 +90,7 @@ EOF
 
         chmod 0755 $initdir/test-socket-group.sh
         setup_testsuite
-    ) || return 1
+    )
 
     setup_nspawn_root
 
index 8252c4b2aaf47b6f34ce3b71c4c76227ab2eebaf..6a40bebbe0ce8cbc014a8392a4b02e55f24f92aa 100755 (executable)
@@ -184,7 +184,7 @@ EOF
 
         chmod 0755 $initdir/test-nspawn.sh
         setup_testsuite
-    ) || return 1
+    )
 
     ddebug "umount $TESTDIR/root"
     umount $TESTDIR/root
index 33e311a1e7ea17ac491286c20e25f11abc5717d9..c22fc7c3c060acd019e98c9a7d0bdf0542cf68a6 100755 (executable)
@@ -73,7 +73,7 @@ EOF
 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
index 6613e32fc9072b349a26feaeca5cf5d13a7b563d..7e6dc45f6bc7221fac9630643f5ee5b172f61638 100755 (executable)
@@ -30,7 +30,7 @@ test_setup() {
         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
index 9bd6e98644adab7a634509425f211b32bf2f75b0..02578d33c79e305d2dc6906ecabf18153a7832a7 100755 (executable)
@@ -38,7 +38,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
 
     ddebug "umount $TESTDIR/root"
     umount $TESTDIR/root
index 277cc2c798968f7462feba3237c1c576e696281b..0cf6391bdcd6d1fc1199c1ff77914cba8e885474 100755 (executable)
@@ -30,7 +30,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     # mask some services that we do not want to run in these tests
index 3ed6a1b928a0eb589bfca80cd355dd77a1dd960b..25461af9dc4f018a132f94c2acc88e4e2682b32e 100755 (executable)
@@ -40,7 +40,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
 
     ddebug "umount $TESTDIR/root"
     umount $TESTDIR/root
index bf80e8b5844f95ca447c0d97de30e3d552f67c62..8adaf490e2ec475534841d982d257684d6affb28 100755 (executable)
@@ -38,7 +38,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root"
index 6ed22be366431de0769fcf857a0dd390f53ac8d4..68423637cea130b563e2615138e0787cd14e043c 100755 (executable)
@@ -38,7 +38,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root"
index 038303edf8fe993bf46cf584ec40b03fa87435c7..5d60b548dd50b9847196f2dba4d97d8d77a10196 100755 (executable)
@@ -91,7 +91,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     # mask some services that we do not want to run in these tests
index 7fe367c3be6d3f7e962f29ed0a597baa676d2b03..4beaa6456e762e571b2b8ff9f164e847b0756667 100755 (executable)
@@ -31,7 +31,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root"
index 9a2913d330c0ba32194fb7c53f5407f20a8832d3..b1a965aff1147347c598d19665296a1f28807baa 100755 (executable)
@@ -30,7 +30,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root"
index fb35f80ec1a592c540c02e69acb4ac59c9f5e2b5..e78e317d6d41508e64835724c13037e2ef23c2f2 100755 (executable)
@@ -40,7 +40,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
     ddebug "umount $TESTDIR/root"
index 67be1d51ccc474c6e396ed9051fd832221971f6a..42db3b621dd4b3de89fff3de710aedf7bfdd9095 100755 (executable)
@@ -49,7 +49,7 @@ ExecStart=/bin/true
 EOF
 
         setup_testsuite
-    ) || return 1
+    )
     setup_nspawn_root
 
         # mask some services that we do not want to run in these tests
index 26ad2f902d1e4a094c9279c3bdead2f78ed6126f..3e297492eaefbbb05e5b4c8d932aa999a29543e2 100755 (executable)
@@ -38,7 +38,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
 
     ddebug "umount $TESTDIR/root"
     umount $TESTDIR/root
index 40e6ff25cd5b9dc6cd9020b22bec47cfa45788bd..47f1fff1d3d1cec6f6f071ffe4a1a8bc86a50868 100755 (executable)
@@ -49,7 +49,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
 
     ddebug "umount $TESTDIR/root"
     umount $TESTDIR/root
index 7156528873dba5b187ba1846c0c4bf8d23e1dbef..1eda377ea0f017f9cf7ed68688e5563c58b9d5cc 100755 (executable)
@@ -38,7 +38,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
 
     ddebug "umount $TESTDIR/root"
     umount $TESTDIR/root
index 7e9431dd11df8951a168957c427340ca5c906f12..aef3db5db52def9a2247f977bc8fecc63b1c9928 100755 (executable)
@@ -41,7 +41,7 @@ EOF
         cp testsuite.sh $initdir/
 
         setup_testsuite
-    ) || return 1
+    )
 
     ddebug "umount $TESTDIR/root"
     umount $TESTDIR/root
diff --git a/test/TEST-33-CLEAN-UNIT/Makefile b/test/TEST-33-CLEAN-UNIT/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-33-CLEAN-UNIT/test.sh b/test/TEST-33-CLEAN-UNIT/test.sh
new file mode 100755 (executable)
index 0000000..44082af
--- /dev/null
@@ -0,0 +1,49 @@
+#!/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 "$@"
diff --git a/test/TEST-33-CLEAN-UNIT/testsuite.sh b/test/TEST-33-CLEAN-UNIT/testsuite.sh
new file mode 100755 (executable)
index 0000000..15aa687
--- /dev/null
@@ -0,0 +1,79 @@
+#!/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
diff --git a/test/TEST-34-DYNAMICUSERMIGRATE/Makefile b/test/TEST-34-DYNAMICUSERMIGRATE/Makefile
new file mode 120000 (symlink)
index 0000000..e9f93b1
--- /dev/null
@@ -0,0 +1 @@
+../TEST-01-BASIC/Makefile
\ No newline at end of file
diff --git a/test/TEST-34-DYNAMICUSERMIGRATE/test.sh b/test/TEST-34-DYNAMICUSERMIGRATE/test.sh
new file mode 100755 (executable)
index 0000000..5964311
--- /dev/null
@@ -0,0 +1,48 @@
+#!/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 "$@"
diff --git a/test/TEST-34-DYNAMICUSERMIGRATE/testsuite.sh b/test/TEST-34-DYNAMICUSERMIGRATE/testsuite.sh
new file mode 100755 (executable)
index 0000000..8f5316e
--- /dev/null
@@ -0,0 +1,46 @@
+#!/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
diff --git a/test/a-conj.service b/test/a-conj.service
new file mode 100644 (file)
index 0000000..db37ae7
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=A conjugate
+Requires=a.service
+After=a.service
+Before=a.service
+
+[Service]
+ExecStart=/bin/true
diff --git a/test/fuzz/fuzz-dns-packet/crash-497be373856c321a8a7b06589df9b2ff2e0d866a b/test/fuzz/fuzz-dns-packet/crash-497be373856c321a8a7b06589df9b2ff2e0d866a
new file mode 100644 (file)
index 0000000..8d24e57
Binary files /dev/null and b/test/fuzz/fuzz-dns-packet/crash-497be373856c321a8a7b06589df9b2ff2e0d866a differ
index 5925e5ad12d046f7eecdfde0ef16297ccae384b9..61155063a8bfe137e28b545c9e58b8074cbfe8cd 100644 (file)
@@ -4,6 +4,7 @@ OriginalName=
 Path=
 Driver=
 Type=
+Property=
 Host=
 Virtualization=
 KernelCommandLine=
diff --git a/test/fuzz/fuzz-netdev-parser/27-xfrm.netdev b/test/fuzz/fuzz-netdev-parser/27-xfrm.netdev
new file mode 100644 (file)
index 0000000..cdcb2d2
--- /dev/null
@@ -0,0 +1,7 @@
+[NetDev]
+Name=xfrm99
+Kind=xfrm
+
+[Xfrm]
+InterfaceId=7
+Independent=false
index be1b197024b5d46b70dfb7fd19c0e6858077c32a..874c3e5f8ff22ae3521402a2e4dcdbc92d2fe915 100644 (file)
@@ -66,6 +66,7 @@ AllowLocalRemote=
 Local=
 TOS=
 Independent=
+AssignToLoopback=
 Key=
 InputKey=
 Encapsulation=
@@ -207,3 +208,6 @@ KeyId=
 Key=
 KeyFile=
 Activate=
+[Xfrm]
+Independent=
+InterfaceId=
diff --git a/test/fuzz/fuzz-network-parser/27-xfrm.network b/test/fuzz/fuzz-network-parser/27-xfrm.network
new file mode 100644 (file)
index 0000000..fa26c84
--- /dev/null
@@ -0,0 +1,5 @@
+[Match]
+Name=eth0
+
+[Network]
+Xfrm=xfrm99
index bba8948d35271dcc0a8a3ed1f8d19f9722fc07c6..b895e09d5302297ed7232704c12e6966ea26a1f5 100644 (file)
@@ -20,6 +20,7 @@ Driver=
 Architecture=
 Path=
 Name=
+Property=
 Virtualization=
 KernelCommandLine=
 Host=
@@ -65,6 +66,38 @@ UseTimezone=
 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=
@@ -120,6 +153,7 @@ DNSSEC=
 IPv6HopLimit=
 IPForward=
 IPv6Token=
+Xfrm=
 Description=
 VXLAN=
 L2TP=
@@ -135,6 +169,7 @@ DHCPServer=
 BindCarrier=
 VRF=
 IgnoreCarrierLoss=
+KeepConfiguration=
 [IPv6Prefix]
 Prefix=
 OnLink=
@@ -211,10 +246,3 @@ MaxLeaseTimeSec=
 DefaultLeaseTimeSec=
 EmitTimezone=
 DNS=
-MaxAttempts=
-[DHCPv4]
-UseHostname=
-UseMTU=
-UseDomainName=
-CriticalConnection=
-UseDNS=
diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-15678 b/test/fuzz/fuzz-network-parser/oss-fuzz-15678
new file mode 100644 (file)
index 0000000..2a37d5f
--- /dev/null
@@ -0,0 +1,2 @@
+[IPv6AcceptRA]
+BlackList=70:: 70::
\ No newline at end of file
index fe6b1be30232bc7e96c3a20a70e68b2a6ed9b70b..8105d23a47a7fac4f98a859f135b3272c7a91efb 100644 (file)
@@ -290,6 +290,7 @@ rd.udev.event_timeout=
 rd.udev.exec_delay=
 rd.udev.log_priority=
 resume=
+resumeflags=
 root=
 rootflags=
 rootfstype=
diff --git a/test/i.service b/test/i.service
new file mode 100644 (file)
index 0000000..938ea77
--- /dev/null
@@ -0,0 +1,8 @@
+[Unit]
+Description=I
+Conflicts=a.service d.service
+Wants=b.service
+After=b.service
+
+[Service]
+ExecStart=/bin/true
index 17d0f3cddddeea790c5e14e3f37041a20993459d..8c71e726678cf420d6a5a11220a429d7d1e266e1 100644 (file)
@@ -2,6 +2,7 @@
 
 test_data_files = '''
         a.service
+        a-conj.service
         b.service
         basic.target
         c.service
@@ -26,6 +27,7 @@ test_data_files = '''
         hello-after-sleep.target
         hello.service
         hwdb/10-bad.hwdb
+        i.service
         journal-data/journal-1.txt
         journal-data/journal-2.txt
         nomem.slice
index 8b672fcecbc88083b5030f3da787cb16a22bce75..ae4618c3f375f14c71a94185b074eeba3e4e5be1 100644 (file)
@@ -1,5 +1,9 @@
 [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
index 4a76dd70ea10cb97190bc9dbd36884d172cd86fb..5dbbff0f068a2c87f4220ca2671684e8b75a63df 100644 (file)
@@ -395,8 +395,8 @@ create_asan_wrapper() {
 
 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.
@@ -659,6 +659,7 @@ install_execs() {
          # 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
     )
@@ -1238,6 +1239,14 @@ find_binary() {
 # 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
diff --git a/test/test-network/conf/13-not-match-udev-property.network b/test/test-network/conf/13-not-match-udev-property.network
new file mode 100644 (file)
index 0000000..f0a530e
--- /dev/null
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+Property=INTERFACE=hoge
+
+[Network]
+IPv6AcceptRA=false
diff --git a/test/test-network/conf/14-match-udev-property.network b/test/test-network/conf/14-match-udev-property.network
new file mode 100644 (file)
index 0000000..b632af1
--- /dev/null
@@ -0,0 +1,7 @@
+[Match]
+Name=dummy98
+Property=INTERFACE=dummy98
+
+[Network]
+IPv6AcceptRA=false
+Address=10.1.2.3/16
diff --git a/test/test-network/conf/23-test1-bond199.network b/test/test-network/conf/23-test1-bond199.network
deleted file mode 100644 (file)
index 6e7c28d..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-[Match]
-Name=test1
-
-[Network]
-Bond=bond199
-PrimarySlave=true
diff --git a/test/test-network/conf/24-keep-configuration-static.network b/test/test-network/conf/24-keep-configuration-static.network
new file mode 100644 (file)
index 0000000..0c65a1d
--- /dev/null
@@ -0,0 +1,5 @@
+[Match]
+Name=dummy98
+
+[Network]
+KeepConfiguration=static
diff --git a/test/test-network/conf/25-bridge-configure-without-carrier.network b/test/test-network/conf/25-bridge-configure-without-carrier.network
new file mode 100644 (file)
index 0000000..c7d2583
--- /dev/null
@@ -0,0 +1,7 @@
+[Match]
+Name=bridge99
+
+[Network]
+LinkLocalAddressing=yes
+IPv6AcceptRA=no
+ConfigureWithoutCarrier=yes
diff --git a/test/test-network/conf/25-gre-tunnel-any-any.netdev b/test/test-network/conf/25-gre-tunnel-any-any.netdev
new file mode 100644 (file)
index 0000000..3467b16
--- /dev/null
@@ -0,0 +1,9 @@
+[NetDev]
+Name=gretun96
+Kind=gre
+
+[Tunnel]
+Local=any
+Remote=any
+Key=106
+SerializeTunneledPackets=false
diff --git a/test/test-network/conf/25-ip6gre-tunnel-any-any.netdev b/test/test-network/conf/25-ip6gre-tunnel-any-any.netdev
new file mode 100644 (file)
index 0000000..519474f
--- /dev/null
@@ -0,0 +1,7 @@
+[NetDev]
+Name=ip6gretun96
+Kind=ip6gre
+
+[Tunnel]
+Local=any
+Remote=any
diff --git a/test/test-network/conf/25-ipip-tunnel-any-any.netdev b/test/test-network/conf/25-ipip-tunnel-any-any.netdev
new file mode 100644 (file)
index 0000000..8803dd1
--- /dev/null
@@ -0,0 +1,8 @@
+[NetDev]
+Name=ipiptun96
+Kind=ipip
+MTUBytes=1480
+
+[Tunnel]
+Local=any
+Remote=any
diff --git a/test/test-network/conf/25-ipip-tunnel-independent-loopback.netdev b/test/test-network/conf/25-ipip-tunnel-independent-loopback.netdev
new file mode 100644 (file)
index 0000000..9ee26c9
--- /dev/null
@@ -0,0 +1,10 @@
+[NetDev]
+Name=ipiptun99
+Kind=ipip
+MTUBytes=1480
+
+[Tunnel]
+Local=192.168.223.238
+Remote=192.169.224.239
+Independent=true
+AssignToLoopback=yes
index d90802f44f22e00f0502f78fdfee1ac1c92d8baf..02dbd3843bf4732b8d2eff8b9ea28fdd8589be1d 100644 (file)
@@ -1,6 +1,9 @@
 [Match]
 Name=dummy98
 
+[Network]
+IPv6AcceptRA=no
+
 [Neighbor]
 Address=192.168.10.1
 MACAddress=00:00:5e:00:02:65
diff --git a/test/test-network/conf/25-sit-tunnel-any-any.netdev b/test/test-network/conf/25-sit-tunnel-any-any.netdev
new file mode 100644 (file)
index 0000000..e961dcb
--- /dev/null
@@ -0,0 +1,7 @@
+[NetDev]
+Name=sittun96
+Kind=sit
+
+[Tunnel]
+Local=any
+Remote=any
index e52078e0d4cf7e2e2ae2539ce29300068a79378b..c4c5fbc12945eb42a9771a470e19ef2817d2c5f0 100644 (file)
@@ -4,3 +4,15 @@ Name=dummy98
 [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
diff --git a/test/test-network/conf/25-tunnel-any-any.network b/test/test-network/conf/25-tunnel-any-any.network
new file mode 100644 (file)
index 0000000..7a9e39e
--- /dev/null
@@ -0,0 +1,8 @@
+[Match]
+Name=*tun96
+
+[Network]
+IPv6AcceptRA=no
+Address=2001:db8:0:f102::19/64
+Address=10.3.2.6/16
+LinkLocalAddressing=yes
diff --git a/test/test-network/conf/25-vti-tunnel-any-any.netdev b/test/test-network/conf/25-vti-tunnel-any-any.netdev
new file mode 100644 (file)
index 0000000..3cac374
--- /dev/null
@@ -0,0 +1,7 @@
+[NetDev]
+Name=vtitun96
+Kind=vti
+
+[Tunnel]
+Local=any
+Remote=any
diff --git a/test/test-network/conf/25-xfrm-independent.netdev b/test/test-network/conf/25-xfrm-independent.netdev
new file mode 100644 (file)
index 0000000..8ed4321
--- /dev/null
@@ -0,0 +1,6 @@
+[NetDev]
+Kind=xfrm
+Name=xfrm99
+
+[Xfrm]
+Independent=yes
diff --git a/test/test-network/conf/25-xfrm.netdev b/test/test-network/conf/25-xfrm.netdev
new file mode 100644 (file)
index 0000000..81b32de
--- /dev/null
@@ -0,0 +1,3 @@
+[NetDev]
+Kind=xfrm
+Name=xfrm99
diff --git a/test/test-network/conf/26-bridge-vlan-master.network b/test/test-network/conf/26-bridge-vlan-master.network
new file mode 100644 (file)
index 0000000..8493e32
--- /dev/null
@@ -0,0 +1,8 @@
+[Match]
+Name=bridge99
+
+[Network]
+IPv6AcceptRA=false
+
+[BridgeVLAN]
+VLAN=4060-4094
diff --git a/test/test-network/conf/26-bridge-vlan-slave.network b/test/test-network/conf/26-bridge-vlan-slave.network
new file mode 100644 (file)
index 0000000..ba50508
--- /dev/null
@@ -0,0 +1,9 @@
+[Match]
+Name=test1
+
+[Network]
+IPv6AcceptRA=no
+Bridge=bridge99
+
+[BridgeVLAN]
+VLAN=4064-4094
index d4741d687dd2b92186672844ddf34ddaa4444916..9797e2edb613c5b5123455627918e49cb4aa3eb7 100644 (file)
@@ -6,6 +6,7 @@ Address=192.168.0.15/24
 Gateway=192.168.0.1
 IPv6AcceptRA=no
 IgnoreCarrierLoss=true
+ConfigureWithoutCarrier=true
 
 [RoutingPolicyRule]
 To=8.8.8.8
similarity index 65%
rename from test/test-network/conf/dhcp-client-critical-connection.network
rename to test/test-network/conf/dhcp-client-keep-configuration-dhcp-on-stop.network
index 0e65dec0ae983518082dcc4e20c504ca0d51b91a..e17c9854021e0261546ab08400db794bd12ef50d 100644 (file)
@@ -4,6 +4,4 @@ Name=veth99
 [Network]
 DHCP=ipv4
 IPv6AcceptRA=false
-
-[DHCP]
-CriticalConnection=true
+KeepConfiguration=dhcp-on-stop
diff --git a/test/test-network/conf/dhcp-client-keep-configuration-dhcp.network b/test/test-network/conf/dhcp-client-keep-configuration-dhcp.network
new file mode 100644 (file)
index 0000000..c43f78d
--- /dev/null
@@ -0,0 +1,7 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=false
+KeepConfiguration=dhcp
diff --git a/test/test-network/conf/dhcp-client-use-dns-ipv4-and-ra.network b/test/test-network/conf/dhcp-client-use-dns-ipv4-and-ra.network
new file mode 100644 (file)
index 0000000..ed59895
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+
+[DHCP]
+UseDNS=yes
+
+[IPv6AcceptRA]
+UseDNS=yes
diff --git a/test/test-network/conf/dhcp-client-use-dns-ipv4.network b/test/test-network/conf/dhcp-client-use-dns-ipv4.network
new file mode 100644 (file)
index 0000000..dbd0f7f
--- /dev/null
@@ -0,0 +1,14 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+
+[DHCPv4]
+UseDNS=yes
+
+[DHCPv6]
+UseDNS=no
+
+[IPv6AcceptRA]
+UseDNS=no
diff --git a/test/test-network/conf/dhcp-client-use-dns-no.network b/test/test-network/conf/dhcp-client-use-dns-no.network
new file mode 100644 (file)
index 0000000..891efdb
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=yes
+
+[DHCP]
+UseDNS=no
+
+[IPv6AcceptRA]
+UseDNS=no
diff --git a/test/test-network/conf/dhcp-client-use-dns-yes.network b/test/test-network/conf/dhcp-client-use-dns-yes.network
new file mode 100644 (file)
index 0000000..f38ffe3
--- /dev/null
@@ -0,0 +1,11 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=yes
+
+[DHCP]
+UseDNS=yes
+
+[IPv6AcceptRA]
+UseDNS=no
diff --git a/test/test-network/conf/dhcp-client-use-routes-no.network b/test/test-network/conf/dhcp-client-use-routes-no.network
new file mode 100644 (file)
index 0000000..0c97ef5
--- /dev/null
@@ -0,0 +1,18 @@
+[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
diff --git a/test/test-network/conf/dhcp-client-with-static-address.network b/test/test-network/conf/dhcp-client-with-static-address.network
new file mode 100644 (file)
index 0000000..b4e11be
--- /dev/null
@@ -0,0 +1,7 @@
+[Match]
+Name=veth99
+
+[Network]
+DHCP=ipv4
+IPv6AcceptRA=no
+Address=192.168.5.250
index 00bb03cab25d1c855da82d467ed92f30fd103ac5..5510b1c9b6544ebe4110255889fa58d94f8aec9c 100644 (file)
@@ -5,3 +5,4 @@ Name=dummy98
 Tunnel=gretun99
 Tunnel=gretun98
 Tunnel=gretun97
+Tunnel=gretun96
index 6d39bbd777d688beeef528af4b55df725af130be..8fbee98851ec185d1001d7d0194bbc0d2dbf9fd2 100644 (file)
@@ -5,3 +5,4 @@ Name=dummy98
 Tunnel=ip6gretun99
 Tunnel=ip6gretun98
 Tunnel=ip6gretun97
+Tunnel=ip6gretun96
index ec6c9581145143800a098fac38119af6b30ad866..ea4b3a13533604c84ec67067f565d41a84d20b00 100644 (file)
@@ -5,3 +5,4 @@ Name=dummy98
 Tunnel=ipiptun99
 Tunnel=ipiptun98
 Tunnel=ipiptun97
+Tunnel=ipiptun96
index b76bfb3bd3bfcdf8509d205fa45068c80e35f6fb..d7dfa7ea97b6338a1948d120ac9068e7c2aa0972 100644 (file)
@@ -12,6 +12,7 @@ Name=vrf99
 Name=geneve99
 Name=ipiptun99
 Name=nlmon99
+Name=xfrm99
 
 [Network]
 LinkLocalAddressing=yes
index 8d97823a5ad5eabea50486669fdcac57822a3793..79909fcd6befd2c1a1d4ff77e96cd0dea06e1d78 100644 (file)
@@ -5,3 +5,4 @@ Name=dummy98
 Tunnel=sittun99
 Tunnel=sittun98
 Tunnel=sittun97
+Tunnel=sittun96
index 1e0b8405c5c61d6c3b0e2f6678425ba2f6446e58..761362e48221daeb5493a518e9d0d2049d8ffa6b 100644 (file)
@@ -5,3 +5,4 @@ Name=dummy98
 Tunnel=vtitun99
 Tunnel=vtitun98
 Tunnel=vtitun97
+Tunnel=vtitun96
diff --git a/test/test-network/conf/xfrm.network b/test/test-network/conf/xfrm.network
new file mode 100644 (file)
index 0000000..bfb2956
--- /dev/null
@@ -0,0 +1,6 @@
+[Match]
+Name=dummy98
+
+[Network]
+IPv6AcceptRA=no
+Xfrm=xfrm99
index d9242cb1162bd4452d4dd4da827e57ce0e010e84..6344b3d87ebf22b327ecbb037d6a97c285ce2153 100755 (executable)
@@ -7,7 +7,6 @@ import os
 import re
 import shutil
 import signal
-import socket
 import subprocess
 import sys
 import time
@@ -24,19 +23,35 @@ dnsmasq_pid_file='/run/networkd-ci/test-test-dnsmasq.pid'
 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):
@@ -48,9 +63,9 @@ def expectedFailureIfModuleIsNotAvailable(module_name):
 
 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)
@@ -59,9 +74,9 @@ def expectedFailureIfERSPANModuleIsNotAvailable():
 
 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)
@@ -70,24 +85,24 @@ def expectedFailureIfRoutingPolicyPortRangeIsNotAvailable():
 
 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
@@ -103,8 +118,9 @@ def setUpModule():
     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]',
@@ -135,168 +151,209 @@ def setUpModule():
     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:
@@ -318,79 +375,97 @@ class NetworkctlTests(unittest.TestCase, Utilities):
     ]
 
     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',
@@ -408,6 +483,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         'gretun99',
         'ip6gretap98',
         'ip6gretap99',
+        'ip6gretun96',
         'ip6gretun97',
         'ip6gretun98',
         'ip6gretun99',
@@ -438,19 +514,23 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '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',
@@ -461,6 +541,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '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',
@@ -473,18 +554,23 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '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',
@@ -495,6 +581,7 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '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',
@@ -506,9 +593,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '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',
@@ -520,6 +609,8 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '25-wireguard-private-key.txt',
         '25-wireguard.netdev',
         '25-wireguard.network',
+        '25-xfrm.netdev',
+        '25-xfrm-independent.netdev',
         '6rd.network',
         'erspan.network',
         'gre.network',
@@ -540,89 +631,104 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         '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')
@@ -631,12 +737,12 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         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')
 
@@ -645,15 +751,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
             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 + ' ')
 
@@ -662,19 +768,19 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
             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 + ' ')
@@ -685,15 +791,15 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
             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)
 
@@ -703,112 +809,112 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
             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')
@@ -816,59 +922,72 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         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.
 
@@ -876,32 +995,36 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         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')
@@ -910,127 +1033,135 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         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')
@@ -1040,10 +1171,34 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         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):
@@ -1051,39 +1206,39 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         # 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')
@@ -1096,26 +1251,26 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         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')
@@ -1131,10 +1286,10 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities):
         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):
 
@@ -1154,22 +1309,24 @@ 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")
@@ -1177,13 +1334,13 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities):
         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")
@@ -1191,24 +1348,24 @@ class NetworkdL2TPTests(unittest.TestCase, Utilities):
 
     @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")
@@ -1225,6 +1382,7 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         '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',
@@ -1250,23 +1408,25 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
     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')
@@ -1276,19 +1436,19 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         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')
@@ -1298,38 +1458,34 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         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')
@@ -1339,33 +1495,32 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         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')
@@ -1376,13 +1531,11 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
 
     @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')
@@ -1390,20 +1543,19 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         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')
@@ -1411,20 +1563,20 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         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')
 
@@ -1432,80 +1584,67 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
         # 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
 
@@ -1534,120 +1673,152 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities):
             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',
@@ -1661,59 +1832,53 @@ class NetworkdBondTests(unittest.TestCase, Utilities):
         '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')
 
@@ -1721,22 +1886,22 @@ class NetworkdBondTests(unittest.TestCase, Utilities):
         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')
@@ -1745,9 +1910,9 @@ class NetworkdBondTests(unittest.TestCase, Utilities):
         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?
@@ -1766,155 +1931,145 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities):
         '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']
 
@@ -1924,18 +2079,20 @@ class NetworkdLLDPTests(unittest.TestCase, Utilities):
         '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')
@@ -1949,19 +2106,21 @@ class NetworkdRATests(unittest.TestCase, Utilities):
         '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')
 
@@ -1976,19 +2135,21 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
         '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')
@@ -1996,12 +2157,12 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities):
         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.*')
@@ -2017,93 +2178,102 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         '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')
@@ -2111,129 +2281,239 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
 
         # 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::')
@@ -2243,59 +2523,62 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         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')
@@ -2305,98 +2588,95 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
         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
@@ -2405,18 +2685,18 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
                 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
@@ -2427,19 +2707,94 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities):
 
         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')
@@ -2448,18 +2803,27 @@ if __name__ == '__main__':
     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
@@ -2469,9 +2833,13 @@ if __name__ == '__main__':
 
     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:
index b37b2e975f5e22a7b9ffe88e1a465f4177cdae9f..85ec41b64135d3c5e09365aa3e567b056403b590 100755 (executable)
@@ -1713,6 +1713,12 @@ sub run_test {
 
 }
 
+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)) {
@@ -1729,11 +1735,13 @@ if ($? >> 8 == 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);
 }
 
@@ -1776,10 +1784,7 @@ if ($list[0]) {
 
 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);
index 0080b065d89cf201f06a2546ca782d1c393f67bd..3fe7e1a8031b48468d7c8fea9cb6c534f0c6ef5d 100755 (executable)
@@ -9,7 +9,7 @@ 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
+sudo apt-get install -y ninja-build python3-pip python3-setuptools quota
 pip3 install meson
 
 cd $REPO_ROOT
diff --git a/travis-ci/managers/fuzzit.sh b/travis-ci/managers/fuzzit.sh
new file mode 100755 (executable)
index 0000000..313e6c6
--- /dev/null
@@ -0,0 +1,78 @@
+#!/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}
index 475a1510aa20ef3bc0faa3769e54706bd78f7316..076e8f9325909f5c6652a467fe43377e60885eea 100755 (executable)
@@ -32,6 +32,7 @@ PACKAGES=(cryptsetup-bin
           python3-pyparsing
           python3-setuptools
           qemu-system-x86
+          quota
           strace
           unifont
           util-linux)
diff --git a/units/halt-local.service.in b/units/halt-local.service.in
deleted file mode 100644 (file)
index f6c2e9c..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-#  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
index fab405502a04e39c18ba63137886bae3f333bf4b..323334f6a3987431d706467991fb8d5f35007edc 100644 (file)
@@ -17,6 +17,7 @@ Before=sysinit.target
 
 [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
index 3eef95c6614f03fdbe25b16344251ef5d3ac4837..1b37290d4f5a2ad3853434e550429149407e87f5 100644 (file)
@@ -22,6 +22,11 @@ After=dbus.socket
 [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
@@ -43,6 +48,7 @@ RestrictRealtime=yes
 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
index 2c74da6f1ede5774b2fdbaaa08d64bf046202a3c..9ea3bb914efbd2a4470d084ece0766b9e2098873 100644 (file)
@@ -21,6 +21,7 @@ Wants=network.target
 [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
index a8eab94d02e524738f58210bf200afee034e6033..c88d3597b7a47ac3f40cbc12edad4d21e15914ac 100644 (file)
@@ -22,6 +22,7 @@ ProtectHostname=yes
 RestrictRealtime=yes
 RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
 SystemCallFilter=@system-service @mount
+SystemCallErrorNumber=EPERM
 SystemCallArchitectures=native
 LockPersonality=yes
 IPAddressDeny=any
index df546f471faa6ed9d424bac5e418f03776fdb4d7..d430ee201754a10195be466bc7b8e9f2c3b5a5d3 100644 (file)
@@ -15,6 +15,7 @@ Documentation=https://www.freedesktop.org/wiki/Software/systemd/timedated
 [Service]
 BusName=org.freedesktop.timedate1
 CapabilityBoundingSet=CAP_SYS_TIME
+DeviceAllow=char-rtc r
 ExecStart=@rootlibexecdir@/systemd-timedated
 IPAddressDeny=any
 LockPersonality=yes