From: Zbigniew Jędrzejewski-Szmek Date: Sat, 15 Jun 2019 15:50:37 +0000 (+0200) Subject: Merge pull request #12753 from jrouleau/fix/hibernate-resume-timeout X-Git-Tag: v243-rc1~283 X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fsystemd.git;a=commitdiff_plain;h=4b381a9ef65d68dc79760b093436a9c81f43fa5d;hp=8b6805a25bd6e2434bc402bff52656ae13216d21 Merge pull request #12753 from jrouleau/fix/hibernate-resume-timeout hibernate-resume: fix resume device timeout --- diff --git a/.travis.yml b/.travis.yml index ab6fe298c76..dd34e9c82ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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: diff --git a/NEWS b/NEWS index 979ee4b5e55..e5a66126479 100644 --- a/NEWS +++ b/NEWS @@ -70,6 +70,24 @@ CHANGES WITH 243 in spe: build/man/man systemctl build/man/html systemd.index + * 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,7 +101,7 @@ 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. … @@ -106,6 +124,18 @@ CHANGES WITH 242: `SYSTEMD_LOG_LEVEL=debug udevadm test-builtin net_setup_link /sys/class/net/` 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 diff --git a/README.md b/README.md index 5ed7cfceac9..1c428b3088b 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ Count of open pull requests over time [![Semaphore CI Build Status](https://semaphoreci.com/api/v1/projects/28a5a3ca-3c56-4078-8b5e-7ed6ef912e14/443470/shields_badge.svg)](https://semaphoreci.com/systemd/systemd)
[![Coverity Scan Status](https://scan.coverity.com/projects/350/badge.svg)](https://scan.coverity.com/projects/350)
+[![Fuzzit Status](https://app.fuzzit.dev/badge?org_id=RxqRpGNXquIvqrmp4iJS&branch=master)](https://app.fuzzit.dev/admin/RxqRpGNXquIvqrmp4iJS/dashboard)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1369/badge)](https://bestpractices.coreinfrastructure.org/projects/1369)
[![Travis CI Build Status](https://travis-ci.org/systemd/systemd.svg?branch=master)](https://travis-ci.org/systemd/systemd)
[![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)
diff --git a/TODO b/TODO index 1b91b7c7995..57c07bea7ae 100644 --- a/TODO +++ b/TODO @@ -27,6 +27,13 @@ Features: * 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 diff --git a/docs/HACKING.md b/docs/HACKING.md index b14be721285..7dc1eb98cba 100644 --- a/docs/HACKING.md +++ b/docs/HACKING.md @@ -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/man/sd_bus_add_object_vtable.xml b/man/sd_bus_add_object_vtable.xml index 6cbb84e7ff8..1c222bc5f41 100644 --- a/man/sd_bus_add_object_vtable.xml +++ b/man/sd_bus_add_object_vtable.xml @@ -200,7 +200,7 @@ userdata parameter contains a pointer that will be passed to various callback functions. It may be specified as NULL if no value is necessary. - sd_bus_add_object_vtable() is similar to + sd_bus_add_fallback_vtable() is similar to sd_bus_add_object_vtable(), but is used to register "fallback" attributes. When looking for an attribute declaration, bus object paths registered with sd_bus_add_object_vtable() are checked first. If no match is found, the fallback diff --git a/man/sd_notify.xml b/man/sd_notify.xml index 00640cb290d..3046ca88ee7 100644 --- a/man/sd_notify.xml +++ b/man/sd_notify.xml @@ -200,7 +200,7 @@ 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 TimeoutStartSec=, RuntimeMaxSec=, + current state is beyond the original maximum times of TimeoutStartSec=, RuntimeMaxSec=, and TimeoutStopSec=. See systemd.service5 for effects on the service timeouts. diff --git a/man/systemctl.xml b/man/systemctl.xml index d991e979f15..5ebe1832bcd 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1341,7 +1341,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err indirect - The unit file itself is not enabled, but it has a non-empty Also= setting in the [Install] 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 DefaultInstance= is enabled. + The unit file itself is not enabled, but it has a non-empty Also= setting in the [Install] 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 DefaultInstance= is enabled. 0 diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index d94d6ac7153..22cd638f1f8 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -187,6 +187,12 @@ /dev/mapper/home and /dev/mapper/srv. Note that this might create conflicts if the same partition is listed in /etc/crypttab with a different device mapper device name. + + When systemd is running in the initrd the / partition may be encrypted in LUKS + format as well. In this case, a device mapper device is set up under the name /dev/mapper/root, + and a sysroot.mount is set up that mounts the device under /sysroot. + For more information, see bootup7. + Mount and automount units for the EFI System Partition (ESP) are generated on EFI systems. The ESP is mounted to /boot/ (except if an Extended Boot Loader partition exists, see diff --git a/man/systemd-veritysetup-generator.xml b/man/systemd-veritysetup-generator.xml index 305dda4b8e8..bcacd59cf96 100644 --- a/man/systemd-veritysetup-generator.xml +++ b/man/systemd-veritysetup-generator.xml @@ -28,12 +28,12 @@ Description systemd-veritysetup-generator 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 systemd-veritysetup@.service8 units as necessary. - Currently, only a single verity device may be se up with this generator, backing the root file system of the + Currently, only a single verity device may be set up with this generator, backing the root file system of the OS. systemd-veritysetup-generator implements @@ -61,7 +61,7 @@ roothash= 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 systemd.verity_root_data= and systemd.verity_root_hash=, 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 @@ systemd.verity_root_data= systemd.verity_root_hash= - 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 + 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 roothash= argument (see above). diff --git a/man/systemd.automount.xml b/man/systemd.automount.xml index 48deb0220e4..75302e07e9f 100644 --- a/man/systemd.automount.xml +++ b/man/systemd.automount.xml @@ -35,9 +35,9 @@ this unit type. See systemd.unit5 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. + configuration items are configured in the generic [Unit] and + [Install] sections. The automount specific configuration options + are configured in the [Automount] section. Automount units must be named after the automount directories they control. Example: the automount point /home/lennart must be configured in a unit file diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index 178dc188a5a..a72a33240d9 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -34,9 +34,9 @@ this unit type. See systemd.unit5 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. + configuration items are configured in the generic [Unit] and + [Install] sections. The mount specific configuration options are + configured in the [Mount] section. Additional options are listed in systemd.exec5, diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 695bcaa0a47..d832e68d71c 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -838,6 +838,22 @@ + + KeepConfiguration= + + Takes a boolean or one of static, dhcp-on-stop, + dhcp. When static, systemd-networkd + will not drop static addresses and routes on starting up process. When set to + dhcp-on-stop, systemd-networkd will not drop addresses + and routes on stopping the daemon. When dhcp, + 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 dhcp + implies dhcp-on-stop, and yes implies + dhcp and static. Defaults to + dhcp-on-stop. + + @@ -1381,17 +1397,6 @@ system. Defaults to no. - - CriticalConnection= - - 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. - - - ClientIdentifier= diff --git a/man/systemd.path.xml b/man/systemd.path.xml index de284d877e0..39cca8cf514 100644 --- a/man/systemd.path.xml +++ b/man/systemd.path.xml @@ -34,9 +34,9 @@ this unit type. See systemd.unit5 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. + configuration items are configured in the generic [Unit] and + [Install] sections. The path specific configuration options are + configured in the [Path] section. For each path file, a matching unit file must exist, describing the unit to activate when the path changes. By default, diff --git a/man/systemd.slice.xml b/man/systemd.slice.xml index 5019bf9976a..7157dfa32d3 100644 --- a/man/systemd.slice.xml +++ b/man/systemd.slice.xml @@ -55,9 +55,9 @@ systemd.unit5 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 [Unit] and [Install] sections. The slice specific configuration options are configured in - the [Slice] section. Currently, only generic resource control settings + the [Slice] section. Currently, only generic resource control settings as described in systemd.resource-control5 are allowed. diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 84faf89c2e6..60ea63f742a 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -35,9 +35,9 @@ this unit type. See systemd.unit5 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. + configuration items are configured in the generic [Unit] and + [Install] sections. The socket specific configuration options are + configured in the [Socket] section. Additional options are listed in systemd.exec5, diff --git a/man/systemd.swap.xml b/man/systemd.swap.xml index d2a9123b0b0..23547bb273c 100644 --- a/man/systemd.swap.xml +++ b/man/systemd.swap.xml @@ -37,9 +37,9 @@ this unit type. See systemd.unit5 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. + configuration items are configured in the generic [Unit] and + [Install] sections. The swap specific configuration options are + configured in the [Swap] section. Additional options are listed in systemd.exec5, diff --git a/man/systemd.target.xml b/man/systemd.target.xml index a706a4588af..3052b177864 100644 --- a/man/systemd.target.xml +++ b/man/systemd.target.xml @@ -34,8 +34,8 @@ This unit type has no specific options. See systemd.unit5 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 [Unit] and + [Install] sections. A separate [Target] section does not exist, since no target-specific options may be configured. Target units do not offer any additional functionality on diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index 13b9ed35d22..340286d9128 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -35,9 +35,9 @@ this unit type. See systemd.unit5 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. + configuration items are configured in the generic [Unit] and + [Install] sections. The timer specific configuration options are + configured in the [Timer] section. For each timer file, a matching unit file must exist, describing the unit to activate when the timer elapses. By diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in index 07db6d47ce0..67c1c2b327a 100644 --- a/shell-completion/zsh/_systemctl.in +++ b/shell-completion/zsh/_systemctl.in @@ -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/boot/bless-boot.c b/src/boot/bless-boot.c index b5d110f4224..f2d033fc407 100644 --- a/src/boot/bless-boot.c +++ b/src/boot/bless-boot.c @@ -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 }, {} }; diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index 55924adf069..e7ba8b4ab9f 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -1435,16 +1435,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 }, {} }; diff --git a/src/core/cgroup.c b/src/core/cgroup.c index a7263855dca..0c885d57440 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -1378,6 +1378,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) diff --git a/src/core/service.c b/src/core/service.c index 190d84e56a9..4b50d8d0297 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -603,7 +603,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; } diff --git a/src/journal/lookup3.c b/src/journal/lookup3.c index 102d2fee183..74c80b724df 100644 --- a/src/journal/lookup3.c +++ b/src/journal/lookup3.c @@ -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) { diff --git a/src/libsystemd/sd-bus/bus-common-errors.c b/src/libsystemd/sd-bus/bus-common-errors.c index ef53d0a450a..edd30bf84d8 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.c +++ b/src/libsystemd/sd-bus/bus-common-errors.c @@ -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), diff --git a/src/libsystemd/sd-bus/bus-common-errors.h b/src/libsystemd/sd-bus/bus-common-errors.h index 2544bdebc10..296579116c5 100644 --- a/src/libsystemd/sd-bus/bus-common-errors.h +++ b/src/libsystemd/sd-bus/bus-common-errors.h @@ -51,6 +51,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" diff --git a/src/libsystemd/sd-bus/test-bus-marshal.c b/src/libsystemd/sd-bus/test-bus-marshal.c index ade16e532df..d7d51ba9504 100644 --- a/src/libsystemd/sd-bus/test-bus-marshal.c +++ b/src/libsystemd/sd-bus/test-bus-marshal.c @@ -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; diff --git a/src/libudev/libudev-device-internal.h b/src/libudev/libudev-device-internal.h index 8a6e5a48f65..cd6c2a5bc57 100644 --- a/src/libudev/libudev-device-internal.h +++ b/src/libudev/libudev-device-internal.h @@ -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); diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c index 357adf69644..91dc910773c 100644 --- a/src/libudev/libudev-device.c +++ b/src/libudev/libudev-device.c @@ -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" @@ -36,6 +37,36 @@ * 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; +} diff --git a/src/libudev/libudev-enumerate.c b/src/libudev/libudev-enumerate.c index 80d5bafdf7d..a8b3f535724 100644 --- a/src/libudev/libudev-enumerate.c +++ b/src/libudev/libudev-enumerate.c @@ -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; } diff --git a/src/libudev/libudev-hwdb.c b/src/libudev/libudev-hwdb.c index ed755e5d3cc..5299e0a16f5 100644 --- a/src/libudev/libudev-hwdb.c +++ b/src/libudev/libudev-hwdb.c @@ -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); diff --git a/src/libudev/libudev-list-internal.h b/src/libudev/libudev-list-internal.h index 4e1632c78d6..a15b3853439 100644 --- a/src/libudev/libudev-list-internal.h +++ b/src/libudev/libudev-list-internal.h @@ -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); diff --git a/src/libudev/libudev-list.c b/src/libudev/libudev-list.c index 00fd58b9ea5..95a9942f64f 100644 --- a/src/libudev/libudev-list.c +++ b/src/libudev/libudev-list.c @@ -1,13 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include -#include -#include -#include - #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 @@ -23,209 +20,164 @@ * 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); } /** diff --git a/src/login/71-seat.rules.in b/src/login/71-seat.rules.in index b67966cdf6d..6010f048aef 100644 --- a/src/login/71-seat.rules.in +++ b/src/login/71-seat.rules.in @@ -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" diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 687a534f7b8..2ad9887066f 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -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; diff --git a/src/login/logind-action.c b/src/login/logind-action.c index 6c9366761da..4f97e0d9bb5 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -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" diff --git a/src/login/logind-brightness.c b/src/login/logind-brightness.c new file mode 100644 index 00000000000..8dfa97d7aed --- /dev/null +++ b/src/login/logind-brightness.c @@ -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 index 00000000000..b22ee37ba77 --- /dev/null +++ b/src/login/logind-brightness.h @@ -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); diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index be767186ff8..0e8925ab944 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -27,6 +27,10 @@ #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" @@ -46,47 +50,78 @@ #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 index 00000000000..6c73a9654f6 --- /dev/null +++ b/src/login/logind-dbus.h @@ -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); diff --git a/src/login/logind-device.c b/src/login/logind-device.c index e724365b131..20108544aad 100644 --- a/src/login/logind-device.c +++ b/src/login/logind-device.c @@ -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) { diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c index d427dcaa012..d963706dcef 100644 --- a/src/login/logind-inhibit.c +++ b/src/login/logind-inhibit.c @@ -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" diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c index 6ee5a1c95d2..c33a0e0ad43 100644 --- a/src/login/logind-seat-dbus.c +++ b/src/login/logind-seat-dbus.c @@ -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 index 00000000000..2590f64922b --- /dev/null +++ b/src/login/logind-seat-dbus.h @@ -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); diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index f5ffb68238e..dc578adf638 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -13,7 +13,9 @@ #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" diff --git a/src/login/logind-seat.h b/src/login/logind-seat.h index 6236f1360bc..64cdf2f25ae 100644 --- a/src/login/logind-seat.h +++ b/src/login/logind-seat.h @@ -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"); +} diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c index df5bfba9821..c297f62cdf9 100644 --- a/src/login/logind-session-dbus.c +++ b/src/login/logind-session-dbus.c @@ -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 index 00000000000..9d2315cc60d --- /dev/null +++ b/src/login/logind-session-dbus.h @@ -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); diff --git a/src/login/logind-session-device.c b/src/login/logind-session-device.c index 3e2ff6d5b8d..3057e72394d 100644 --- a/src/login/logind-session-device.c +++ b/src/login/logind-session-device.c @@ -7,14 +7,15 @@ #include #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 { diff --git a/src/login/logind-session.c b/src/login/logind-session.c index f1efeb0e017..17700c69219 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -22,7 +22,11 @@ #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" diff --git a/src/login/logind-session.h b/src/login/logind-session.h index f3c17a8d918..50fe29e7970 100644 --- a/src/login/logind-session.h +++ b/src/login/logind-session.h @@ -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"); +} diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index fcaeba13f6b..beb97362e73 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -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 index 00000000000..acfcb981cf3 --- /dev/null +++ b/src/login/logind-user-dbus.h @@ -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); diff --git a/src/login/logind-user.c b/src/login/logind-user.c index c5d442865cc..b17fb2e3225 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -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, diff --git a/src/login/logind-user.h b/src/login/logind-user.h index c41973e27dc..4bd65d83734 100644 --- a/src/login/logind-user.h +++ b/src/login/logind-user.h @@ -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); diff --git a/src/login/logind.c b/src/login/logind.c index 4c0e8ce6b1c..d60223db686 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -18,6 +18,10 @@ #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); @@ -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) diff --git a/src/login/logind.h b/src/login/logind.h index 7b6f73c6ec6..f260f2dc96d 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -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); diff --git a/src/login/meson.build b/src/login/meson.build index 1cc75fd1cfe..832274af787 100644 --- a/src/login/meson.build +++ b/src/login/meson.build @@ -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] diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index f3c13ad89a0..124a25810e3 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -302,6 +302,10 @@ send_interface="org.freedesktop.login1.Session" send_member="PauseDeviceComplete"/> + + diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index a26895e695e..cd070329f4c 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -271,7 +271,7 @@ static int append_session_memory_max(pam_handle_t *handle, sd_bus_message *m, co 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); } } @@ -294,7 +294,7 @@ static int append_session_tasks_max(pam_handle_t *handle, sd_bus_message *m, con 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; } diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c index 43e216acad0..4e9ab762e93 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -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; } diff --git a/src/network/networkctl.c b/src/network/networkctl.c index b265f1f0566..901e88cc98d 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -269,7 +269,7 @@ 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); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index d6c6f5f2715..e75a49f6149 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -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( diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 4ed020ea8e5..978378d7b82 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -13,6 +13,34 @@ #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 +49,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); } @@ -114,7 +148,7 @@ static int link_set_dhcp_routes(Link *link) { 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 +187,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 +203,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 +212,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 +245,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); @@ -216,13 +276,16 @@ static int dhcp_remove_routes(Link *link, struct in_addr *address) { route->table = table; route->scope = route_scope_from_address(route, address); + if (route_present_in_routes(route, new_routes, m)) + continue; + (void) route_remove(route, link, NULL); } return n; } -static int dhcp_remove_router(Link *link, struct in_addr *address) { +static int dhcp_remove_router(Link *link, sd_dhcp_lease *lease, struct in_addr *address) { _cleanup_(route_freep) Route *route_gw = NULL, *route = NULL; const struct in_addr *router; uint32_t table; @@ -234,7 +297,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 +341,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 +359,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 +428,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 +445,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; + } - link_set_dhcp_routes(link); + manager_rtnl_process_address(rtnl, m, link->manager); + + 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 +539,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 +580,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 +659,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 +749,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 +767,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 +780,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; diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index 4ae511fc7ae..e3333698f8a 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -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. */ diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 6613992a091..67c0903fa69 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -62,6 +62,20 @@ DUID* link_get_duid(Link *link) { return &link->manager->duid; } +int link_sysctl_ipv6_enabled(Link *link) { + _cleanup_free_ char *value = NULL; + int r; + + r = sysctl_read_ip_property(AF_INET6, link->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", + link->ifname); + + link->sysctl_ipv6_enabled = value[0] == '0'; + return link->sysctl_ipv6_enabled; +} + static bool link_dhcp6_enabled(Link *link) { assert(link); @@ -80,7 +94,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; @@ -166,7 +180,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 +195,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 +244,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; @@ -541,6 +555,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 +695,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,7 +749,7 @@ 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); } @@ -829,14 +847,14 @@ static int link_request_set_routing_policy_rule(Link *link) { 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 +928,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 +975,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); @@ -1191,8 +1212,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) { @@ -2485,6 +2506,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 +2544,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 +2566,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 +2640,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 +3327,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; diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index d0290f7c7d5..80fc4baee60 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -78,7 +78,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 +126,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*); @@ -167,6 +169,7 @@ int link_set_mtu(Link *link, uint32_t mtu); int ipv4ll_configure(Link *link); bool link_ipv4ll_enabled(Link *link, AddressFamilyBoolean mask); +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); @@ -175,7 +178,7 @@ 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_; @@ -191,6 +194,8 @@ 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, \ diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 6984c5b9679..92e3b0a0f12 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -1382,8 +1382,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; @@ -1447,7 +1445,7 @@ void manager_free(Manager *m) { 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 +1884,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; -} diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index c816d86d187..281e9cbac44 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -55,8 +55,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; @@ -100,6 +98,4 @@ 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); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index 3016f3448bd..0d2fde2f742 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -121,8 +121,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 +209,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 +259,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 +320,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; } diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 9ef07ea372b..2986ff1123d 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -87,6 +87,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 @@ -143,7 +144,7 @@ DHCP.Anonymize, config_parse_bool, 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.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical) /* deprecated */ 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) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index a5e7cad58a4..0741dfe646d 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -239,6 +239,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 +340,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, @@ -392,6 +409,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, }; @@ -1752,3 +1771,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); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index d2a0b8c5f14..7b92a544266 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -78,6 +78,17 @@ typedef enum RADVPrefixDelegation { _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; struct Network { @@ -119,7 +130,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; @@ -227,6 +238,7 @@ struct Network { bool unmanaged; bool configure_without_carrier; bool ignore_carrier_loss; + KeepConfiguration keep_configuration; uint32_t iaid; DUID duid; @@ -318,6 +330,7 @@ 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); +CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration); /* Legacy IPv4LL support */ CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll); @@ -336,3 +349,6 @@ 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_; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index b21e7dfd86f..cd31548f94e 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -500,6 +500,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 +693,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) { diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index f6253215ed8..e4dca830719 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -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); @@ -275,8 +277,8 @@ static int routing_policy_rule_add_internal(Manager *m, rule->tos = tos; rule->fwmark = fwmark; rule->table = table; - rule->iif = iif; - rule->oif = oif; + rule->iif = TAKE_PTR(iif); + rule->oif = TAKE_PTR(oif); rule->protocol = protocol; rule->sport = *sport; rule->dport = *dport; @@ -292,9 +294,7 @@ static int routing_policy_rule_add_internal(Manager *m, if (ret) *ret = rule; - rule = NULL; - iif = oif = NULL; - + TAKE_PTR(rule); return 0; } @@ -474,7 +474,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 +484,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"); @@ -593,7 +598,7 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netl 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) { diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index 4ee0b5489ef..0b3204ad9c2 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -57,7 +57,7 @@ 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, diff --git a/src/resolve/resolved-dnstls-openssl.c b/src/resolve/resolved-dnstls-openssl.c index f269e4d6487..5d922300758 100644 --- a/src/resolve/resolved-dnstls-openssl.c +++ b/src/resolve/resolved-dnstls-openssl.c @@ -6,6 +6,7 @@ #include #include +#include #include "io-util.h" #include "resolved-dns-stream.h" @@ -34,9 +35,11 @@ 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; if (stream->dnstls_data.write_buffer->length > 0) { + memmove(stream->dnstls_data.write_buffer->data, + stream->dnstls_data.write_buffer->data + ss, + stream->dnstls_data.write_buffer->length); stream->dnstls_events |= EPOLLOUT; return -EAGAIN; } diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index c2440271feb..81acff4602b 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1767,3 +1767,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, +}; diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index 59bfdb23981..3216b0c37a1 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -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/verbs.c b/src/shared/verbs.c index 7c5dcb02a24..c87b496736e 100644 --- a/src/shared/verbs.c +++ b/src/shared/verbs.c @@ -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 { diff --git a/src/shared/verbs.h b/src/shared/verbs.h index 010c0df3fdc..c5fe6cc7c58 100644 --- a/src/shared/verbs.h +++ b/src/shared/verbs.h @@ -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 { diff --git a/src/system-update-generator/system-update-generator.c b/src/system-update-generator/system-update-generator.c index 77e265d7107..6ec4986c10c 100644 --- a/src/system-update-generator/system-update-generator.c +++ b/src/system-update-generator/system-update-generator.c @@ -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"); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index a1ec281162b..1158914fb4f 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -1942,6 +1942,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); @@ -1952,7 +1953,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; } @@ -1991,9 +1992,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, @@ -2006,8 +2010,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) { @@ -8876,7 +8884,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 }, diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c index 7878512465e..dcb5bcc2544 100644 --- a/src/test/test-libudev.c +++ b/src/test/test-libudev.c @@ -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) { diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c index f9b3e95794a..82e25149325 100644 --- a/src/udev/udevadm-control.c +++ b/src/udev/udevadm-control.c @@ -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; diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index b7dafb77557..f14010a2d0d 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -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) { @@ -301,12 +304,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; diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index bba8948d352..496c52336cb 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -135,6 +135,7 @@ DHCPServer= BindCarrier= VRF= IgnoreCarrierLoss= +KeepConfiguration= [IPv6Prefix] Prefix= OnLink= 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 index 00000000000..0c65a1d9e2e --- /dev/null +++ b/test/test-network/conf/24-keep-configuration-static.network @@ -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 index 00000000000..c7d25832526 --- /dev/null +++ b/test/test-network/conf/25-bridge-configure-without-carrier.network @@ -0,0 +1,7 @@ +[Match] +Name=bridge99 + +[Network] +LinkLocalAddressing=yes +IPv6AcceptRA=no +ConfigureWithoutCarrier=yes diff --git a/test/test-network/conf/25-neighbor-section.network b/test/test-network/conf/25-neighbor-section.network index d90802f44f2..02dbd3843bf 100644 --- a/test/test-network/conf/25-neighbor-section.network +++ b/test/test-network/conf/25-neighbor-section.network @@ -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-sysctl-disable-ipv6.network b/test/test-network/conf/25-sysctl-disable-ipv6.network index e52078e0d4c..c4c5fbc1294 100644 --- a/test/test-network/conf/25-sysctl-disable-ipv6.network +++ b/test/test-network/conf/25-sysctl-disable-ipv6.network @@ -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/dhcp-client-critical-connection.network b/test/test-network/conf/dhcp-client-keep-configuration-dhcp-on-stop.network 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 0e65dec0ae9..e17c9854021 100644 --- a/test/test-network/conf/dhcp-client-critical-connection.network +++ b/test/test-network/conf/dhcp-client-keep-configuration-dhcp-on-stop.network @@ -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 index 00000000000..c43f78d1dea --- /dev/null +++ b/test/test-network/conf/dhcp-client-keep-configuration-dhcp.network @@ -0,0 +1,7 @@ +[Match] +Name=veth99 + +[Network] +DHCP=ipv4 +IPv6AcceptRA=false +KeepConfiguration=dhcp diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 3311bf86e93..5e948623677 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -7,7 +7,6 @@ import os import re import shutil import signal -import socket import subprocess import sys import time @@ -33,10 +32,23 @@ 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 +60,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 +71,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,9 +82,9 @@ 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) @@ -82,12 +94,12 @@ def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable(): def expectedFailureIfEthtoolDoesNotSupportDriver(): 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) + ret = run('udevadm info -w10s /sys/class/net/dummy99', stdout=subprocess.PIPE, stderr=subprocess.STDOUT) if ret.returncode == 0 and 'E: ID_NET_DRIVER=dummy' in ret.stdout.rstrip(): support = True - subprocess.call(['ip', 'link', 'del', 'dummy99']) + call('ip link del dummy99') if support: return func @@ -103,8 +115,8 @@ 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') drop_in = [ '[Service]', @@ -135,168 +147,166 @@ 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) + check_output('systemctl daemon-reload') + print(check_output('systemctl cat systemd-networkd.service')) def tearDownModule(): shutil.rmtree(networkd_ci_path) - subprocess.check_call('systemctl stop systemd-networkd.service', shell=True) + check_output('systemctl stop systemd-networkd.service') shutil.rmtree('/run/systemd/system/systemd-networkd.service.d') - subprocess.check_call('systemctl daemon-reload', shell=True) + check_output('systemctl daemon-reload') + + check_output('systemctl start systemd-networkd.socket') + check_output('systemctl start systemd-networkd.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: + 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 l2tp_tunnel_remove(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 start_networkd(sleep_sec=5, remove_state_files=True): + if (remove_state_files and + os.path.exists(os.path.join(networkd_runtime_directory, 'state'))): + check_output('systemctl stop systemd-networkd') + os.remove(os.path.join(networkd_runtime_directory, 'state')) + check_output('systemctl start systemd-networkd') + else: + check_output('systemctl restart systemd-networkd') + if sleep_sec > 0: + time.sleep(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,75 +328,75 @@ class NetworkctlTests(unittest.TestCase, Utilities): ] def setUp(self): - self.remove_links(self.links) + remove_links(self.links) 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) 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(0) - 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(0) - 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) + copy_unit_to_networkd_unit_path('11-dummy.netdev', '11-dummy.network', + '25-veth.netdev', 'netdev-link-local-addressing-yes.network') + start_networkd(0) - self.wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer:degraded']) + wait_online(['test1:degraded', 'veth99:degraded', 'veth-peer: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, 'Driver: dummy') - output = subprocess.check_output(networkctl_cmd + ['status', 'veth99'], universal_newlines=True, env=env).rstrip() + output = check_output(*networkctl_cmd, 'status', 'veth99', env=env) self.assertRegex(output, 'Driver: veth') - output = subprocess.check_output(networkctl_cmd + ['status', 'veth-peer'], universal_newlines=True, env=env).rstrip() + output = check_output(*networkctl_cmd, 'status', 'veth-peer', env=env) self.assertRegex(output, 'Driver: veth') 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(0) - 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): @@ -461,6 +471,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', @@ -547,82 +558,82 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): '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) 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_unit_from_networkd_path(self.units) 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(0) - 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_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(0) - 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(0) - 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(0) + + 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(0) - 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 +642,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 +656,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(0) - 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 +673,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(0) - 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 +696,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(0) + 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 +714,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(0) + 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(0) - 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(0) - 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(0) - 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(0) - 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(0) - 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(0) - 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(0) + 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(0) - 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,46 +827,46 @@ 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') + start_networkd(0) + wait_online(['ipiptun99:routable', 'ipiptun98:routable', 'ipiptun97: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') 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') + start_networkd(0) + wait_online(['gretun99:routable', 'gretun98:routable', 'gretun97: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') @@ -864,11 +875,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 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') + start_networkd(5) # Old kernels seem not to support IPv6LL address on ip6gre tunnel, So please do not use wait_online() here. @@ -877,31 +888,31 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.check_link_exists('ip6gretun98') self.check_link_exists('ip6gretun97') - 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') 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(0) + 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 +921,127 @@ 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(0) + 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') + start_networkd(0) + wait_online(['vtitun99:routable', 'vtitun98:routable', 'vtitun97: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') 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(0) + 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(0) + 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') + start_networkd(0) + wait_online(['sittun99:routable', 'sittun98:routable', 'sittun97: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") 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(0) + 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(0) + 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(0) + 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 +1051,10 @@ 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(0) - self.wait_online(['ipiptun99:carrier']) + wait_online(['ipiptun99:carrier']) @expectedFailureIfModuleIsNotAvailable('fou') def test_fou(self): @@ -1051,39 +1062,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(0) - 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(0) - 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 +1107,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(0) - 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 +1142,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(0) - self.wait_online(['nlmon99:carrier']) + wait_online(['nlmon99:carrier']) class NetworkdL2TPTests(unittest.TestCase, Utilities): @@ -1154,22 +1165,22 @@ 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) + l2tp_tunnel_remove(self.l2tp_tunnel_ids) + remove_links(self.links) def tearDown(self): - self.l2tp_tunnel_remove(self.l2tp_tunnel_ids) - self.remove_links(self.links) - self.remove_unit_from_networkd_path(self.units) + l2tp_tunnel_remove(self.l2tp_tunnel_ids) + remove_links(self.links) + remove_unit_from_networkd_path(self.units) @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(0) - 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 +1188,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 +1202,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(0) - 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 +1236,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 +1262,23 @@ 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) 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) 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(0) - 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 +1288,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 +1310,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(0) + 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(0) + 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 +1347,30 @@ 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)) - - self.check_link_exists('test1') - self.check_link_exists('dummy98') + start_networkd(0, remove_state_files=(trial == 0)) + wait_online(['test1:degraded', 'dummy98:degraded']) + time.sleep(1) - 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') @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(0) + 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 +1381,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(0) + 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 +1393,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(0) + 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 +1413,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 +1434,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() - - self.check_link_exists('dummy98') - self.check_link_exists('bond199') + 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(0) + wait_online(['dummy98:enslaved', 'bond199:routable']) - 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() + copy_unit_to_networkd_unit_path('25-address-link-section.network', '12-dummy.netdev') + start_networkd(0) + wait_online(['dummy98:degraded']) - self.check_link_exists('dummy98') - - 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() + copy_unit_to_networkd_unit_path('25-ipv6-address-label-section.network', '12-dummy.netdev') + start_networkd(0) + wait_online(['dummy98:degraded']) - self.check_link_exists('dummy98') - - 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() - - self.check_link_exists('dummy98') + copy_unit_to_networkd_unit_path('25-neighbor-section.network', '12-dummy.netdev') + start_networkd(0) + wait_online(['dummy98:degraded'], timeout='40s') - 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(0) + 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 +1523,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(0) + 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(0) + 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']) + start_networkd(0) + 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(0) + 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(0) + 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(0) + 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', @@ -1668,52 +1689,52 @@ class NetworkdBondTests(unittest.TestCase, Utilities): 'bond-slave.network'] def setUp(self): - self.remove_links(self.links) + remove_links(self.links) 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) 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() + copy_unit_to_networkd_unit_path('23-active-slave.network', '23-bond199.network', '25-bond-active-backup-slave.netdev', '12-dummy.netdev') + start_networkd() self.check_link_exists('dummy98') 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, '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-test1-bond199.network', '25-bond-active-backup-slave.netdev', '11-dummy.netdev') + start_networkd() 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') 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() + copy_unit_to_networkd_unit_path('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev', + 'bond99.network','bond-slave.network') + start_networkd() self.check_link_exists('bond99') self.check_link_exists('dummy98') self.check_link_exists('test1') - 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 +1742,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 +1766,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? @@ -1770,150 +1791,149 @@ class NetworkdBridgeTests(unittest.TestCase, Utilities): 'bridge99.network'] def setUp(self): - self.remove_links(self.links) + remove_links(self.links) 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) 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() + 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() self.check_link_exists('dummy98') self.check_link_exists('test1') self.check_link_exists('bridge99') - 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']) + 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') + call('ip rule del table 100') - self.start_networkd() + start_networkd() self.check_link_exists('dummy98') self.check_link_exists('test1') self.check_link_exists('bridge99') - 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']) + 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') + 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']) + call('ip rule del table 100') - self.start_networkd() + 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) + check_output('ip link add dummy98 type dummy') + check_output('ip link set dummy98 up') + 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) - self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0) + check_output('ip link add dummy98 type dummy') + check_output('ip link set dummy98 up') + 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) - self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0) + check_output('ip link add dummy98 type dummy') + check_output('ip link set dummy98 up') + 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) + check_output('ip link add dummy98 type dummy') + check_output('ip link set dummy98 up') for trial in range(30): if trial > 0: time.sleep(1) - if self.get_operstate('bridge99') == 'routable' and self.get_operstate('dummy98') == 'enslaved': + if get_operstate('bridge99') == 'routable' and 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']) + call('ip rule del table 100') class NetworkdLLDPTests(unittest.TestCase, Utilities): links = ['veth99'] @@ -1924,18 +1944,18 @@ class NetworkdLLDPTests(unittest.TestCase, Utilities): '25-veth.netdev'] def setUp(self): - self.remove_links(self.links) + remove_links(self.links) 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) 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(0) + 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 +1969,19 @@ class NetworkdRATests(unittest.TestCase, Utilities): 'ipv6-prefix-veth.network'] def setUp(self): - self.remove_links(self.links) + remove_links(self.links) 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) 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(0) + 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 +1996,19 @@ class NetworkdDHCPServerTests(unittest.TestCase, Utilities): 'dhcp-server-timezone-router.network'] def setUp(self): - self.remove_links(self.links) + remove_links(self.links) 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) 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(0) + 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 +2016,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(0) + 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,13 +2037,14 @@ 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', @@ -2036,74 +2057,74 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): '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) 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) 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(0) + 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(0) + 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(0) + 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 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(0) + 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 +2132,187 @@ 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(0) + 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(0) + 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(0) + 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(0) + 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 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_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(0) + 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(0) + 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_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(0) + 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'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(0) + 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.*') + + start_networkd(0) + 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(0) + 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 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,46 +2322,46 @@ 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']) + start_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(0) + 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 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 = subprocess.check_output(['ip', 'address', 'show', 'vrf', 'vrf99'], universal_newlines=True).rstrip() + 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') @@ -2290,7 +2369,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): 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.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') @@ -2298,7 +2377,7 @@ class NetworkdDHCPClientTests(unittest.TestCase, Utilities): 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') @@ -2308,98 +2387,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(0) + 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(0) + 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(0) + 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(0) + 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 @@ -2408,18 +2484,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 @@ -2430,7 +2506,7 @@ 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') diff --git a/travis-ci/managers/fuzzit.sh b/travis-ci/managers/fuzzit.sh new file mode 100755 index 00000000000..71858b4be87 --- /dev/null +++ b/travis-ci/managers/fuzzit.sh @@ -0,0 +1,79 @@ +#!/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 that's how the fuzzers are built on OSS-Fuzz: https://github.com/google/oss-fuzz/blob/a3c935fe9ca7f82bafa520731525e1cc38acf650/infra/base-images/base-builder/Dockerfile#L33-L34 +# and we know they don't fail there. We can turn on everything else later after the issues mentioned in +# https://github.com/systemd/systemd/pull/12771#issuecomment-502139157 are sorted out at least. +# TODO: "null" should probably be added too. On OSS-Fuzz it was turned off in https://github.com/google/oss-fuzz/pull/674 +# TODO: figure out what to do about unsigned-integer-overflow: https://github.com/google/oss-fuzz/issues/910 +export SANITIZER="address -fsanitize=bool,array-bounds,float-divide-by-zero,function,integer-divide-by-zero,return,shift,signed-integer-overflow,unsigned-integer-overflow,vla-bound,vptr -fno-sanitize-recover=bool,array-bounds,float-divide-by-zero,function,integer-divide-by-zero,return,shift,signed-integer-overflow,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} diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in index 3eef95c6614..8a7262776f9 100644 --- a/units/systemd-logind.service.in +++ b/units/systemd-logind.service.in @@ -43,6 +43,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