diff --git a/.clang-format b/.clang-format index c94866fcd..6aa4e6a45 100644 --- a/.clang-format +++ b/.clang-format @@ -1,7 +1,7 @@ # This configuration file can be used to auto-format the code base. # Not all guidelines specified in CODING_STYLE are followed, so the # result MUST NOT be committed indiscriminately, but each automated -# change should be reviewed and only the appropriate ones commited. +# change should be reviewed and only the appropriate ones committed. # # The easiest way to apply the formatting to your changes ONLY, # is to use the git-clang-format script (usually installed with clang-format). diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile new file mode 100644 index 000000000..47f238c78 --- /dev/null +++ b/.clusterfuzzlite/Dockerfile @@ -0,0 +1,5 @@ +FROM gcr.io/oss-fuzz-base/base-builder@sha256:14b332de0e18683f37386eaedbf735bc6e8d81f9c0e1138d620f2178e20cd30a +ENV MERGE_WITH_OSS_FUZZ_CORPORA=yes +COPY . $SRC/systemd +WORKDIR $SRC/systemd +COPY tools/oss-fuzz.sh $SRC/build.sh diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index 8eb40d433..592833e24 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -10,7 +10,7 @@ SPDX-License-Identifier: LGPL-2.1-or-later - + **Used distribution** diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a23d6374c..3e067c176 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,10 +5,15 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "weekly" + interval: "monthly" open-pull-requests-limit: 2 - package-ecosystem: "pip" directory: "/.github/workflows" schedule: interval: "monthly" open-pull-requests-limit: 2 + - package-ecosystem: "docker" + directory: "/.clusterfuzzlite" + schedule: + interval: "monthly" + open-pull-requests-limit: 2 diff --git a/.github/workflows/build_test.sh b/.github/workflows/build_test.sh index 5a173a18d..7a27a5f9f 100755 --- a/.github/workflows/build_test.sh +++ b/.github/workflows/build_test.sh @@ -9,9 +9,8 @@ success() { echo >&2 -e "\033[32;1m$1\033[0m"; } ARGS=( "--optimization=0" - "--optimization=2" "--optimization=s" - "--optimization=3 -Db_lto=true" + "--optimization=3 -Db_lto=true -Ddns-over-tls=false" "--optimization=3 -Db_lto=false" "--optimization=3 -Ddns-over-tls=openssl" "--optimization=3 -Dfexecve=true -Dstandalone-binaries=true -Dstatic-libsystemd=true -Dstatic-libudev=true" @@ -63,6 +62,7 @@ PACKAGES=( COMPILER="${COMPILER:?}" COMPILER_VERSION="${COMPILER_VERSION:?}" LINKER="${LINKER:?}" +CRYPTOLIB="${CRYPTOLIB:?}" RELEASE="$(lsb_release -cs)" bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ $RELEASE main restricted universe multiverse' >>/etc/apt/sources.list" @@ -117,18 +117,28 @@ ninja --version for args in "${ARGS[@]}"; do SECONDS=0 + # meson fails with + # src/boot/efi/meson.build:52: WARNING: Not using lld as efi-ld, falling back to bfd + # src/boot/efi/meson.build:52:16: ERROR: Fatal warnings enabled, aborting + # when LINKER is set to lld so let's just not turn meson warnings into errors with lld + # to make sure that the build systemd can pick up the correct efi-ld linker automatically. + if [[ "$LINKER" != lld ]]; then + additional_meson_args="--fatal-meson-warnings" + fi info "Checking build with $args" # shellcheck disable=SC2086 if ! AR="$AR" \ CC="$CC" CC_LD="$LINKER" CFLAGS="-Werror" \ CXX="$CXX" CXX_LD="$LINKER" CXXFLAGS="-Werror" \ meson -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true --werror \ - $args build; then + -Dnobody-group=nogroup $additional_meson_args \ + -Dcryptolib="${CRYPTOLIB:?}" $args build; then + cat build/meson-logs/meson-log.txt fatal "meson failed with $args" fi - if ! meson compile -C build; then + if ! meson compile -C build -v; then fatal "'meson compile' failed with $args" fi diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml index 69487fa59..f0b9fe495 100644 --- a/.github/workflows/build_test.yml +++ b/.github/workflows/build_test.yml @@ -19,20 +19,20 @@ jobs: build: runs-on: ubuntu-20.04 concurrency: - group: ${{ github.workflow }}-${{ matrix.env.COMPILER }}-${{ matrix.env.COMPILER_VERSION }}-${{ matrix.env.LINKER }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ toJSON(matrix.env) }}-${{ github.ref }} cancel-in-progress: true strategy: fail-fast: false matrix: env: - - { COMPILER: "gcc", COMPILER_VERSION: "10", LINKER: "bfd" } - - { COMPILER: "gcc", COMPILER_VERSION: "11", LINKER: "gold" } - - { COMPILER: "clang", COMPILER_VERSION: "11", LINKER: "bfd" } - - { COMPILER: "clang", COMPILER_VERSION: "12", LINKER: "gold" } - - { COMPILER: "clang", COMPILER_VERSION: "13", LINKER: "lld" } + - { COMPILER: "gcc", COMPILER_VERSION: "10", LINKER: "bfd", CRYPTOLIB: "gcrypt" } + - { COMPILER: "gcc", COMPILER_VERSION: "11", LINKER: "gold", CRYPTOLIB: "openssl" } + - { COMPILER: "clang", COMPILER_VERSION: "11", LINKER: "bfd", CRYPTOLIB: "auto" } + - { COMPILER: "clang", COMPILER_VERSION: "12", LINKER: "gold", CRYPTOLIB: "gcrypt" } + - { COMPILER: "clang", COMPILER_VERSION: "13", LINKER: "lld", CRYPTOLIB: "openssl" } env: ${{ matrix.env }} steps: - name: Repository checkout uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 - - name: Build check (${{ env.COMPILER }}-${{ env.COMPILER_VERSION }}-${{ env.LINKER }}) + - name: ${{ format('Build check ({0}-{1}-{2}-{3})', env.COMPILER, env.COMPILER_VERSION, env.LINKER, env.CRYPTOLIB) }} run: sudo -E .github/workflows/build_test.sh diff --git a/.github/workflows/cflite_pr.yml b/.github/workflows/cflite_pr.yml new file mode 100644 index 000000000..3fe2bac61 --- /dev/null +++ b/.github/workflows/cflite_pr.yml @@ -0,0 +1,39 @@ +--- +# vi: ts=2 sw=2 et: +# SPDX-License-Identifier: LGPL-2.1-or-later +# +name: ClusterFuzzLite PR fuzzing +on: + pull_request: + branches: + - main + - v[0-9]+-stable + +permissions: read-all + +jobs: + PR: + runs-on: ubuntu-latest + if: github.repository != 'systemd/systemd' || github.event.pull_request.user.login == 'dependabot[bot]' + concurrency: + group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }} + cancel-in-progress: true + strategy: + fail-fast: false + matrix: + sanitizer: [address, undefined, memory] + steps: + - name: Build Fuzzers (${{ matrix.sanitizer }}) + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@41dccd0566905e2a7d1724e7883edbfa66d78877 + with: + sanitizer: ${{ matrix.sanitizer }} + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Run Fuzzers (${{ matrix.sanitizer }}) + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@41dccd0566905e2a7d1724e7883edbfa66d78877 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fuzz-seconds: 1200 + mode: 'code-change' + sanitizer: ${{ matrix.sanitizer }} diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 11ea788a4..f674d4314 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -39,6 +39,8 @@ jobs: oss-fuzz-project-name: 'systemd' dry-run: false allowed-broken-targets-percentage: 0 + # keep-unaffected-fuzz-targets should be removed once https://github.com/google/oss-fuzz/issues/7011 is fixed + keep-unaffected-fuzz-targets: true sanitizer: ${{ matrix.sanitizer }} - name: Run Fuzzers (${{ matrix.sanitizer }}) uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index cceb51451..eae19bfd7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -11,6 +11,8 @@ on: - .github/codeql-config.yml - .github/codeql-custom.qls - .github/workflows/codeql-analysis.yml + - .github/workflows/requirements.txt + - .github/workflows/unit_tests.sh # It takes the workflow approximately 30 minutes to analyze the code base # so it doesn't seem to make much sense to trigger it on every PR or commit. # It runs daily at 01:00 to avoid colliding with the Coverity workflow. @@ -41,7 +43,7 @@ jobs: uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 - name: Initialize CodeQL - uses: github/codeql-action/init@5f532563584d71fdef14ee64d17bafb34f751ce5 + uses: github/codeql-action/init@75f07e7ab2ee63cba88752d8c696324e4df67466 with: languages: ${{ matrix.language }} config-file: ./.github/codeql-config.yml @@ -49,7 +51,7 @@ jobs: - run: sudo -E .github/workflows/unit_tests.sh SETUP - name: Autobuild - uses: github/codeql-action/autobuild@5f532563584d71fdef14ee64d17bafb34f751ce5 + uses: github/codeql-action/autobuild@75f07e7ab2ee63cba88752d8c696324e4df67466 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@5f532563584d71fdef14ee64d17bafb34f751ce5 + uses: github/codeql-action/analyze@75f07e7ab2ee63cba88752d8c696324e4df67466 diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index a164d16fb..46b286308 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 # https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable - name: Set the $COVERITY_SCAN_NOTIFICATION_EMAIL env variable - run: echo "COVERITY_SCAN_NOTIFICATION_EMAIL=$(git log -1 ${{ github.sha }} --pretty=\"%aE\")" >> $GITHUB_ENV + run: echo "COVERITY_SCAN_NOTIFICATION_EMAIL=$(git log -1 ${{ github.sha }} --pretty=\"%aE\")" >> "$GITHUB_ENV" - name: Install Coverity tools run: tools/get-coverity.sh # Reuse the setup phase of the unit test script to avoid code duplication diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 34d9d63d4..b94c330b7 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -16,7 +16,7 @@ jobs: permissions: pull-requests: write steps: - - uses: actions/labeler@69da01b8e0929f147b8943611bee75ee4175a49e + - uses: actions/labeler@3d612d72e6784a1a65365cc6d33b5a001c12bf10 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" configuration-path: .github/labeler.yml diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 99fba07fa..bb4d2434f 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -29,9 +29,10 @@ jobs: fetch-depth: 0 - name: Lint Code Base - uses: github/super-linter@563be7dc5568017515b9e700329e9c6d3862f2b7 + uses: github/super-linter/slim@b8641364ca9a79b3cf07f3c4c59a82709cd39094 env: DEFAULT_BRANCH: main + MULTI_STATUS: false # Excludes: # - man/.* - all snippets in man pages (false positives due to # missing shebangs) @@ -40,7 +41,6 @@ jobs: # - .*\.(in|SKELETON) - all template/skeleton files # - tools/coverity\.sh - external file (with some modifications) FILTER_REGEX_EXCLUDE: .*/(man/.*|src/kernel-install/.*|.*\.(in|SKELETON)|tools/coverity\.sh)$ - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - MULTI_STATUS: true VALIDATE_ALL_CODEBASE: false VALIDATE_BASH: true + VALIDATE_GITHUB_ACTIONS: true diff --git a/.github/workflows/mkosi.yml b/.github/workflows/mkosi.yml index 68d5f86dc..882565403 100644 --- a/.github/workflows/mkosi.yml +++ b/.github/workflows/mkosi.yml @@ -26,30 +26,50 @@ jobs: ci: runs-on: ubuntu-20.04 concurrency: - group: ${{ github.workflow }}-${{ matrix.distro }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ matrix.distro }}-${{ matrix.release }}-${{ github.ref }} cancel-in-progress: true strategy: fail-fast: false matrix: - distro: - - arch - - debian - - ubuntu - - fedora - - opensuse + include: + - distro: arch + release: rolling + - distro: debian + release: testing + - distro: ubuntu + release: focal + - distro: fedora + release: "35" + - distro: opensuse + release: tumbleweed + - distro: centos_epel + release: 8-stream + - distro: centos_epel + release: 9-stream steps: - uses: actions/checkout@ec3a7ce113134d7a93b817d10a8272cb61118579 - - uses: systemd/mkosi@4d64fc8134f93d87ac584183e7762ac1d0efa0e5 + - uses: systemd/mkosi@0dd39c20a4b3a2fab6efdc54da92bffad7c7b7ca - name: Install run: sudo apt-get update && sudo apt-get install --no-install-recommends python3-pexpect python3-jinja2 - name: Configure - run: echo -e "[Distribution]\nDistribution=${{ matrix.distro }}\n" >mkosi.default + run: | + tee mkosi.default <<- EOF + [Distribution] + Distribution=${{ matrix.distro }} + Release=${{ matrix.release }} + + [Content] + Environment=CI_BUILD=1 + + [Output] + KernelCommandLine=${{ env.KERNEL_CMDLINE }} + EOF - name: Build ${{ matrix.distro }} - run: ./.github/workflows/run_mkosi.sh --build-environment=CI_BUILD=1 --kernel-command-line "${{ env.KERNEL_CMDLINE }}" build + run: ./.github/workflows/run_mkosi.sh build - name: Show ${{ matrix.distro }} image summary run: ./.github/workflows/run_mkosi.sh summary @@ -60,8 +80,14 @@ jobs: - name: Check ${{ matrix.distro }} systemd-nspawn run: ./.github/workflows/run_mkosi.sh shell bash -c "[[ -e /testok ]] || { cat /failed-services; exit 1; }" + # TODO: Remove CentOS exclusion once Ubuntu 22.04 is available in GA. + # See https://github.com/systemd/systemd/pull/22417 and https://github.com/systemd/mkosi/pull/907 for + # more information. + - name: Boot ${{ matrix.distro }} QEMU + if: ${{ matrix.distro != 'centos_epel' }} run: ./.github/workflows/run_mkosi.sh qemu - name: Check ${{ matrix.distro }} QEMU + if: ${{ matrix.distro != 'centos_epel' }} run: ./.github/workflows/run_mkosi.sh shell bash -c "[[ -e /testok ]] || { cat /failed-services; exit 1; }" diff --git a/.github/workflows/requirements.txt b/.github/workflows/requirements.txt index 889e0826b..85ee13b6c 100644 --- a/.github/workflows/requirements.txt +++ b/.github/workflows/requirements.txt @@ -1,6 +1,6 @@ -meson==0.60.2 \ - --hash=sha256:64e6968565bf1b8152f4f9d6ca8154efb9e14caa9aabf7b22e71e6c5d053e921 \ - --hash=sha256:f486659a8c723ec8d54dbe00a9a8b4696fc75f499a60a566a9b0d02952ac0be9 +meson==0.61.2 \ + --hash=sha256:0233a7f8d959079318f6052b0939c27f68a5de86ba601f25c9ee6869fb5f5889 \ + --hash=sha256:2e2d71c4d8e47624cc9fdff6de92915b3e143fc800cc44ccedd2a88362ebe4dd ninja==1.10.2.3 \ --hash=sha256:0560eea57199e41e86ac2c1af0108b63ae77c3ca4d05a9425a750e908135935a \ --hash=sha256:21a1d84d4c7df5881bfd86c25cce4cf7af44ba2b8b255c57bc1c434ec30a2dfc \ diff --git a/.github/workflows/run_mkosi.sh b/.github/workflows/run_mkosi.sh index a6a84b6c4..153ebe3d3 100755 --- a/.github/workflows/run_mkosi.sh +++ b/.github/workflows/run_mkosi.sh @@ -22,7 +22,7 @@ for ((i = 0; i < 5; i++)); do EC=0 (sudo python3 -m mkosi --extra-tree="$TEMP_EXTRA_TREE" "$@") |& tee "$TEMPFILE" || EC=$? if [[ $EC -eq 0 ]]; then - # The command passed - let's return immediatelly + # The command passed — let's return immediately break fi @@ -32,7 +32,7 @@ for ((i = 0; i < 5; i++)); do exit $EC fi - # The command failed due to the dissect-related timeout - let's try again + # The command failed due to the dissect-related timeout — let's try again sleep 1 done diff --git a/.github/workflows/unit_tests.sh b/.github/workflows/unit_tests.sh index 9c7beb6d1..ca7309b14 100755 --- a/.github/workflows/unit_tests.sh +++ b/.github/workflows/unit_tests.sh @@ -9,6 +9,7 @@ ADDITIONAL_DEPS=( expect fdisk jekyll + libbpf-dev libfdisk-dev libfido2-dev libp11-kit-dev @@ -16,10 +17,12 @@ ADDITIONAL_DEPS=( libqrencode-dev libssl-dev libtss2-dev + libxkbcommon-dev libzstd-dev perl python3-libevdev python3-pyparsing + rpm zstd ) @@ -27,6 +30,13 @@ function info() { echo -e "\033[33;1m$1\033[0m" } +function run_meson() { + if ! meson "$@"; then + find . -type f -name meson-log.txt -exec cat '{}' + + return 1 + fi +} + set -ex MESON_ARGS=(-Dcryptolib=${CRYPTOLIB:-auto}) @@ -41,6 +51,7 @@ for phase in "${PHASES[@]}"; do apt-get -y update apt-get -y build-dep systemd apt-get -y install "${ADDITIONAL_DEPS[@]}" + pip3 install -r .github/workflows/requirements.txt --require-hashes ;; RUN|RUN_GCC|RUN_CLANG) if [[ "$phase" = "RUN_CLANG" ]]; then @@ -49,22 +60,26 @@ for phase in "${PHASES[@]}"; do # The docs build is slow and is not affected by compiler/flags, so do it just once MESON_ARGS+=(-Dman=true) fi - meson --werror -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true "${MESON_ARGS[@]}" build + run_meson --fatal-meson-warnings -Dnobody-group=nogroup --werror -Dtests=unsafe -Dslow-tests=true -Dfuzz-tests=true "${MESON_ARGS[@]}" build ninja -C build -v meson test -C build --print-errorlogs ;; - RUN_ASAN_UBSAN|RUN_GCC_ASAN_UBSAN|RUN_CLANG_ASAN_UBSAN) + RUN_ASAN_UBSAN|RUN_GCC_ASAN_UBSAN|RUN_CLANG_ASAN_UBSAN|RUN_CLANG_ASAN_UBSAN_NO_DEPS) MESON_ARGS=(--optimization=1) - if [[ "$phase" = "RUN_CLANG_ASAN_UBSAN" ]]; then + if [[ "$phase" =~ ^RUN_CLANG_ASAN_UBSAN ]]; then export CC=clang export CXX=clang++ # Build fuzzer regression tests only with clang (for now), # see: https://github.com/systemd/systemd/pull/15886#issuecomment-632689604 # -Db_lundef=false: See https://github.com/mesonbuild/meson/issues/764 MESON_ARGS+=(-Db_lundef=false -Dfuzz-tests=true) + + if [[ "$phase" == "RUN_CLANG_ASAN_UBSAN_NO_DEPS" ]]; then + MESON_ARGS+=(-Dskip-deps=true) + fi fi - meson --werror -Dtests=unsafe -Db_sanitize=address,undefined "${MESON_ARGS[@]}" build + run_meson --fatal-meson-warnings -Dnobody-group=nogroup --werror -Dtests=unsafe -Db_sanitize=address,undefined "${MESON_ARGS[@]}" build ninja -C build -v export ASAN_OPTIONS=strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1 diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index d4a4f3c72..3c8fb5485 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -21,7 +21,7 @@ jobs: strategy: fail-fast: false matrix: - run_phase: [GCC, GCC_ASAN_UBSAN, CLANG, CLANG_ASAN_UBSAN] + run_phase: [GCC, GCC_ASAN_UBSAN, CLANG, CLANG_ASAN_UBSAN, CLANG_ASAN_UBSAN_NO_DEPS] cryptolib: [auto] include: - run_phase: GCC diff --git a/.gitignore b/.gitignore index b3aba0921..c5424c5d7 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,7 @@ __pycache__/ /mkosi.builddir/ /mkosi.output/ /mkosi.default +/mkosi.installdir/ # Ignore any mkosi config files with "local" in the name /mkosi.default.d/**/*local*.conf /tags diff --git a/.lgtm.yml b/.lgtm.yml index 86fd0e742..77016500d 100644 --- a/.lgtm.yml +++ b/.lgtm.yml @@ -4,6 +4,8 @@ # Explicitly enable certain checks which are hidden by default queries: + # See: https://github.com/github/codeql/issues/8409 + - exclude: cpp/missing-return - include: cpp/bad-strncpy-size - include: cpp/declaration-hides-variable - include: cpp/inconsistent-null-check diff --git a/.packit.yml b/.packit.yml index e16622311..a7502b25b 100644 --- a/.packit.yml +++ b/.packit.yml @@ -13,6 +13,7 @@ downstream_package_name: systemd # `git describe` returns in systemd's case 'v245-xxx' which breaks RPM version # detection (that expects 245-xxxx'). Let's tweak the version string accordingly upstream_tag_template: "v{version}" +srpm_build_deps: [] actions: post-upstream-clone: @@ -40,4 +41,5 @@ jobs: - fedora-rawhide-aarch64 - fedora-rawhide-i386 - fedora-rawhide-ppc64le + - fedora-rawhide-s390x - fedora-rawhide-x86_64 diff --git a/.semaphore/semaphore-runner.sh b/.semaphore/semaphore-runner.sh index d02b449e0..6ccf271a8 100755 --- a/.semaphore/semaphore-runner.sh +++ b/.semaphore/semaphore-runner.sh @@ -42,7 +42,7 @@ apt-get -q --allow-releaseinfo-change update apt-get -y dist-upgrade apt-get install -y eatmydata # The following four are needed as long as these deps are not covered by Debian's own packaging -apt-get install -y fdisk tree libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev +apt-get install -y fdisk tree libfdisk-dev libp11-kit-dev libssl-dev libpwquality-dev rpm apt-get purge --auto-remove -y unattended-upgrades systemctl unmask systemd-networkd systemctl enable systemd-networkd diff --git a/LICENSES/README.md b/LICENSES/README.md index 2f825c44b..69ef2d631 100644 --- a/LICENSES/README.md +++ b/LICENSES/README.md @@ -48,6 +48,7 @@ The following exceptions apply: - src/systemctl/systemd-sysv-install.SKELETON - tools/check-includes.pl - all examples under man/ + - config files and examples under /network * the following sources are under **Public Domain** (LicenseRef-murmurhash2-public-domain): - src/basic/MurmurHash2.c - src/basic/MurmurHash2.h diff --git a/NEWS b/NEWS index f010959be..53855d5ee 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,452 @@ systemd System and Service Manager +CHANGES WITH 251: + + Backwards-incompatible changes: + + * The minimum kernel version required has been bumped from 3.13 to 3.15, + and CLOCK_BOOTTIME is now assumed to always exist. + + * In v250, a systemd-networkd feature that automatically configures + routes to addresses specified in AllowedIPs= was added and enabled by + default. However, this causes network connectivity issues in many + existing setups. Hence, it has been disabled by default since + systemd-stable 250.3. The feature can still be used by explicitly + configuring RouteTable= setting in .netdev files. + + * Jobs started via StartUnitWithFlags() will no longer return 'skipped' + when a Condition*= check does not succeed, restoring the JobRemoved + signal to the behaviour it had before v250. + + * The org.freedesktop.portable1 methods GetMetadataWithExtensions() and + GetImageMetadataWithExtensions() have been fixed to provide an extra + return parameter, containing the actual extension release metadata. + The current implementation was judged to be broken and unusable, and + thus the usual procedure of adding a new set of methods was skipped, + and backward compatibility broken instead on the assumption that + nobody can be affected given the current state of this interface. + + * All kernels supported by systemd mix RDRAND (or similar) into the + entropy pool at early boot. This means that on those systems, even if + /dev/urandom is not yet initialized, it still returns bytes that that + are at least as high quality as RDRAND. For that reason, we no longer + have reason to invoke RDRAND from systemd itself, which has + historically been a source of bugs. Furthermore, kernels ≥5.6 provide + the getrandom(GRND_INSECURE) interface for returning random bytes + before the entropy pool is initialized without warning into kmsg, + which is what we attempt to use if available. systemd's direct usage + of RDRAND has been removed. x86 systems ≥Broadwell that are running + an older kernel may experience kmsg warnings that were not seen with + 250. For newer kernels, non-x86 systems, or older x86 systems, there + should be no visible changes. + + * sd-boot will now measure the kernel command line into TPM PCR 12 + rather than PCR 8. This improves usefulness of the measurements on + systems where sd-boot is chainloaded from Grub. Grub measures all + commands its executes into PCR 8, which makes it very hard to use + reasonably, hence separate ourselves from that and use PCR 12 + instead, which is what certain Ubuntu editions already do. To retain + compatibility with systems running older systemd systems a new meson + option 'efi-tpm-pcr-compat' has been added (which defaults to false). + If enabled, the measurement is done twice: into the new-style PCR 12 + *and* the old-style PCR 8. It's strongly advised to migrate all users + to PCR 12 for this purpose in the long run, as we intend to remove + this compatibility feature in two year's time. + + * busctl capture now writes output in the newer pcapng format instead + of pcap. + + * An udev rule that imported hwdb matches for USB devices with + lowercase hexadecimal vendor/product ID digits was added in systemd + 250. This has been reverted, since uppercase hexadecimal digits are + supposed to be used, and we already had a rule that with the + appropriate match. + + Users might need to adjust their local hwdb entries. + + * arch_prctl(2) has been moved to the @default set in the syscall filters + (as exposed via the SystemCallFilter= setting in service unit files). + It is apparently used by the linker now. + + Changes in the Boot Loader Specification, kernel-install and sd-boot: + + * kernel-install's and bootctl's Boot Loader Specification Type #1 + entry generation logic has been reworked. The user may now pick + explicitly by which "token" string to name the installation's boot + entries, via the new /etc/kernel/entry-token file or the new + --entry-token= switch to bootctl. By default — as before — the + entries are named after the local machine ID. However, in "golden + image" environments, where the machine ID shall be initialized on + first boot (as opposed to at installation time before first boot) the + machine ID will not be available at build time. In this case the + --entry-token= switch to bootctl (or the /etc/kernel/entry-token + file) may be used to override the "token" for the entries, for + example the IMAGE_ID= or ID= fields from /etc/os-release. This will + make the OS images independent of any machine ID, and ensure that the + images will not carry any identifiable information before first boot, + but on the other hand means that multiple parallel installations of + the very same image on the same disk cannot be supported. + + Summary: if you are building golden images that shall acquire + identity information exclusively on first boot, make sure to both + remove /etc/machine-id *and* to write /etc/kernel/entry-token to the + value of the IMAGE_ID= or ID= field of /etc/os-release or another + suitable identifier before deploying the image. + + * The Boot Loader Specification has been extended with + /loader/entries.srel file located in the EFI System Partition (ESP) + that disambiguates the format of the entries in the /loader/entries/ + directory (in order to discern them from incompatible uses of this + directory by other projects). For entries that follow the + Specification, the string "type1" is stored in this file. + + bootctl will now write this file automatically when installing the + systemd-boot boot loader. + + * kernel-install supports a new initrd_generator= setting in + /etc/kernel/install.conf, that is exported as + $KERNEL_INSTALL_INITRD_GENERATOR to kernel-install plugins. This + allows choosing different initrd generators. + + * kernel-install will now create a "staging area" (an initially-empty + directory to gather files for a Boot Loader Specification Type #1 + entry). The path to this directory is exported as + $KERNEL_INSTALL_STAGING_AREA to kernel-install plugins, which should + drop files there instead of writing them directly to the final + location. kernel-install will move them when all files have been + prepared successfully. + + * New option sort-key= has been added to the Boot Loader Specification + to override the sorting order of the entries in the boot menu. It is + read by sd-boot and bootctl, and will be written by kernel-install, + with the default value of IMAGE_ID= or ID= fields from + os-release. Together, this means that on multiboot installations, + entries should be grouped and sorted in a predictable way. + + * The sort order of boot entries has been updated: entries which have + the new field sort-key= are sorted by it first, and all entries + without it are ordered later. After that, entries are sorted by + version so that newest entries are towards the beginning of the list. + + * The kernel-install tool gained a new 'inspect' verb which shows the + paths and other settings used. + + * sd-boot can now optionally beep when the menu is shown and menu + entries are selected, which can be useful on machines without a + working display. (Controllable via a loader.conf setting.) + + * The --make-machine-id-directory= switch to bootctl has been replaced + by --make-entry-directory=, given that the entry directory is not + necessarily named after the machine ID, but after some other suitable + ID as selected via --entry-token= described above. The old name of + the option is still understood to maximize compatibility. + + * 'bootctl list' gained support for a new --json= switch to output boot + menu entries in JSON format. + + Changes in systemd-homed: + + * Starting with v250 systemd-homed uses UID/GID mapping on the mounts + of activated home directories it manages (if the kernel and selected + file systems support it). So far it mapped three UID ranges: the + range from 0…60000, the user's own UID, and the range 60514…65534, + leaving everything else unmapped (in other words, the 16bit UID range + is mapped almost fully, with the exception of the UID subrange used + for systemd-homed users, with one exception: the user's own UID). + Unmapped UIDs may not be used for file ownership in the home + directory — any chown() attempts with them will fail. With this + release a fourth range is added to these mappings: + 524288…1879048191. This range is the UID range intended for container + uses, see: + + https://systemd.io/UIDS-GIDS + + This range may be used for container managers that place container OS + trees in the home directory (which is a questionable approach, for + quota, permission, SUID handling and network file system + compatibility reasons, but nonetheless apparently commonplace). Note + that this mapping is mapped 1:1 in a pass-through fashion, i.e. the + UID assignments from the range are not managed or mapped by + `systemd-homed`, and must be managed with other mechanisms, in the + context of the local system. + + Typically, a better approach to user namespacing in relevant + container managers would be to leave container OS trees on disk at + UID offset 0, but then map them to a dynamically allocated runtime + UID range via another UID mount map at container invocation + time. That way user namespace UID ranges become strictly a runtime + concept, and do not leak into persistent file systems, persistent + user databases or persistent configuration, thus greatly simplifying + handling, and improving compatibility with home directories intended + to be portable like the ones managed by systemd-homed. + + Changes in shared libraries: + + * A new libsystemd-core-.so private shared library is + installed under /usr/lib/systemd/system, mirroring the existing + libsystemd-shared-.so library. This allows the total + installation size to be reduced by binary code reuse. + + * The tag used in the name of libsystemd-shared.so and + libsystemd-core.so can be configured via the meson option + 'shared-lib-tag'. Distributions may build subsequent versions of the + systemd package with unique tags (e.g. the full package version), + thus allowing multiple installations of those shared libraries to be + available at the same time. This is intended to fix an issue where + programs that link to those libraries would fail to execute because + they were installed earlier or later than the appropriate version of + the library. + + * The sd-id128 API gained a new call sd_id128_to_uuid_string() that is + similar to sd_id128_to_string() but formats the ID in RFC 4122 UUID + format instead of simple series of hex characters. + + Changes in PID1, systemctl, and systemd-oomd: + + * A new set of service monitor environment variables will be passed to + OnFailure=/OnSuccess= handlers, but only if exactly one unit lists the + handler unit as OnFailure=/OnSuccess=. The variables are: + $MONITOR_SERVICE_RESULT, $MONITOR_EXIT_CODE, $MONITOR_EXIT_STATUS, + $MONITOR_INVOCATION_ID and $MONITOR_UNIT. For cases when a single + handler needs to watch multiple units, use a templated handler. + + * A new ExtensionDirectories= setting in service unit files allows + system extensions to be loaded from a directory. (It is similar to + ExtensionImages=, but takes paths to directories, instead of + disk image files.) + + 'portablectl attach --extension=' now also accepts directory paths. + + * The user.delegate and user.invocation_id extended attributes on + cgroups are used in addition to trusted.delegate and + trusted.invocation_id. The latter pair requires privileges to set, + but the former doesn't and can be also set by the unprivileged user + manager. + + (Only supported on kernels ≥5.6.) + + * Units that were killed by systemd-oomd will now have a service result + of 'oom-kill'. The number of times a service was killed is tallied + in the 'user.oomd_ooms' extended attribute. + + The OOMPolicy= unit file setting is now also honoured by + systemd-oomd. + + * In unit files the new %y/%Y specifiers can be used to refer to + normalized unit file path, which is particularly useful for symlinked + unit files. + + The new %R specifier resolves to the pretty hostname + (i.e. PRETTY_HOSTNAME= from /etc/machine-info). + + The new %d specifier resolves to the credentials directory of a + service (same as $CREDENTIALS_DIRECTORY). + + * The RootDirectory=, MountAPIVFS=, ExtensionDirectories=, + *Capabilities*=, ProtectHome=, *Directory=, TemporaryFileSystem=, + PrivateTmp=, PrivateDevices=, PrivateNetwork=, NetworkNamespacePath=, + PrivateIPC=, IPCNamespacePath=, PrivateUsers=, ProtectClock=, + ProtectKernelTunables=, ProtectKernelModules=, ProtectKernelLogs=, + MountFlags= service settings now also work in unprivileged user + services, i.e. those run by the user's --user service manager, as long + as user namespaces are enabled on the system. + + * Services with Restart=always and a failing ExecCondition= will no + longer be restarted, to bring ExecCondition= behaviour in line with + Condition*= settings. + + * LoadCredential= now accepts a directory as the argument; all files + from the directory will be loaded as credentials. + + * A new D-Bus property ControlGroupId is now exposed on service units, + that encapsulates the service's numeric cgroup ID that newer kernels + assign to each cgroup. + + * PID 1 gained support for configuring the "pre-timeout" of watchdog + devices and the associated governor, via the new + RuntimeWatchdogPreSec= and RuntimeWatchdogPreGovernor= configuration + options in /etc/systemd/system.conf. + + * systemctl's --timestamp= option gained a new choice "unix", to show + timestamp as unix times, i.e. seconds since 1970, Jan 1st. + + * 'systemctl enable' and similar commands will now create relative + symlinks in .wants/ and .requires/ and for aliases. Most of the time + systemd itself doesn't care, but absolute symlinks were causing wrong + behaviour in case of aliases to linked unit files. The change was + necessary to fix this aspect. Absolute links are interpreted as + before, and it is still possible to create them via other means. + + Changes in systemd-journald: + + * The journal JSON export format has been added to listed of stable + interfaces (https://systemd.io/PORTABILITY_AND_STABILITY/). + + * journalctl --list-boots now supports JSON output and the --reverse option. + + * Under docs/: JOURNAL_EXPORT_FORMATS was imported from the wiki and + updated, BUILDING_IMAGES is new: + + https://systemd.io/JOURNAL_EXPORT_FORMATS + https://systemd.io/BUILDING_IMAGES + + Changes in udev: + + * Two new hwdb files have been added. One lists "handhelds" (PDAs, + calculators, etc.), the other AV production devices (DJ tables, + keypads, etc.) that should accessible to the seat owner user by + default. + + * udevadm trigger gained a new --prioritized-subsystem= option to + process certain subsystems (and all their parent devices) earlier. + + systemd-udev-trigger.service now uses this new option to trigger + block and TPM devices first, hopefully making the boot a bit faster. + + * udevadm trigger now implements --type=all, --initialized-match, + --initialized-nomatch to trigger both subsystems and devices, only + already-initialized devices, and only devices which haven't been + initialized yet, respectively. + + * .link files gained support for setting MDI/MID-X on a link. + + * .link files gained support for [Match] Firmware= setting to match on + the device firmware description string. By mistake, it was previously + only supported in .network files. + + * .link files gained support for [Link] SR-IOVVirtualFunctions= setting + and [SR-IOV] section to configure SR-IOV virtual functions. + + Changes in systemd-networkd: + + * The default scope for unicast routes configured through [Route] + section is changed to "link", to make the behavior consistent with + "ip route" command. The manual configuration of [Route] Scope= is + still honored. + + * A new unit systemd-networkd-wait-online@.service has been + added that can be used to wait for a specific network interface to be + up. + + * systemd-networkd gained a new [Bridge] Isolated=true|false setting + that configures the eponymous kernel attribute on the bridge. + + * .netdev files now can be used to create virtual WLAN devices, and + configure various settings on them, via the [WLAN] section. + + * .link/.network files gained support for [Match] Kind= setting to match + on device kind ("bond", "bridge", "gre", "tun", "veth", etc.) + + This value is also shown by 'networkctl status'. + + * The Local= setting in .netdev files for various virtual network + devices gained support for specifying, in addition to the network + address, the name of a local interface which must have the specified + address. + + * systemd-networkd gained a new [Tunnel] External= setting in .netdev + files, to configure tunnels in external mode (a.k.a. collect metadata + mode). + + * [Network] L2TP= setting was removed. Please use interface specifier in + Local= setting in .netdev files of corresponding L2TP interface. + + * New [DHCPServer] BootServerName=, BootServerAddress=, and + BootFilename= settings can be used to configure the server address, + server name, and file name sent in the DHCP packet (e.g. to configure + PXE boot). + + Changes in systemd-resolved: + + * systemd-resolved is started earlier (in sysinit.target), so it + available earlier and will also be started in the initrd if installed + there. + + Changes in disk encryption: + + * systemd-cryptenroll can now control whether to require the user to + enter a PIN when using TPM-based unlocking of a volume via the new + --tpm2-with-pin= option. + + Option tpm2-pin= can be used in /etc/crypttab. + + * When unlocking devices via TPM, TPM2 parameter encryption is now + used, to ensure that communication between CPU and discrete TPM chips + cannot be eavesdropped to acquire disk encryption keys. + + Changes in systemd-hostnamed: + + * HARDWARE_VENDOR= and HARDWARE_MODEL= can be set in /etc/machine-info + to override the values gleaned from the hwdb. + + * A ID_CHASSIS property can be set in the hwdb (for the DMI device + /sys/class/dmi/id) to override the chassis that is reported by + hostnamed. + + * hostnamed's D-Bus interface gained a new method GetHardwareSerial() + for reading the hardware serial number, as reportd by DMI. + + Changes in other components: + + * /etc/locale.conf is now populated through tmpfiles.d factory /etc/ + handling with the values that were configured during systemd build + (if /etc/locale.conf has not been created through some other + mechanism). This means that /etc/locale.conf should always have + reasonable contents and we avoid a potential mismatch in defaults. + + * The userdbctl tool will now show UID range information as part of the + list of known users. + + * A new build-time configuration setting default-user-shell= can be + used to set the default shell for user records and nspawn shell + invocations (instead of of the default /bin/bash). + + Experimental features: + + * sd-boot gained a new *experimental* setting "reboot-for-bitlocker" in + loader.conf that implements booting Microsoft Windows from the + sd-boot in a way that first reboots the system, to reset the TPM + PCRs. This improves compatibility with BitLocker's TPM use, as the + PCRs will only record the Windows boot process, and not sd-boot + itself, thus retaining the PCR measurements not involving sd-boot. + Note that this feature is experimental for now, and is likely going + to be generalized and renamed in a future release, without retaining + compatibility with the current implementation. + + * A new systemd-sysupdate component has been added that automatically + discovers, downloads, and installs A/B-style updates for the host + installation itself, or container images, portable service images, + and other assets. See the new systemd-sysupdate man page for updates. + + Contributions from: 4piu, Adam Williamson, adrian5, Albert Brox, + AlexCatze, Alfonso Sánchez-Beato, Alvin Šipraga, Andrea Pappacoda, + Andy Chi, Anita Zhang, Antonio Alvarez Feijoo, + Arfrever Frehtes Taifersar Arahesis, ash, Bastien Nocera, Be, + bearhoney, Benjamin Berg, Christian Brauner, Clyde Byrd III, + Curtis Klein, Daan De Meyer, Danilo Krummrich, David, David Bond, + Davide Cavalca, David Tardon, dependabot[bot], Donald Chan, + Dorian Clay, Eduard Tolosa, Erik Sjölund, Evgeny Vereshchagin, + Federico Ceratto, Franck Bui, Frantisek Sumsal, Gaël PORTAY, + Georges Basile Stavracas Neto, Goffredo Baroncelli, Grigori Goronzy, + Hans de Goede, Heiko Becker, Hugo Carvalho, James Hilliard, + Jan Janssen, Jason A. Donenfeld, Joan Bruguera, Joerie de Gram, + Josh Triplett, Julia Kartseva, ksa678491784, Lan Tian, Laura Barcziova, + Lennart Poettering, Leviticoh, licunlong, Lidong Zhong, lincoln auster, + Lubomir Rintel, Luca Boccassi, Luca BRUNO, Ludwig Nussel, + Marcel Hellwig, march1993, Marco Scardovi, Markus Weippert, + Martin Wilck, Matija Skala, Matthias Lisin, Matt Walton, Max Gautier, + Michael Biebl, Michael Olbrich, Michal Koutný, Mike Gilbert, + Morten Linderud, Nishal Kulkarni, Noel Kuntze, Peter Hutterer, + Peter Morrow, Pigmy-penguin, prumian, Richard Neill, + Rike-Benjamin Schuppner, Romain Naour, Ruben Kerkhof, Ryan Hendrickson, + Santa Wiryaman, Seth Falco, Stephen Hemminger, tawefogo, + Temuri Doghonadze, Thomas Batten, Thomas Haller, Tobias Stoeckmann, + Tyson Whitehead, Vishal Chillara Srinivas, Vivien Didelot, Weblate, + Xiaotian Wu, yangmingtai, YmrDtnJu, Yonathan Randolph, Yu Watanabe, + Zbigniew Jędrzejewski-Szmek, наб + + — Warsaw, 2022-03--- + CHANGES WITH 250: * Support for encrypted and authenticated credentials has been added. @@ -100,9 +547,9 @@ CHANGES WITH 250: time-out for the boot. * A new setting DefaultOOMScoreAdjust= is now supported in - /etc/systemd/system.conf + /etc/systemd/user.conf that may be used to - set the default process OOM score adjustment value for processes - forked off the service manager. For per-user service managers this + /etc/systemd/system.conf and /etc/systemd/user.conf. It may be used + to set the default process OOM score adjustment value for processes + started by the service manager. For per-user service managers this now defaults to 100, but for per-system service managers is left as is. This means that by default now services forked off the user service manager are more likely to be killed by the OOM killer than @@ -145,7 +592,7 @@ CHANGES WITH 250: ProtectKernelLogs=yes can now be used. * The default maximum numbers of inodes have been raised from 64k to 1M - for /dev, and from 400k to 1M for /tmp. + for /dev/, and from 400k to 1M for /tmp/. * The per-user service manager learnt support for communicating with systemd-oomd to acquire OOM kill information. @@ -271,36 +718,6 @@ CHANGES WITH 250: monotonic clock even without RTC hardware and with some robustness against abnormal system shutdown. - * .network files gained a new UplinkInterface in the [IPv6SendRA] - section, for automatically propagating DNS settings from other - interfaces. - - * The static lease DHCP server logic in systemd-networkd may now serve - IP addresses outside of the configured IP pool range for the server. - - * CAN support in systemd-networkd gained four new settings Loopback=, - OneShot=, PresumeAck=, ClassicDataLengthCode= for tweaking CAN - control modes. It gained a number of further settings for tweaking - CAN timing quanta. - - * The [CAN] section in .network file gained new TimeQuantaNSec=, - PropagationSegment=, PhaseBufferSegment1=, PhaseBufferSegment2=, - SyncJumpWidth=, DataTimeQuantaNSec=, DataPropagationSegment=, - DataPhaseBufferSegment1=, DataPhaseBufferSegment2=, and - DataSyncJumpWidth= settings to control bit-timing processed by the - CAN interface. - - * DHCPv4 client support in systemd-networkd learnt a new Label= option - for configuring the address label to apply to configure IPv4 - addresses. - - * The various systemd-udevd "ethtool" buffer settings now understand - the special value "max" to configure the buffers to the maximum the - hardware supports. - - * systemd-udevd's .link files may now configure a large variety of - NIC coalescing settings, plus more hardware offload settings. - * systemd-analyze verify gained support for a pair of new --image= + --root= switches for verifying units below a specific root directory/image instead of on the host. @@ -331,7 +748,7 @@ CHANGES WITH 250: non-essential output. It's honored by the "dot", "syscall-filter", "filesystems" commands. - * systemd-analyze security gained a --profile option that can be used + * systemd-analyze security gained a --profile= option that can be used to take into account a portable profile when analyzing portable services, since a lot of the security-related settings are enabled through them. @@ -341,40 +758,33 @@ CHANGES WITH 250: including the build-id and other info described on: https://systemd.io/COREDUMP_PACKAGE_METADATA/ + * .network files gained a new UplinkInterface= in the [IPv6SendRA] + section, for automatically propagating DNS settings from other + interfaces. + + * The static lease DHCP server logic in systemd-networkd may now serve + IP addresses outside of the configured IP pool range for the server. + + * CAN support in systemd-networkd gained four new settings Loopback=, + OneShot=, PresumeAck=, ClassicDataLengthCode= for tweaking CAN + control modes. It gained a number of further settings for tweaking + CAN timing quanta. + + * The [CAN] section in .network file gained new TimeQuantaNSec=, + PropagationSegment=, PhaseBufferSegment1=, PhaseBufferSegment2=, + SyncJumpWidth=, DataTimeQuantaNSec=, DataPropagationSegment=, + DataPhaseBufferSegment1=, DataPhaseBufferSegment2=, and + DataSyncJumpWidth= settings to control bit-timing processed by the + CAN interface. + + * DHCPv4 client support in systemd-networkd learnt a new Label= option + for configuring the address label to apply to configure IPv4 + addresses. + * The [IPv6AcceptRA] section of .network files gained support for a new UseMTU= setting that may be used to control whether to apply the announced MTU settings to the local interface. - * systemd-networkd now ships with new default .network files: - 80-container-vb.network which matches host-side network bridge device - created by systemd-nspawn's --network-bridge or --network-zone - switch, and 80-6rd-tunnel.network which matches automatically created - sit tunnel with 6rd prefix when the DHCP 6RD option is received. - - * systemd-networkd and systemd-udevd now support IP over InfiniBand - interfaces. The Kind= setting in .netdev file accepts "ipoib". And - systemd.netdev files gained the [IPoIB] section. - - * systemd-networkd and systemd-udevd now support net.ifname-policy= - option on the kernel command-line. This is implemented through the - systemd-network-generator service that automatically generates - appropriate .link, .network, and .netdev files. - - * systemd-networkd's handling of Endpoint= resolution for WireGuard - interfaces has been improved. - - * systemd-networkd will now automatically configure routes to addresses - specified in AllowedIPs=. This feature can be controlled via RouteTable= - and RouteMetric= settings in [WireGuard] or [WireGuardPeer] sections. - - * systemd-networkd will now once again automatically generate persistent - MAC addresses for batadv and bridge interfaces. Users can disable this - by using MACAddress=none in .netdev files. - - * .link files gained a new WakeOnLanPassword= setting in the [Link] - section that allows to specify a WoL "SecureOn" password on hardware - that supports this. - * The [DHCPv4] section in .network file gained a new Use6RD= boolean setting to control whether the DHCPv4 client request and process the DHCP 6RD option. @@ -395,17 +805,12 @@ CHANGES WITH 250: whether to use the relevant fields from the IPv6 Router Advertisement records. - * The ForceDHCPv6PDOtherInformation= setting in the [DHCPv6] section is - now deprecated. Please use the WithoutRA= and UseDelegatedPrefix= + * The ForceDHCPv6PDOtherInformation= setting in the [DHCPv6] section + has been removed. Please use the WithoutRA= and UseDelegatedPrefix= settings in the [DHCPv6] section and the DHCPv6Client= setting in the [IPv6AcceptRA] section to control when the DHCPv6 client is started and how the delegated prefixes are handled by the DHCPv6 client. - * The [CAKE] section of .network files gained various new settings - AutoRateIngress=, CompensationMode=, FlowIsolationMode=, NAT=, - MPUBytes=, PriorityQueueingPreset=, FirewallMark=, Wash=, SplitGSO=, - and UseRawPacketSize= for configuring CAKE. - * The IPv6Token= section in the [Network] section is deprecated, and the [IPv6AcceptRA] section gained the Token= setting for its replacement. The [IPv6Prefix] section also gained the Token= setting. @@ -425,6 +830,49 @@ CHANGES WITH 250: * The [DHCPServer] section of .network file gained a new Router= setting to specify the router address. + * The [CAKE] section of .network files gained various new settings + AutoRateIngress=, CompensationMode=, FlowIsolationMode=, NAT=, + MPUBytes=, PriorityQueueingPreset=, FirewallMark=, Wash=, SplitGSO=, + and UseRawPacketSize= for configuring CAKE. + + * systemd-networkd now ships with new default .network files: + 80-container-vb.network which matches host-side network bridge device + created by systemd-nspawn's --network-bridge or --network-zone + switch, and 80-6rd-tunnel.network which matches automatically created + sit tunnel with 6rd prefix when the DHCP 6RD option is received. + + * systemd-networkd's handling of Endpoint= resolution for WireGuard + interfaces has been improved. + + * systemd-networkd will now automatically configure routes to addresses + specified in AllowedIPs=. This feature can be controlled via + RouteTable= and RouteMetric= settings in [WireGuard] or + [WireGuardPeer] sections. + + * systemd-networkd will now once again automatically generate persistent + MAC addresses for batadv and bridge interfaces. Users can disable this + by using MACAddress=none in .netdev files. + + * systemd-networkd and systemd-udevd now support IP over InfiniBand + interfaces. The Kind= setting in .netdev file accepts "ipoib". And + systemd.netdev files gained the [IPoIB] section. + + * systemd-networkd and systemd-udevd now support net.ifname-policy= + option on the kernel command-line. This is implemented through the + systemd-network-generator service that automatically generates + appropriate .link, .network, and .netdev files. + + * The various systemd-udevd "ethtool" buffer settings now understand + the special value "max" to configure the buffers to the maximum the + hardware supports. + + * systemd-udevd's .link files may now configure a large variety of + NIC coalescing settings, plus more hardware offload settings. + + * .link files gained a new WakeOnLanPassword= setting in the [Link] + section that allows to specify a WoL "SecureOn" password on hardware + that supports this. + * systemd-nspawn's --setenv= switch now supports an additional syntax: if only a variable name is specified (i.e. without being suffixed by a '=' character and a value) the current value of the environment @@ -522,15 +970,13 @@ CHANGES WITH 250: may be used to set the boot menu time-out of the boot loader (for all or just the subsequent boot). - * bootctl and kernel-install will now read KERNEL_INSTALL_MACHINE_ID - and KERNEL_INSTALL_LAYOUT from kernel/install.conf. The first - variable specifies the machine-id to use for installation. It would - previously be used if set in the environment, and now it'll also be - read automatically from the config file. The second variable is new. - When set, it specifies the layout to use for installation directories - on the boot partition, so that tools don't need to guess it based on - the already-existing directories. The only value that is defined - natively is "bls", corresponding to the layout specified in + * bootctl and kernel-install will now read variables + KERNEL_INSTALL_LAYOUT= from /etc/machine-info and layout= from + /etc/kernel/install.conf. When set, it specifies the layout to use + for installation directories on the boot partition, so that tools + don't need to guess it based on the already-existing directories. The + only value that is defined natively is "bls", corresponding to the + layout specified in https://systemd.io/BOOT_LOADER_SPECIFICATION/. Plugins for kernel-install that implement a different layout can declare other values for this variable. @@ -12359,7 +12805,7 @@ CHANGES WITH 197: based on a calendar time specification such as "Thu,Fri 2013-*-1,5 11:12:13" which refers to 11:12:13 of the first or fifth day of any month of the year 2013, given that it is - a thursday or friday. This brings timer event support + a Thursday or a Friday. This brings timer event support considerably closer to cron's capabilities. For details on the supported calendar time specification language see systemd.time(7). diff --git a/README b/README index 48d9994de..420276eda 100644 --- a/README +++ b/README @@ -30,7 +30,7 @@ LICENSE: LGPL-2.1-or-later for all code, exceptions noted in LICENSES/README.md REQUIREMENTS: - Linux kernel >= 3.13 + Linux kernel >= 3.15 Linux kernel >= 4.2 for unified cgroup hierarchy support Linux kernel >= 4.10 for cgroup-bpf egress and ingress hooks Linux kernel >= 4.15 for cgroup-bpf device hook @@ -88,7 +88,7 @@ REQUIREMENTS: CONFIG_{TMPFS,EXT4_FS,XFS,BTRFS_FS,...}_POSIX_ACL CONFIG_SECCOMP CONFIG_SECCOMP_FILTER (required for seccomp support) - CONFIG_CHECKPOINT_RESTORE (for the kcmp() syscall) + CONFIG_KCMP (for the kcmp() syscall, used to be under CONFIG_CHECKPOINT_RESTORE before ~5.12) Required for CPUShares= in resource control unit settings CONFIG_CGROUP_SCHED diff --git a/TODO b/TODO index 8c5074902..6f80e5764 100644 --- a/TODO +++ b/TODO @@ -78,6 +78,52 @@ Janitorial Clean-ups: Features: +* improve scope units to support creation by pidfd instead of by PID + +* deprecate cgroupsv1 (i.e. taint system with it, print log message at boot) + +* systemd-dissect: add --cat switch for dumping files such as /etc/os-release + +* per-service sandboxing option: ProtectIds=. If used, will overmount + /etc/machine-id and /proc/sys/kernel/random/boot_id with synthetic files, to + make it harder for the service to identify the host. Depending on the user + setting it should be fully randomized at invocation time, or a hash of the + real thing, keyed by the unit name or so. Of course, there are other ways to + get these IDs (e.g. journal) or similar ids (e.g. MAC addresses, DMI ids, CPU + ids), so this knob would only be useful in combination with other lockdown + options. Particularly useful for portable services, and anything else that + uses RootDirectory= or RootImage=. (Might also over-mount + /sys/class/dmi/id/*{uuid,serial} with /dev/null). + +* journalctl/timesyncd: whenever timesyncd acquires a synchronization from NTP, + create a structured log entry that contains boot ID, monotonic clock and + realtime clock (I mean, this requires no special work, as these three fields + are implicit). Then in journalctl when attempting to display the realtime + timestamp of a log entry, first search for the closest later log entry + of this kinda that has a matching boot id, and convert the monotonic clock + timestamp of the entry to the realtime clock using this info. This way we can + retroactively correct the wallclock timestamps, in particular for systems + without RTC, i.e. where initially wallclock timestamps carry rubbish, until + an NTP sync is acquired. + +* kernel-install: + - add --all switch for rerunning kernel-install for all installed kernels + - maybe add env var that shortcuts kernel-install for installers that want to + call it at the end only + +* doc: prep a document explaining resolved's internal objects, i.e. Query + vs. Question vs. Transaction vs. Stream and so on. + +* doc: prep a document explaining PID 1's internal logic, i.e. transactions, + jobs, units + +* bootspec: remove tries counter from boot entry ids + +* bootspec: bring UEFI and userspace enumeration of bootspec entries back into + sync, i.e. parse out tries in both + +* automatically ignore threaded cgroups in cg_xyz(). + * add linker script that implicitly adds symbol for build ID and new coredump json package metadata, and use that when logging @@ -215,21 +261,33 @@ Features: * rework recursive read-only remount to use new mount API -* PAM: pick auf one authentication token from credentials - -* tpm2: figure out if we need to do anything for TPM2 parameter encryption? And - if so, what precisely? +* PAM: pick up authentication token from credentials * when mounting disk images: if IMAGE_ID/IMAGE_VERSION is set in os-release data in the image, make sure the image filename actually matches this, so that images cannot be misused. * New udev block device symlink names: - /dev/disk/by-parttypelabel//. Use case: if pt label is used + /dev/disk/by-parttypelabel/-. Use case: if pt label is used as partition image version string, this is a safe way to reference a specific version of a specific partition type, in particular where related partitions are processed (e.g. verity + rootfs both named "LennartOS_0.7"). +* sysupdate: + - add fuzzing to the pattern parser + - support casync as download mechanism + - direct TPM2 PCR change handling, possible renrolling LUKS2 media if needed. + - "systemd-sysupdate update --all" support, that iterates through all components + defined on the host, plus all images installed into /var/lib/machines/, + /var/lib/portable/ and so on. + - figure out what to do about system extensions (i.e. they need to imply an + update component, since otherwise system extenion' sysupdate.d/ files would + override the host's update files.) + - Allow invocation with a single transfer definition, i.e. with + --definitions= pointing to a file rather than a dir. + - add ability to disable implicit decompression of downloaded artifacts, + i.e. a Compress=no option in the transfer definitions + * in sd-id128: also parse UUIDs in RFC4122 URN syntax (i.e. chop off urn:uuid: prefix) * DynamicUser= + StateDirectory= → use uid mapping mounts, too, in order to @@ -272,9 +330,6 @@ Features: * importd: support image signature verification with PKCS#7 + OpenBSD signify logic, as alternative to crummy gpg -* sysext: optionally, if the merged trees allow it use bind mounts instead of - overlayfs - * add "systemd-analyze debug" + AttachDebugger= in unit files: The former specifies a command to execute; the latter specifies that an already running "systemd-analyze debug" instance shall be contacted and execution paused @@ -303,16 +358,9 @@ Features: - make gatwayd/remote read key via creds logic - add sd_notify() command for flushing out creds not needed anymore -* teach LoadCredential= the ability to load all files from a specified dir as - individual creds - * add tpm.target or so which is delayed until TPM2 device showed up in case firmware indicates there is one. -* tpm2: support a PIN policy, i.e. allowing windows-style short authentication - passwords by using the TPM2 to enforce ratelimiting and such, use for - cryptsetup and homed - * Add concept for upgrading TPM2 enrollments, maybe a new switch --pcrs=4: or so, i.e. select a PCR to include in the hash, and then override its hash @@ -417,8 +465,6 @@ Features: * seccomp: don't install filters for ABIs that are masked anyway for the specific service -* seccomp: maybe merge all filters we install into one with that libseccomp API that allows merging. - * busctl: maybe expose a verb "ping" for pinging a dbus service to see if it exists and responds. @@ -598,9 +644,7 @@ Features: selected user is resolvable in the service even if it ships its own /etc/passwd) * Fix DECIMAL_STR_MAX or DECIMAL_STR_WIDTH. One includes a trailing NUL, the - other doesn't. What a disaster. Probably to exclude it. Also - DECIMAL_STR_WIDTH should probably add an extra "-" into account for negative - numbers. + other doesn't. What a disaster. Probably to exclude it. * Check that users of inotify's IN_DELETE_SELF flag are using it properly, as usually IN_ATTRIB is the right way to watch deleted files, as the former only @@ -700,7 +744,7 @@ Features: * beef up pam_systemd to take unit file settings such as cgroups properties as parameters -* maybe hook of xfs/ext4 quotactl() with services? i.e. automatically manage +* maybe hook up xfs/ext4 quotactl() with services? i.e. automatically manage the quota of the user indicated in User= via unit file settings, like the other resource management concepts. Would mix nicely with DynamicUser=1. Or alternatively, do this with projids, so that we can also cover services @@ -723,10 +767,6 @@ Features: ReadWritePaths=:/var/lib/foobar -* hostnamed: populate form factor data from a new hwdb database, so that old - yogas can be recognized as "convertible" too, even if they predate the DMI - "convertible" form factor - * Add ExecMonitor= setting. May be used multiple times. Forks off a process in the service cgroup, which is supposed to monitor the service, and when it exits the service is considered failed by its monitor. @@ -801,8 +841,6 @@ Features: * when we detect that there are waiting jobs but no running jobs, do something -* push CPUAffinity= also into the "cpuset" cgroup controller - * PID 1 should send out sd_notify("WATCHDOG=1") messages (for usage in the --user mode, and when run via nspawn) * there's probably something wrong with having user mounts below /sys, @@ -994,8 +1032,7 @@ Features: - add verification of [Install] section to systemd-analyze verify * timer units: - - timer units should get the ability to trigger when: - o DST changes + - timer units should get the ability to trigger when DST changes - Modulate timer frequency based on battery state * add libsystemd-password or so to query passwords during boot using the password agent logic @@ -1006,8 +1043,6 @@ Features: * make repeated alt-ctrl-del presses printing a dump -* hostnamed: before returning information from /etc/machine-info.conf check the modification data and reread. Similar for localed, ... - * currently x-systemd.timeout is lost in the initrd, since crypttab is copied into dracut, but fstab is not * add a pam module that passes the hdd passphrase into the PAM stack and then expires it, for usage by gdm auto-login. @@ -1126,6 +1161,10 @@ Features: - teach it to prepare an ESP wholesale, i.e. with mkfs.vfat invocation - teach it to copy in unified kernel images and maybe type #1 boot loader spec entries from host - make it operate on loopback files, dissecting enough to find ESP to operate on + - bootspec: properly support boot attempt counters when parsing entry file names + +* kernel-install: + - optionally, support generating type #2 entries instead of type #1, including signing them * logind: - logind: optionally, ignore idle-hint logic for autosuspend, block suspend as long as a session is around @@ -1312,6 +1351,10 @@ Features: can easily set overall quota for all users - on login, if we can't fallocate initially, but rebalance is on, then allow login in discard mode, then immediately rebalance, then turn off discard + - extend user records with optional "bulk" data. Specifically, a user + avatar/photo or so. This data should be stored along with the user record, + but probably shouldn't be part of the record itself, since it might be + large. * add a new switch --auto-definitions=yes/no or so to systemd-repart. If specified, synthesize a definition automatically if we can: enlarge last diff --git a/catalog/meson.build b/catalog/meson.build index 83c22d7d3..6a0a2193a 100644 --- a/catalog/meson.build +++ b/catalog/meson.build @@ -18,7 +18,7 @@ support_url = get_option('support-url') support_sed = 's~%SUPPORT_URL%~@0@~'.format(support_url) foreach file : in_files - custom_target( + catalogs += custom_target( file, input : file + '.in', output: file, diff --git a/catalog/systemd.catalog.in b/catalog/systemd.catalog.in index a5d7dc6f7..a3f05c069 100644 --- a/catalog/systemd.catalog.in +++ b/catalog/systemd.catalog.in @@ -526,3 +526,11 @@ be updated to operate in a hotplug fashion without depending on systemd-udev-settle.service: @OFFENDING_UNITS@ + +-- 7c8a41f37b764941a0e1780b1be2f037 +Subject: Initial clock synchronization +Defined-By: systemd +Support: %SUPPORT_URL% + +For the first time during the current boot an NTP synchronization has been +acquired and the local system clock adjustment has been initiated. diff --git a/coccinelle/macros.h b/coccinelle/macros.h index 0be4aaea4..6a0a64bb0 100644 --- a/coccinelle/macros.h +++ b/coccinelle/macros.h @@ -165,7 +165,7 @@ } while (false) #define LIST_JUST_US(name,item) \ - (!(item)->name##_prev && !(item)->name##_next) \ + (!(item)->name##_prev && !(item)->name##_next) #define LIST_FOREACH(name,i,head) \ for ((i) = (head); (i); (i) = (i)->name##_next) #define LIST_FOREACH_SAFE(name,i,n,head) \ diff --git a/coccinelle/mempcpy.cocci b/coccinelle/mempcpy.cocci new file mode 100644 index 000000000..efb657ae7 --- /dev/null +++ b/coccinelle/mempcpy.cocci @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +@@ +expression x, y, z; +@@ +- memcpy(x, y, z); +- x += z; ++ x = mempcpy(x, y, z); +@@ +expression x, y, z; +@@ +- memcpy_safe(x, y, z); +- x += z; ++ x = mempcpy_safe(x, y, z); diff --git a/coccinelle/timestamp-is-set.cocci b/coccinelle/timestamp-is-set.cocci new file mode 100644 index 000000000..2d251fa20 --- /dev/null +++ b/coccinelle/timestamp-is-set.cocci @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +@@ +expression x; +constant USEC_INFINITY = USEC_INFINITY; +/* We want to stick with the literal expression in the implementation of timestamp_is_set(), i.e. in time-util.c */ +position p : script:python() { p[0].file != "src/basic/time-util.h" }; +@@ +( +- x > 0 && x < USEC_INFINITY ++ timestamp_is_set(x) +| +- x < USEC_INFINITY && x > 0 ++ timestamp_is_set(x) +| +- x@p > 0 && x != USEC_INFINITY ++ timestamp_is_set(x) +| +- x != USEC_INFINITY && x > 0 ++ timestamp_is_set(x) +| +- x != 0 && x < USEC_INFINITY ++ timestamp_is_set(x) +| +- x < USEC_INFINITY && x != 0 ++ timestamp_is_set(x) +| +- x != 0 && x != USEC_INFINITY ++ timestamp_is_set(x) +| +- x != USEC_INFINITY && x != 0 ++ timestamp_is_set(x) +| +- !IN_SET(x, 0, USEC_INFINITY) ++ timestamp_is_set(x) +| +- !IN_SET(x, USEC_INFINITY, 0) ++ timestamp_is_set(x) +) +@@ +expression x; +constant USEC_INFINITY = USEC_INFINITY; +@@ +( +- x <= 0 || x >= USEC_INFINITY ++ !timestamp_is_set(x) +| +- x >= USEC_INFINITY || x <= 0 ++ !timestamp_is_set(x) +| +- x <= 0 || x == USEC_INFINITY ++ !timestamp_is_set(x) +| +- x == USEC_INFINITY || x <= 0 ++ !timestamp_is_set(x) +| +- x == 0 || x >= USEC_INFINITY ++ !timestamp_is_set(x) +| +- x >= USEC_INFINITY || x == 0 ++ !timestamp_is_set(x) +| +- x == 0 || x == USEC_INFINITY ++ !timestamp_is_set(x) +| +- x == USEC_INFINITY || x == 0 ++ !timestamp_is_set(x) +| +- IN_SET(x, 0, USEC_INFINITY) ++ !timestamp_is_set(x) +| +- IN_SET(x, USEC_INFINITY, 0) ++ !timestamp_is_set(x) +) diff --git a/docs/BLOCK_DEVICE_LOCKING.md b/docs/BLOCK_DEVICE_LOCKING.md index 428e4e3fb..13ae3f6e0 100644 --- a/docs/BLOCK_DEVICE_LOCKING.md +++ b/docs/BLOCK_DEVICE_LOCKING.md @@ -25,7 +25,7 @@ taking a BSD file lock on the block device node. Specifically, whenever lock using [`flock(2)`](http://man7.org/linux/man-pages/man2/flock.2.html) on the main block device (i.e. never on any partition block device, but on the device the partition belongs to). If this lock cannot be taken (i.e. `flock()` -returns `EBUSY`), it refrains from processing the device. If it manages to take +returns `EAGAIN`), it refrains from processing the device. If it manages to take the lock it is kept for the entire time the device is processed. Note that `systemd-udevd` also watches all block device nodes it manages for diff --git a/docs/BOOT_LOADER_INTERFACE.md b/docs/BOOT_LOADER_INTERFACE.md index 0e0eab7a2..a96c081a8 100644 --- a/docs/BOOT_LOADER_INTERFACE.md +++ b/docs/BOOT_LOADER_INTERFACE.md @@ -148,6 +148,6 @@ names for them in UIs. [Boot Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION)
[Discoverable Partitions Specification](https://systemd.io/DISCOVERABLE_PARTITIONS)
-[systemd-boot(7)](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)
-[bootctl(1)](https://www.freedesktop.org/software/systemd/man/bootctl.html)
-[systemd-gpt-auto-generator(8)](https://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html) +[`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)
+[`bootctl(1)`](https://www.freedesktop.org/software/systemd/man/bootctl.html)
+[`systemd-gpt-auto-generator(8)`](https://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html) diff --git a/docs/BOOT_LOADER_SPECIFICATION.md b/docs/BOOT_LOADER_SPECIFICATION.md index 8653a5bc0..5c93a0ec7 100644 --- a/docs/BOOT_LOADER_SPECIFICATION.md +++ b/docs/BOOT_LOADER_SPECIFICATION.md @@ -232,6 +232,16 @@ spaces from its value. The following keys are known: other installed operating systems. This ID shall be formatted as 32 lower case hexadecimal characters (i.e. without any UUID formatting). This key is optional. Example: `4098b3f648d74c13b1f04ccfba7798e8`. +* `sort-key` shall contain a short string used for sorting entries on + display. This can be defined freely though should typically be initialized + from `IMAGE_ID=` or `ID=` from `/etc/os-release` of the relevant entry, + possibly suffixed. This field is optional. If set, it is used as primary + sorting key for the entries on display (lexicographically increasing). It + does not have to be unique (and usually is not). If non-unique the the + `machine-id` (lexicographically increasing) and `version` (lexicographically + decreasing, i.e. newest version first) fields described above are used as + secondary/ternary sorting keys. If this field is not set entries are + typically sorted by the `.conf` file name of the entry. * `linux` refers to the Linux kernel to spawn and shall be a path relative to `$BOOT`. It is recommended that every distribution creates a machine id and version specific subdirectory below `$BOOT` and places its kernels and @@ -269,8 +279,9 @@ key and is otherwise not valid. Here's an example for a complete drop-in file: # /boot/loader/entries/6a9857a393724b7a981ebb5b8495b9ea-3.8.0-2.fc19.x86_64.conf title Fedora 19 (Rawhide) - version 3.8.0-2.fc19.x86_64 + sort-key fedora machine-id 6a9857a393724b7a981ebb5b8495b9ea + version 3.8.0-2.fc19.x86_64 options root=UUID=6d3376e4-fc93-4509-95ec-a21d68011da2 architecture x64 linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux @@ -298,6 +309,18 @@ focus for this specification. More specifically, on non-EFI systems configuration snippets following this specification cannot be used to spawn other operating systems (such as Windows). +Unfortunately, there are implementations of boot loading infrastructure that +are also using the /loader/entries/ directory, but place files in them that are +not valid by this specification. In order to minimize confusion a boot loader +implementation may place a file /loader/entries.srel next to the +/loader/entries/ directory containing the ASCII string "type1" (suffixed +with a UNIX newline). Tools that need to determine whether an existing +directory implements the semantics described here may check for this file and +contents: if it exists and contains the mentioned string, it shall assume a +standards compliant implementation is in place. If it exists but contains a +different string it shall assume non-standard semantics are implemented. If the +file does not exist no assumptions should be made. + ### Type #2 EFI Unified Kernel Images A unified kernel image is a single EFI PE executable combining an EFI stub @@ -358,10 +381,10 @@ simply reads all files `$BOOT/loader/entries/*.conf`, and populates its boot menu with this. On EFI, it then extends this with any unified kernel images found in `$BOOT/EFI/Linux/*.efi`. It may also add additional entries, for example a "Reboot into firmware" option. Optionally it may sort the menu based -on the `machine-id` and `version` fields, and possibly others. It uses the file -name to identify specific items, for example in case it supports storing away -default entry information somewhere. A boot loader should generally not modify -these files. +on the `sort-key`, `machine-id` and `version` fields, and possibly others. It +uses the file name to identify specific items, for example in case it supports +storing away default entry information somewhere. A boot loader should +generally not modify these files. For "Boot Loader Specification Entries" (Type #1), the _kernel package installer_ installs the kernel and initrd images to `$BOOT` (it is recommended @@ -417,6 +440,6 @@ There are a couple of items that are out of focus for this specification: [GUID Partition Table](https://en.wikipedia.org/wiki/GUID_Partition_Table)
[Boot Loader Interface](https://systemd.io/BOOT_LOADER_INTERFACE)
[Discoverable Partitions Specification](https://systemd.io/DISCOVERABLE_PARTITIONS)
-[systemd-boot(7)](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)
-[bootctl(1)](https://www.freedesktop.org/software/systemd/man/bootctl.html)
-[systemd-gpt-auto-generator(8)](https://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html) +[`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)
+[`bootctl(1)`](https://www.freedesktop.org/software/systemd/man/bootctl.html)
+[`systemd-gpt-auto-generator(8)`](https://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html) diff --git a/docs/BUILDING_IMAGES.md b/docs/BUILDING_IMAGES.md new file mode 100644 index 000000000..268c8cdb3 --- /dev/null +++ b/docs/BUILDING_IMAGES.md @@ -0,0 +1,267 @@ +--- +title: Safely Building Images +category: Concepts +layout: default +SPDX-License-Identifier: LGPL-2.1-or-later +--- + +# Safely Building Images + +In many scenarios OS installations are shipped as pre-built images, that +require no further installation process beyond simple `dd`-ing the image to +disk and booting it up. When building such "golden" OS images for +`systemd`-based OSes a few points should be taken into account. + +Most of the points described here are implemented by the +[`mkosi`](https://github.com/systemd/mkosi) OS image builder developed and +maintained by the systemd project. If you are using or working on another image +builder it's recommended to keep the following concepts and recommendations in +mind. + +## Resources to Reset + +Typically the same OS image shall be deployable in multiple instances, and each +instance should automatically acquire its own identifying credentials on first +boot. For that it's essential to: + +1. Remove the + [`/etc/machine-id`](https://www.freedesktop.org/software/systemd/man/machine-id.html) + file or write the string `uninitialized\n` into it. This file is supposed to + carry a 128bit identifier unique to the system. Only when it is reset it + will be auto-generated on first boot and thus be truly unique. If this file + is not reset, and carries a valid ID every instance of the system will come + up with the same ID and that will likely lead to problems sooner or later, + as many network-visible identifiers are commonly derived from the machine + ID, for example IPv6 addresses or transient MAC addresses. + +2. Remove the `/var/lib/systemd/random-seed` file (see + [`systemd-random-seed(8)`](https://www.freedesktop.org/software/systemd/man/systemd-random-seed.service.html)), + which is used to seed the kernel's random pool on boot. If this file is + shipped pre-initialized, every instance will seed its random pool with the + same random data that is included in the image, and thus possibly generate + random data that is more similar to other instances booted off the same + image than advisable. + +3. Remove the `/loader/random-seed` file (see + [`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)) + from the UEFI System Partition (ESP), in case the `systemd-boot` boot loader + is used in the image. + +4. It might also make sense to remove + [`/etc/hostname`](https://www.freedesktop.org/software/systemd/man/hostname.html) + and + [`/etc/machine-info`](https://www.freedesktop.org/software/systemd/man/machine-info.html) + which carry additional identifying information about the OS image. + +## Boot Menu Entry Identifiers + +The +[`kernel-install(8)`](https://www.freedesktop.org/software/systemd/man/kernel-install.html) +logic used to generate [Boot Loader Specification Type +1](https://systemd.io/BOOT_LOADER_SPECIFICATION) entries by default uses the +machine ID as stored in `/etc/machine-id` for naming boot menu entries and the +directories in the ESP to place kernel images in. This is done in order to +allow multiple installations of the same OS on the same system without +conflicts. However, this is problematic if the machine ID shall be generated +automatically on first boot: if the ID is not known before the first boot it +cannot be used to name the most basic resources required for the boot process +to complete. + +Thus, for images that shall acquire their identity on first boot only, it is +required to use a different identifier for naming boot menu entries. To allow +this the `kernel-install` logic knows the generalized *entry* *token* concept, +which can be a freely chosen string to use for identifying the boot menu +resources of the OS. If not configured explicitly it defaults to the machine +ID. The file `/etc/kernel/entry-token` may be used to configure this string +explicitly. Thus, golden image builders should write a suitable identifier into +this file, for example the `IMAGE_ID=` or `ID=` field from +[`/etc/os-release`](https://www.freedesktop.org/software/systemd/man/os-release.html) +(also see below). It is recommended to do this before the `kernel-install` +functionality is invoked (i.e. before the package manager is used to install +packages into the OS tree being prepared), so that the selected string is +automatically used for all entries to be generated. + +## Booting with Empty `/var/` and/or Empty Root File System + +`systemd` is designed to be able to come up safely and robustly if the `/var/` +file system or even the entire root file system (with exception of `/usr/`, +i.e. the vendor OS resources) is empty (i.e. "unpopulated"). With this in mind +it's relatively easy to build images that only ship a `/usr/` tree, and +otherwise carry no other data, populating the rest of the directory hierarchy +on first boot as needed. + +Specifically, the following mechanisms are in place: + +1. The `swich-root` logic in systemd, that is used to switch from the initrd + phase to the host will create the basic OS hierarchy skeleton if missing. It + will create a couple of directories strictly necessary to boot up + successfully, plus essential symlinks (such as those necessary for the + dynamic loader `ld.so` to function). + +2. PID 1 will initialize `/etc/machine-id` automatically if not initialized yet + (see above). + +3. The + [`nss-systemd(8)`](https://www.freedesktop.org/software/systemd/man/nss-systemd.html) + glibc NSS module ensures the `root` and `nobody` users and groups remain + resolvable, even without `/etc/passwd` and `/etc/group` around. + +4. The + [`systemd-sysusers(8)`](https://www.freedesktop.org/software/systemd/man/systemd-sysusers.service.html) + will component automatically populate `/etc/passwd` and `/etc/group` on + first boot with further necessary system users. + +5. The + [`systemd-tmpfiles(8)`](https://www.freedesktop.org/software/systemd/man/systemd-tmpfiles-setup.service.html) + component ensures that various files and directories below `/etc/`, `/var/` + and other places are created automatically at boot if missing. Unlike the + directories/symlinks created by the `switch-root` logic above this logic is + extensible by packages, and can adjust access modes, file ownership and + more. Among others this will also link `/etc/os-release` → + `/usr/lib/os-release`, ensuring that the OS release information is + unconditionally accessible through `/etc/os-release`. + +6. The + [`nss-myhostname(8)`](https://www.freedesktop.org/software/systemd/man/nss-myhostname.html) + glibc NSS module will ensure the local host name as well as `localhost` + remains resolvable, even without `/etc/hosts` around. + +With these mechanisms the hierarchies below `/var/` and `/etc/` can be safely +and robustly populated on first boot, so that the OS can safely boot up. Note +that some auxiliary package are not prepared to operate correctly if their +configuration data in `/etc/` or their state directories in `/var/` are +missing. This can typically be addressed via `systemd-tmpfiles` lines that +ensure the missing files and directories are created if missing. In particular, +configuration files that are necessary for operation can be automatically +copied or symlinked from the `/usr/share/factory/etc/` tree via the `C` or `L` +line types. That said, we recommend that all packages safely fall back to +internal defaults if their configuration is missing, making such additional +steps unnecessary. + +Note that while `systemd` itself explicitly supports booting up with entirely +unpopulated images (`/usr/` being the only required directory to be populated) +distributions might not be there yet: depending on your distribution further, +manual work might be required to make this scenario work. + +## Adapting OS Images to Storage + +Typically, if an image is `dd`-ed onto a target disk it will be minimal: +i.e. only consist of necessary vendor data, and lack "payload" data, that shall +be individual to the system, and dependent on host parameters. On first boot, +the OS should take possession of the backing storage as necessary, dynamically +using available space. Specifically: + +1. Additional partitions should be created, that make no sense to ship + pre-built in the image. For example `/tmp/` or `/home/` partitions, or even + `/var/` or the root file system (see above). + +2. Additional partitions should be created that shall function as A/B + secondaries for partitions shipped in the original image. In other words: if + the `/usr/` file system shall be updated in an A/B fashion it typically + makes sense to ship the original A file system in the deployed image, but + create the B partition on first boot. + +3. Partitions covering only a part of the disk should be grown to the full + extent of the disk. + +4. File systems in uninitialized partitions should be formatted with a file + system of choice. + +5. File systems covering only a part of a partition should be grown to the full + extent of the partition. + +6. Partitions should be encrypted with cryptographic keys generated locally on + the machine the system is first booted on, ensuring these keys remain local + and are not shared with any other instance of the OS image. + +Or any combination of the above: i.e. first create a partition, then encrypt +it, then format it. + +`systemd` provides multiple tools to implement the above logic: + +1. The + [`systemd-repart(8)`](https://www.freedesktop.org/software/systemd/man/systemd-repart.service.html) + component may manipulate GPT partition tables automatically on boot, growing + partitions or adding in partitions taking the backing storage size into + account. It can also encrypt partitions automatically it creates (even bind + to TPM2, automatically) and populate partitions from various sources. It + does this all in a robust fashion so that aborted invocations will not leave + incompletely set up partitions around. + +2. The + [`systemd-growfs@(8).service`](https://www.freedesktop.org/software/systemd/man/systemd-growfs.html) + tool can automatically grow a file system to the partition it is contained + in. The `x-systemd.growfs` mount option in `/etc/fstab` is sufficient to + enable this logic for specific mounts. Alternatively appropriately set up + partitions can set GPT partition flag 59 to request this behaviour, see the + [Discoverable Partitions + Specification](https://systemd.io/DISCOVERABLE_PARTITIONS) for details. If + the file system is already grown it executes no operation. + +3. Similar, the `systemd-makefs@.service` and `systemd-makeswap@.service` + services can format file systems and swap spaces before first use, if they + carry no file system signature yet. The `x-systemd.makefs` mount option in + `/etc/fstab` may be used to request this functionality. + +## Provisioning Image Settings + +While a lot of work has gone into ensuring `systemd` systems can safely boot +with unpopulated `/etc/` trees, it sometimes is desirable to set a couple of +basic settings *after* `dd`-ing the image to disk, but *before* first boot. For +this the tool +[`systemd-firstboot(1)`](https://www.freedesktop.org/software/systemd/man/systemd-firstboot.html) +can be useful, with its `--image=` switch. It may be used to set very basic +settings, such as the root password or hostname on an OS disk image or +installed block device. + +## Distinguishing First Boot + +For various purposes it's useful to be able to distinguish the first boot-up of +the system from later boot-ups (for example, to set up TPM hardware +specifically, or register a system somewhere). `systemd` provides mechanisms to +implement that. Specifically, the `ConditionFirstBoot=` and `AssertFirstBoot=` +settings may be used to conditionalize units to only run on first boot. See +[`systemd.unit(5)`](https://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConditionFirstBoot=) +for details. + +A special target unit `first-boot-complete.target` may be used as milestone to +safely handle first boots where the system is powered off too early: if the +first boot process is aborted before this target is reached, the following boot +process will be considered a first boot, too. Once the target is reached, +subsequent boots will not be considered first boots anymore, even if the boot +process is aborted immediately after. Thus, services that must complete fully +before a system shall be considered fully past the first boot should be ordered +before this target unit. + +Whether a system will come up in first boot state or not is derived from the +initialization status of `/etc/machine-id`: if the file already carries a valid +ID the system is already past the first boot. If it is not initialized yet it +is still considered in the first boot state. For details see +[`machine-id(5)`](https://www.freedesktop.org/software/systemd/man/machine-id.html). + +## Image Metadata + +Typically, when operating with golden disk images it is useful to be able to +identify them and their version. For this the two fields `IMAGE_ID=` and +`IMAGE_VERSION=` have been defined in +[`os-release(5)`](https://www.freedesktop.org/software/systemd/man/os-release.html). These +fields may be accessed from unit files and similar via the `%M` and `%A` +specifiers. + +Depending on how the images are put together it might make sense to leave the +OS distribution's `os-release` file as is in `/usr/lib/os-release` but to +replace the usual `/etc/os-release` symlink with a regular file that extends +the distribution's file with one augmented with these two additional +fields. + +## Links + +[`machine-id(5)`](https://www.freedesktop.org/software/systemd/man/machine-id.html)
+[`systemd-random-seed(8)`](https://www.freedesktop.org/software/systemd/man/systemd-random-seed.service.html)
+[`os-release(5)`](https://www.freedesktop.org/software/systemd/man/os-release.html)
+[Boot Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION)
+[Discoverable Partitions Specification](https://systemd.io/DISCOVERABLE_PARTITIONS)
+[`mkosi`](https://github.com/systemd/mkosi)
+[`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)
+[`systemd-repart(8)`](https://www.freedesktop.org/software/systemd/man/systemd-repart.service.html)
+[`systemd-growfs@(8).service`](https://www.freedesktop.org/software/systemd/man/systemd-growfs.html)
diff --git a/docs/CGROUP_DELEGATION.md b/docs/CGROUP_DELEGATION.md index aeb2be97b..8b14acf4e 100644 --- a/docs/CGROUP_DELEGATION.md +++ b/docs/CGROUP_DELEGATION.md @@ -253,6 +253,13 @@ So, if you want to do your own raw cgroups kernel level access, then allocate a scope unit, or a service unit (or just use the service unit you already have for your service code), and turn on delegation for it. +The service manager sets the `user.delegate` extended attribute (readable via +`getxattr(2)` and related calls) to the character `1` on cgroup directories +where delegation is enabled (and removes it on those cgroups where it is +not). This may be used by service programs to determine whether a cgroup tree +was delegated to them. Note that this is only supported on kernels 5.6 and +newer in combination with systemd 251 and newer. + (OK, here's one caveat: if you turn on delegation for a service, and that service has `ExecStartPost=`, `ExecReload=`, `ExecStop=` or `ExecStopPost=` set, then these commands will be executed within the `.control/` sub-cgroup of @@ -266,6 +273,15 @@ tree by the time it notifies the service manager about start-up readiness, so that the service's main cgroup is definitely an inner node by the time the service manager might start `ExecStartPost=`.) +(Also note, if you intend to use "threaded" cgroups — as added in Linux 4.14 —, +then you should do that *two* levels down from the main service cgroup your +turned delegation on for. Why that? You need one level so that systemd can +properly create the `.control` subgroup, as described above. But that one +cannot be threaded, since that would mean `.control` has to be threaded too — +this is a requirement of threaded cgroups: either a cgroup and all its siblings +are threaded or none –, but systemd expects it to be a regular cgroup. Thus you +have to nest a second cgroup beneath it which then can be threaded.) + ## Three Scenarios Let's say you write a container manager, and you wonder what to do regarding @@ -356,7 +372,7 @@ but of course that's between you and those other tenants, and systemd won't care. Replicating the cgroup hierarchies in those unsupported controllers would mean replicating the full cgroup paths in them, and hence the prefixing `.slice` components too, otherwise the hierarchies will start being orthogonal -after all, and that's not really desirable. On more thing: systemd will clean +after all, and that's not really desirable. One more thing: systemd will clean up after you in the hierarchies it manages: if your daemon goes down, its cgroups will be removed too. You basically get the guarantee that you start with a pristine cgroup sub-tree for your service or scope whenever it is diff --git a/docs/CODE_QUALITY.md b/docs/CODE_QUALITY.md index 4b76a1055..b1f7dd109 100644 --- a/docs/CODE_QUALITY.md +++ b/docs/CODE_QUALITY.md @@ -51,8 +51,8 @@ available functionality: 9. There are multiple CI systems in use that run on every github PR submission. -10. [Coverity](https://scan.coverity.com/) is analyzing systemd master in - regular intervals. The reports are available +10. [Coverity](https://scan.coverity.com/) is analyzing systemd `main` branch + in regular intervals. The reports are available [online](https://scan.coverity.com/projects/systemd). 11. [oss-fuzz](https://oss-fuzz.com/) is continuously fuzzing the @@ -65,7 +65,7 @@ available functionality: 13. When building systemd from a git checkout the build scripts will automatically enable a git commit hook that ensures whitespace cleanliness. -14. [LGTM](https://lgtm.com/) analyzes every commit pushed to master. The list +14. [LGTM](https://lgtm.com/) analyzes every commit pushed to `main`. The list of active alerts can be found [here](https://lgtm.com/projects/g/systemd/systemd/alerts/?mode=list). @@ -75,7 +75,7 @@ available functionality: for more information. 16. Fossies provides [source code misspelling reports](https://fossies.org/features.html#codespell). - The systemd report can be found [here](https://fossies.org/linux/test/systemd-master.tar.gz/codespell.html). + The systemd report can be found [here](https://fossies.org/linux/misc/systemd/codespell.html). Access to Coverity and oss-fuzz reports is limited. Please reach out to the maintainers if you need access. diff --git a/docs/CODING_STYLE.md b/docs/CODING_STYLE.md index eba1e1444..20e725149 100644 --- a/docs/CODING_STYLE.md +++ b/docs/CODING_STYLE.md @@ -79,8 +79,61 @@ SPDX-License-Identifier: LGPL-2.1-or-later dont_find_waldo(); ``` +- Please define flags types like this: + + ```c + typedef enum FoobarFlags { + FOOBAR_QUUX = 1 << 0, + FOOBAR_WALDO = 1 << 1, + FOOBAR_XOXO = 1 << 2, + … + } FoobarFlags; + ``` + + i.e. use an enum for it, if possible. Indicate bit values via `1 <<` + expressions, and align them vertically. Define both an enum and a type for + it. + +- If you define (non-flags) enums, follow this template: + + ```c + typedef enum FoobarMode { + FOOBAR_AAA, + FOOBAR_BBB, + FOOBAR_CCC, + … + _FOOBAR_MAX, + _FOOBAR_INVALID = -EINVAL, + } FoobarMode; + ``` + + i.e. define a `_MAX` enum for the largest defined enum value, plus one. Since + this is not a regular enum value, prefix it with `_`. Also, define a special + "invalid" enum value, and set it to `-EINVAL`. That way the enum type can + safely be used to propagate conversion errors. + +- If you define an enum in a public API, be extra careful, as the size of the + enum might change when new values are added, which would break ABI + compatibility. Since we typically want to allow adding new enum values to an + existing enum type with later API versions, please use the + `_SD_ENUM_FORCE_S64()` macro in the enum definition, which forces the size of + the enum to be signed 64bit wide. + ## Code Organization and Semantics +- For our codebase we intend to use ISO C11 *with* GNU extensions (aka + "gnu11"). Public APIs (i.e. those we expose via `libsystemd.so` + i.e. `systemd/sd-*.h`) should only use ISO C89 however (with a very limited + set of conservative and common extensions, such as fixed size integer types + from ``), so that we don't force consuming programs into C11 + mode. (This discrepancy in particular means one thing: internally we use C99 + `bool` booleans, externally C89-compatible `int` booleans which generally + have different size in memory and slightly different semantics, also see + below.) Both for internal and external code it's OK to use even newer + features and GCC extension than "gnu11", as long as there's reasonable + fallback #ifdeffery in place to ensure compatibility is retained with older + compilers. + - Please name structures in `PascalCase` (with exceptions, such as public API structs), variables and functions in `snake_case`. @@ -358,7 +411,7 @@ SPDX-License-Identifier: LGPL-2.1-or-later - For every function you add, think about whether it is a "logging" function or a "non-logging" function. "Logging" functions do (non-debug) logging on their - own, "non-logging" function never log on their own (except at debug level) + own, "non-logging" functions never log on their own (except at debug level) and expect their callers to log. All functions in "library" code, i.e. in `src/shared/` and suchlike must be "non-logging". Every time a "logging" function calls a "non-logging" function, it should log about the resulting @@ -491,7 +544,8 @@ SPDX-License-Identifier: LGPL-2.1-or-later - Use the bool type for booleans, not integers. One exception: in public headers (i.e those in `src/systemd/sd-*.h`) use integers after all, as `bool` - is C99 and in our public APIs we try to stick to C89 (with a few extensions). + is C99 and in our public APIs we try to stick to C89 (with a few extensions; + also see above). ## Deadlocks @@ -518,7 +572,7 @@ SPDX-License-Identifier: LGPL-2.1-or-later - It's a good idea to use `O_NONBLOCK` when opening 'foreign' regular files, i.e. file system objects that are supposed to be regular files whose paths - where specified by the user and hence might actually refer to other types of + were specified by the user and hence might actually refer to other types of file system objects. This is a good idea so that we don't end up blocking on 'strange' file nodes, for example if the user pointed us to a FIFO or device node which may block when opening. Moreover even for actual regular files diff --git a/docs/CONTAINER_INTERFACE.md b/docs/CONTAINER_INTERFACE.md index 1332ed3e4..757207ce9 100644 --- a/docs/CONTAINER_INTERFACE.md +++ b/docs/CONTAINER_INTERFACE.md @@ -37,18 +37,18 @@ manager, please consider supporting the following interfaces. in this context.) 3. Pre-mount `/dev/` as (container private) `tmpfs` for the container and bind - mount some suitable TTY to `/dev/console`. If this is a pty, make sure to not - close the controlling pty master during systemd's lifetime. PID1 will close + mount some suitable TTY to `/dev/console`. If this is a pty, make sure to + not close the controlling pty during systemd's lifetime. PID1 will close ttys, to avoid being killed by SAK. It only opens ttys for the time it - actually needs to print something. Also, make sure to create device - nodes for `/dev/null`, `/dev/zero`, `/dev/full`, `/dev/random`, - `/dev/urandom`, `/dev/tty`, `/dev/ptmx` in `/dev/`. It is not necessary to - create `/dev/fd` or `/dev/stdout`, as systemd will do that on its own. Make - sure to set up a `BPF_PROG_TYPE_CGROUP_DEVICE` BPF program — on cgroupv2 — - or the `devices` cgroup controller — on cgroupv1 — so that no other devices - but these may be created in the container. Note that many systemd services - use `PrivateDevices=`, which means that systemd will set up a private - `/dev/` for them for which it needs to be able to create these device nodes. + actually needs to print something. Also, make sure to create device nodes + for `/dev/null`, `/dev/zero`, `/dev/full`, `/dev/random`, `/dev/urandom`, + `/dev/tty`, `/dev/ptmx` in `/dev/`. It is not necessary to create `/dev/fd` + or `/dev/stdout`, as systemd will do that on its own. Make sure to set up a + `BPF_PROG_TYPE_CGROUP_DEVICE` BPF program — on cgroupv2 — or the `devices` + cgroup controller — on cgroupv1 — so that no other devices but these may be + created in the container. Note that many systemd services use + `PrivateDevices=`, which means that systemd will set up a private `/dev/` + for them for which it needs to be able to create these device nodes. Dropping `CAP_MKNOD` for containers is hence generally not advisable, but see below. @@ -277,7 +277,7 @@ care should be taken to avoid naming conflicts. `systemd` (and in particular 1. Do not drop `CAP_MKNOD` from the container. `PrivateDevices=` is a commonly used service setting that provides a service with its own, private, minimal version of `/dev/`. To set this up systemd in the container needs this - capability. If you take away the capability than all services that set this + capability. If you take away the capability, then all services that set this flag will cease to work. Use `BPF_PROG_TYPE_CGROUP_DEVICE` BPF programs — on cgroupv2 — or the `devices` controller — on cgroupv1 — to restrict what device nodes the container can create instead of taking away the capability diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 67ebcf3b9..ebfc31aa2 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -31,7 +31,7 @@ See [reporting of security vulnerabilities](SECURITY.md). ## Posting Pull Requests -* Make sure to post PRs only relative to a very recent git master. +* Make sure to post PRs only relative to a very recent git tip. * Follow our [Coding Style](CODING_STYLE.md) when contributing code. This is a requirement for all code we merge. * Please make sure to test your change before submitting the PR. See the [Hacking guide](HACKING.md) for details on how to do this. * Make sure to run the test suite locally, before posting your PR. We use a CI system, meaning we don't even look at your PR, if the build and tests don't pass. diff --git a/docs/COREDUMP_PACKAGE_METADATA.md b/docs/COREDUMP_PACKAGE_METADATA.md index db75ad143..8dac5c61b 100644 --- a/docs/COREDUMP_PACKAGE_METADATA.md +++ b/docs/COREDUMP_PACKAGE_METADATA.md @@ -92,9 +92,9 @@ $ ./generate-package-notes.py --rpm systemd-248~rc2-1.fc33.arm32 --cpe cpe:/o:fe SECTIONS { .note.package (READONLY) : ALIGN(4) { - BYTE(0x04) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Owner including NUL */ - BYTE(0x7b) BYTE(0x00) BYTE(0x00) BYTE(0x00) /* Length of Value including NUL */ - BYTE(0x7e) BYTE(0x1a) BYTE(0xfe) BYTE(0xca) /* Note ID */ + LONG(0x0004) /* Length of Owner including NUL */ + LONG(0x007b) /* Length of Value including NUL */ + LONG(0xcafe1a7e) /* Note ID */ BYTE(0x46) BYTE(0x44) BYTE(0x4f) BYTE(0x00) /* Owner: 'FDO\x00' */ BYTE(0x7b) BYTE(0x22) BYTE(0x74) BYTE(0x79) /* Value: '{"type":"rpm","name":"systemd","version":"248~rc2-1.fc33","architecture":"arm32","osCpe":"cpe:/o:fedoraproject:fedora:33"}\x00\x00' */ BYTE(0x70) BYTE(0x65) BYTE(0x22) BYTE(0x3a) diff --git a/docs/DESKTOP_ENVIRONMENTS.md b/docs/DESKTOP_ENVIRONMENTS.md index b5195da26..0a0eff653 100644 --- a/docs/DESKTOP_ENVIRONMENTS.md +++ b/docs/DESKTOP_ENVIRONMENTS.md @@ -97,13 +97,13 @@ to create appropriate units for the autostart directory (`systemd-xdg-autostart-generator`). Desktop Environments can opt-in to using this by starting `xdg-desktop-autostart.target`. The systemd generator correctly handles -`OnlyShowIn=` and `NotShowin=`. It also handles the KDE and GNOME specific -`X-KDE-autostart-condition=` and `AutostartCondition=` by using desktop -environment provided binaries in an `ExecCondition=` line. +`OnlyShowIn=` and `NotShowIn=`. It also handles the KDE and GNOME specific +`X-KDE-autostart-condition=` and `AutostartCondition=` by using desktop-environment-provided +binaries in an `ExecCondition=` line. However, this generator is somewhat limited in what it supports. For example, all generated units will have `After=graphical-session.target` set on them, -it may therefore not be useful to start session services. +and therefore may not be useful to start session services. Desktop files can be marked to be explicitly excluded from the generator using the line `X-systemd-skip=true`. This should be set if an application provides its own diff --git a/docs/DISCOVERABLE_PARTITIONS.md b/docs/DISCOVERABLE_PARTITIONS.md index 1223ae716..85e858921 100644 --- a/docs/DISCOVERABLE_PARTITIONS.md +++ b/docs/DISCOVERABLE_PARTITIONS.md @@ -407,3 +407,12 @@ available. The `gdisk` tool (from version 1.0.5 onward) and its variants (`sgdisk`, `cgdisk`) also support creation of partitions with a matching type code. + +## Links + +[Boot Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION)
+[Boot Loader Interface](https://systemd.io/BOOT_LOADER_INTERFACE)
+[Safely Building Images](https://systemd.io/BUILDING_IMAGES)
+[`systemd-boot(7)`](https://www.freedesktop.org/software/systemd/man/systemd-boot.html)
+[`bootctl(1)`](https://www.freedesktop.org/software/systemd/man/bootctl.html)
+[`systemd-gpt-auto-generator(8)`](https://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html) diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 12b4cad25..0cbe5cfb6 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -43,6 +43,11 @@ All tools: debugging, in order to test generators and other code against specific kernel command lines. +* `$SYSTEMD_OS_RELEASE` — if set, use this path instead of `/etc/os-release` or + `/usr/lib/os-release`. When operating under some root (e.g. `systemctl + --root=…`), the path is taken relative to the outside root. Only useful for + debugging. + * `$SYSTEMD_FSTAB` — if set, use this path instead of `/etc/fstab`. Only useful for debugging. @@ -97,9 +102,6 @@ All tools: systems built with libxcrypt and is ignored on systems using glibc's original, internal `crypt()` implementation.) -* `$SYSTEMD_RDRAND=0` — if set, the RDRAND instruction will never be used, - even if the CPU supports it. - * `$SYSTEMD_SECCOMP=0` — if set, seccomp filters will not be enforced, even if support for it is compiled in and available in the kernel. @@ -321,7 +323,7 @@ fuzzers: * `$SYSTEMD_FUZZ_RUNS` — The number of times execution should be repeated in manual invocations. -Note that is may be also useful to set `$SYSTEMD_LOG_LEVEL`, since all logging +Note that it may be also useful to set `$SYSTEMD_LOG_LEVEL`, since all logging is suppressed by default. `systemd-importd`: diff --git a/docs/GVARIANT-SERIALIZATION.md b/docs/GVARIANT-SERIALIZATION.md index c999fdd58..3dca54ebe 100644 --- a/docs/GVARIANT-SERIALIZATION.md +++ b/docs/GVARIANT-SERIALIZATION.md @@ -7,7 +7,7 @@ SPDX-License-Identifier: LGPL-2.1-or-later # GVariant D-Bus Message Serialization -We stay close to the original dbus1 framing as possible, but make +We stay as close to the original dbus1 framing as possible, but make certain changes to adapt for GVariant. dbus1 has the following framing: diff --git a/docs/HACKING.md b/docs/HACKING.md index e194df64c..4b334715c 100644 --- a/docs/HACKING.md +++ b/docs/HACKING.md @@ -27,7 +27,7 @@ Please also have a look at our list of [code quality tools](CODE_QUALITY.md) we have setup for systemd, to ensure our codebase stays in good shape. Please always test your work before submitting a PR. For many of the components -of systemd testing is straight-forward as you can simply compile systemd and +of systemd testing is straightforward as you can simply compile systemd and run the relevant tool from the build directory. For some components (most importantly, systemd/PID1 itself) this is not @@ -330,3 +330,43 @@ To debug systemd components other than PID 1, set "program" to the full path of debug and set "processId" to "${command:pickProcess}". Now, when starting the debugger, VSCode will ask you the PID of the process you want to debug. Run `systemctl show --property MainPID --value ` in the container to figure out the PID and enter it when asked and VSCode will attach to that process instead. + +# Debugging systemd-boot + +During boot, systemd-boot and the stub loader will output a message like `systemd-boot@0x0A,0x0B`, +providing the location of the text and data sections. These location can then be used to attach +to a QEMU session (provided it was run with `-s`) with these gdb commands: + +``` + (gdb) file build/src/boot/efi/systemd-bootx64.efi + (gdb) add-symbol-file build/src/boot/efi/systemd_boot.so 0x0A -s .data 0x0B + (gdb) set architecture i386:x86-64 + (gdb) target remote :1234 +``` + +This process can be automated by using the `debug-sd-boot.sh` script in the tools folder. If run +without arguments it will provide usage information. + +If the debugger is too slow to attach to examine an early boot code passage, we can uncomment the +call to `debug_break()` inside of `efi_main()`. As soon as the debugger has control we can then run +`set variable wait = 0` or `return` to continue. Once the debugger has attached, setting breakpoints +will work like usual. + +To debug systemd-boot in an IDE such as VSCode we can use a launch configuration like this: +```json +{ + "name": "systemd-boot", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/build/src/boot/efi/systemd-bootx64.efi", + "cwd": "${workspaceFolder}", + "MIMode": "gdb", + "miDebuggerServerAddress": ":1234", + "setupCommands": [ + { "text": "shell mkfifo /tmp/sdboot.{in,out}" }, + { "text": "shell qemu-system-x86_64 [...] -s -serial pipe:/tmp/sdboot" }, + { "text": "shell ${workspaceFolder}/tools/debug-sd-boot.sh ${workspaceFolder}/build/src/boot/efi/systemd-bootx64.efi /tmp/sdboot.out systemd-boot.gdb" }, + { "text": "source /tmp/systemd-boot.gdb" }, + ] +} +``` diff --git a/docs/HOME_DIRECTORY.md b/docs/HOME_DIRECTORY.md index 142da3a87..680b2ed23 100644 --- a/docs/HOME_DIRECTORY.md +++ b/docs/HOME_DIRECTORY.md @@ -9,11 +9,11 @@ SPDX-License-Identifier: LGPL-2.1-or-later [`systemd-homed.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-homed.service.html) manages home directories of regular ("human") users. Each directory it manages -encapsulates both the data store and the user record of the user so that it +encapsulates both the data store and the user record of the user, so that it comprehensively describes the user account, and is thus naturally portable between systems without any further, external metadata. This document describes -the format used by these home directories, in context of the storage mechanism -used. +the format used by these home directories, in the context of the storage +mechanism used. ## General Structure @@ -22,7 +22,7 @@ user record of the user. It follows the format defined in [`JSON User Records`](https://systemd.io/USER_RECORD). It is recommended to bring the record into 'normalized' form (i.e. all objects should contain their fields sorted alphabetically by their key) before storing it there, though this is not -required nor enforced. Since the user record is cryptographically signed the +required nor enforced. Since the user record is cryptographically signed, the user cannot make modifications to the file on their own (at least not without corrupting it, or knowing the private key used for signing the record). Note that user records are stored here without their `binding`, `status` and @@ -35,13 +35,13 @@ If the plain directory or `btrfs` subvolume storage mechanism of `systemd-homed` is used (i.e. `--storage=directory` or `--storage=subvolume` on the [`homectl(1)`](https://www.freedesktop.org/software/systemd/man/homectl.html) -command line) the home directory requires no special set-up besides including +command line) the home directory requires no special setup besides including the user record in the `~/.identity` file. It is recommended to name home directories managed this way by `systemd-homed.service` by the user name, suffixed with `.homedir` (example: `lennart.homedir` for a user `lennart`) but this is not enforced. When the user -is logged in the directory is generally mounted to `/home/$USER` (in our +is logged in, the directory is generally mounted to `/home/$USER` (in our example: `/home/lennart`), thus dropping the suffix while the home directory is active. `systemd-homed` will automatically discover home directories named this way in `/home/*.homedir` and synthesize NSS user records for them as they show @@ -54,19 +54,19 @@ mechanism, except that the home directory is encrypted using `fscrypt`. (Use `--storage=fscrypt` on the `homectl` command line.) Key management is implemented via extended attributes on the directory itself: for each password an extended attribute `trusted.fscrypt_slot0`, `trusted.fscrypt_slot1`, -`trusted.fscrypt_slot2`, … is maintained. It's value contains a colon-separated +`trusted.fscrypt_slot2`, … is maintained. Its value contains a colon-separated pair of Base64 encoded data fields. The first field contains a salt value, the second field the encrypted volume key. The latter is encrypted using AES256 in -counter mode, using a key derived from the password via PBKDF2-HMAC-SHA512 +counter mode, using a key derived from the password via PBKDF2-HMAC-SHA512, together with the salt value. The construction is similar to what LUKS does for `dm-crypt` encrypted volumes. Note that extended attributes are not encrypted -by `fscrypt` and hence are suitable for carry the key slots. Moreover, by using -extended attributes the slots are directly attached to the directory and an -independent sidecar key database is not required. +by `fscrypt` and hence are suitable for carrying the key slots. Moreover, by +using extended attributes, the slots are directly attached to the directory and +an independent sidecar key database is not required. ## Storage Mechanism: `cifs` Home Directories -In this storage mechanism the home directory is mounted from a CIFS server and +In this storage mechanism, the home directory is mounted from a CIFS server and service at login, configured inside the user record. (Use `--storage=cifs` on the `homectl` command line.) The local password of the user is used to log into the CIFS service. The directory share needs to contain the user record in @@ -85,7 +85,7 @@ media). (Use `--storage=luks` on the `homectl` command line.) Specifically: * The image contains a GPT partition table. For now it should only contain a single partition, and that partition must have the type UUID - `773f91ef-66d4-49b5-bd83-d683bf40ad16`. It's partition label must be the + `773f91ef-66d4-49b5-bd83-d683bf40ad16`. Its partition label must be the user name. * This partition must contain a LUKS2 volume, whose label must be the user @@ -106,24 +106,24 @@ media). (Use `--storage=luks` on the `homectl` command line.) Specifically: contains a second copy of the user record in the `~/.identity` file, like in the other storage mechanisms. -The image file should either reside in a directory `/home/` on the system, -named after the user, suffixed with `.home`. When activated the container home +The image file should reside in a directory `/home/` on the system, +named after the user, suffixed with `.home`. When activated, the container home directory is mounted to the same path, though with the `.home` suffix dropped — unless a different mount point is defined in the user record. (e.g.: the loopback file `/home/waldo.home` is mounted to `/home/waldo` while activated.) -When the image is stored on removable media (such as a USB stick) the image -file can be directly `dd`'ed onto it, the format is unchanged. The GPT envelope +When the image is stored on removable media (such as a USB stick), the image +file can be directly `dd`'ed onto it; the format is unchanged. The GPT envelope should ensure the image is properly recognizable as a home directory both when used in a loopback file and on a removable USB stick. (Note that when mounting -a home directory from an USB stick it too defaults to a directory in `/home/`, +a home directory from an USB stick, it too defaults to a directory in `/home/`, named after the username, with no further suffix.) Rationale for the GPT partition table envelope: this way the image is nicely discoverable and recognizable already by partition managers as a home directory. Moreover, when copied onto a USB stick the GPT envelope makes sure the stick is properly recognizable as a portable home directory -medium. (Moreover it allows to embed additional partitions later on, for -example for allowing a multi-purpose USB stick that contains both a home +medium. (Moreover, it allows embedding additional partitions later on, for +example on a multi-purpose USB stick that contains both a home directory and a generic storage volume.) Rationale for including the encrypted user record in the LUKS2 header: @@ -133,7 +133,7 @@ images can be used as attack vectors, exploiting the kernel. Thus it is necessary to validate the home directory image *before* mounting it and establishing a minimal level of trust. Since the user record data is cryptographically signed and user records not signed with a recognized private -key are not accepted a minimal level of trust between the system and the home +key are not accepted, a minimal level of trust between the system and the home directory image is established. Rationale for storing the home directory one level below to root directory of @@ -145,10 +145,10 @@ do not show up in the user's home directory. Regardless of the storage mechanism used, an activated home directory necessarily involves a mount point to be established. In case of the directory-based storage mechanisms (`directory`, `subvolume` and `fscrypt`) -this is a bind mount, in case of `cifs` this is a CIFS network mount, and in +this is a bind mount. In case of `cifs` this is a CIFS network mount, and in case of the LUKS2 backend a regular block device mount of the file system contained in the LUKS2 image. By requiring a mount for all cases (even for -those that already are a directory) a clear logic is defined to distinguish +those that already are a directory), a clear logic is defined to distinguish active and inactive home directories, so that the directories become inaccessible under their regular path the instant they are deactivated. Moreover, the `nosuid`, `nodev` and `noexec` flags configured in @@ -161,7 +161,7 @@ compared. Activation is only permitted if they match the same user and are signed by a recognized key. When the three instances differ in `lastChangeUSec` field, the newest record wins, and is propagated to the other two locations. -During activation the file system checker (`fsck`) appropriate for the +During activation, the file system checker (`fsck`) appropriate for the selected file system is automatically invoked, ensuring the file system is in a healthy state before it is mounted. @@ -169,7 +169,7 @@ If the UID assigned to a user does not match the owner of the home directory in the file system, the home directory is automatically and recursively `chown()`ed to the correct UID. -Depending on the `luksDiscard` setting of the user record either the backing +Depending on the `luksDiscard` setting of the user record, either the backing loopback file is `fallocate()`ed during activation, or the mounted file system is `FITRIM`ed after mounting, to ensure the setting is correctly enforced. diff --git a/docs/INITRD_INTERFACE.md b/docs/INITRD_INTERFACE.md index 2d1d0ac60..3898840c5 100644 --- a/docs/INITRD_INTERFACE.md +++ b/docs/INITRD_INTERFACE.md @@ -12,7 +12,7 @@ The Linux initrd mechanism (short for "initial RAM disk") refers to a small file system archive that is unpacked by the kernel and contains the first userspace code that runs. It typically finds and transitions into the actual root file system to use. systemd supports both initrd and initrd-less boots. If -an initrd is used it is a good idea to pass a few bits of runtime information +an initrd is used, it is a good idea to pass a few bits of runtime information from the initrd to systemd in order to avoid duplicate work and to provide performance data to the administrator. In this page we attempt to roughly describe the interfaces that exist between the initrd and systemd. These diff --git a/docs/JOURNAL_EXPORT_FORMATS.md b/docs/JOURNAL_EXPORT_FORMATS.md new file mode 100644 index 000000000..441872b08 --- /dev/null +++ b/docs/JOURNAL_EXPORT_FORMATS.md @@ -0,0 +1,156 @@ +--- +title: Journal Export Format +category: Interfaces +layout: default +SPDX-License-Identifier: LGPL-2.1-or-later +--- + +# Journal Export Format + +_Note that this document describes the binary serialization format of journals only, as used for transfer across the network. +For interfacing with web technologies there's the Journal JSON Format, described below. +The binary format on disk is documented as the [Journal File Format](https://systemd.io/JOURNAL_FILE_FORMAT/)._ + +_Before reading on, please make sure you are aware of the [basic properties of journal entries](https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html), in particular realize that they may include binary non-text data (though usually don't), and the same field might have multiple values assigned within the same entry (though usually hasn't)._ + +When exporting journal data for other uses or transferring it via the network/local IPC the _journal export format_ is used. It's a simple serialization of journal entries, that is easy to read without any special tools, but still binary safe where necessary. The format is like this: + +* Two journal entries that follow each other are separated by a double newline. +* Journal fields consisting only of valid non-control UTF-8 codepoints are serialized as they are (i.e. the field name, followed by '=', followed by field data), followed by a newline as separator to the next field. Note that fields containing newlines cannot be formatted like this. Non-control UTF-8 codepoints are the codepoints with value at or above 32 (' '), or equal to 9 (TAB). +* Other journal fields are serialized in a special binary safe way: field name, followed by newline, followed by a binary 64bit little endian size value, followed by the binary field data, followed by a newline as separator to the next field. +* Entry metadata that is not actually a field is serialized like it was a field, but beginning with two underscores. More specifically, `__CURSOR=`, `__REALTIME_TIMESTAMP=`, `__MONOTONIC_TIMESTAMP=` are introduced this way. Note that these meta-fields are only generated when actual journal files are serialized. They are omitted for entries that do not originate from a journal file (for example because they are transferred for the first time to be stored in one). Or in other words: if you are generating this format you shouldn't care about these special double-underscore fields. But you might find them usable when you deserialize the format generated by us. Additional fields prefixed with two underscores might be added later on, your parser should skip over the fields it does not know. +* The order in which fields appear in an entry is undefined and might be different for each entry that is serialized. +And that's already it. + +This format can be generated via `journalctl -o export`. + +Here's an example for two serialized entries which consist only of text data: + +``` +__CURSOR=s=739ad463348b4ceca5a9e69c95a3c93f;i=4ece7;b=6c7c6013a26343b29e964691ff25d04c;m=4fc72436e;t=4c508a72423d9;x=d3e5610681098c10;p=system.journal +__REALTIME_TIMESTAMP=1342540861416409 +__MONOTONIC_TIMESTAMP=21415215982 +_BOOT_ID=6c7c6013a26343b29e964691ff25d04c +_TRANSPORT=syslog +PRIORITY=4 +SYSLOG_FACILITY=3 +SYSLOG_IDENTIFIER=gdm-password] +SYSLOG_PID=587 +MESSAGE=AccountsService-DEBUG(+): ActUserManager: ignoring unspecified session '8' since it's not graphical: Success +_PID=587 +_UID=0 +_GID=500 +_COMM=gdm-session-wor +_EXE=/usr/libexec/gdm-session-worker +_CMDLINE=gdm-session-worker [pam/gdm-password] +_AUDIT_SESSION=2 +_AUDIT_LOGINUID=500 +_SYSTEMD_CGROUP=/user/lennart/2 +_SYSTEMD_SESSION=2 +_SELINUX_CONTEXT=system_u:system_r:xdm_t:s0-s0:c0.c1023 +_SOURCE_REALTIME_TIMESTAMP=1342540861413961 +_MACHINE_ID=a91663387a90b89f185d4e860000001a +_HOSTNAME=epsilon + +__CURSOR=s=739ad463348b4ceca5a9e69c95a3c93f;i=4ece8;b=6c7c6013a26343b29e964691ff25d04c;m=4fc72572f;t=4c508a7243799;x=68597058a89b7246;p=system.journal +__REALTIME_TIMESTAMP=1342540861421465 +__MONOTONIC_TIMESTAMP=21415221039 +_BOOT_ID=6c7c6013a26343b29e964691ff25d04c +_TRANSPORT=syslog +PRIORITY=6 +SYSLOG_FACILITY=9 +SYSLOG_IDENTIFIER=/USR/SBIN/CROND +SYSLOG_PID=8278 +MESSAGE=(root) CMD (run-parts /etc/cron.hourly) +_PID=8278 +_UID=0 +_GID=0 +_COMM=run-parts +_EXE=/usr/bin/bash +_CMDLINE=/bin/bash /bin/run-parts /etc/cron.hourly +_AUDIT_SESSION=8 +_AUDIT_LOGINUID=0 +_SYSTEMD_CGROUP=/user/root/8 +_SYSTEMD_SESSION=8 +_SELINUX_CONTEXT=system_u:system_r:crond_t:s0-s0:c0.c1023 +_SOURCE_REALTIME_TIMESTAMP=1342540861416351 +_MACHINE_ID=a91663387a90b89f185d4e860000001a +_HOSTNAME=epsilon + +``` + +A message with a binary field produced by +```bash +python3 -c 'from systemd import journal; journal.send("foo\nbar")' +journalctl -n1 -o export +``` + +``` +__CURSOR=s=bcce4fb8ffcb40e9a6e05eee8b7831bf;i=5ef603;b=ec25d6795f0645619ddac9afdef453ee;m=545242e7049;t=50f1202 +__REALTIME_TIMESTAMP=1423944916375353 +__MONOTONIC_TIMESTAMP=5794517905481 +_BOOT_ID=ec25d6795f0645619ddac9afdef453ee +_TRANSPORT=journal +_UID=1001 +_GID=1001 +_CAP_EFFECTIVE=0 +_SYSTEMD_OWNER_UID=1001 +_SYSTEMD_SLICE=user-1001.slice +_MACHINE_ID=5833158886a8445e801d437313d25eff +_HOSTNAME=bupkis +_AUDIT_LOGINUID=1001 +_SELINUX_CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 +CODE_LINE=1 +CODE_FUNC= +SYSLOG_IDENTIFIER=python3 +_COMM=python3 +_EXE=/usr/bin/python3.4 +_AUDIT_SESSION=35898 +_SYSTEMD_CGROUP=/user.slice/user-1001.slice/session-35898.scope +_SYSTEMD_SESSION=35898 +_SYSTEMD_UNIT=session-35898.scope +MESSAGE +^G^@^@^@^@^@^@^@foo +bar +CODE_FILE= +_PID=16853 +_CMDLINE=python3 -c from systemd import journal; journal.send("foo\nbar") +_SOURCE_REALTIME_TIMESTAMP=1423944916372858 +``` + +# Journal JSON Format + +_Note that this section describes the JSON serialization format of the journal only, as used for interfacing with web technologies. +For binary transfer of journal data across the network there's the Journal Export Format described above. +The binary format on disk is documented as [Journal File Format](https://systemd.io/JOURNAL_FILE_FORMAT)._ + +_Before reading on, please make sure you are aware of the [basic properties of journal entries](https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html), in particular realize that they may include binary non-text data (though usually don't), and the same field might have multiple values assigned within the same entry (though usually hasn't)._ + +In most cases the Journal JSON serialization is the obvious mapping of the entry field names (as JSON strings) to the entry field values (also as JSON strings) encapsulated in one JSON object. However, there are a few special cases to handle: + +* A field that contains non-printable or non-UTF8 is serialized as a number array instead. This is necessary to handle binary data in a safe way without losing data, since JSON cannot embed binary data natively. Each byte of the binary field will be mapped to its numeric value in the range 0…255. +* The JSON serializer can optionally skip huge (as in larger than a specific threshold) data fields from the JSON object. If that is enabled and a data field is too large, the field name is still included in the JSON object but assigned _null_. +* Within the same entry, Journal fields may have multiple values assigned. This is not allowed in JSON. The serializer will hence create a single JSON field only for these cases, and assign it an array of values (which the can be strings, _null_ or number arrays, see above). +* If the JSON data originates from a journal file it may include the special addressing fields `__CURSOR`, `__REALTIME_TIMESTAMP`, `__MONOTONIC_TIMESTAMP`, which contain the cursor string of this entry as string, and the realtime/monotonic timestamps of this entry as formatted numeric string of usec since the respective epoch. + +Here's an example, illustrating all cases mentioned above. Consider this entry: + +``` +MESSAGE=Hello World +_UDEV_DEVNODE=/dev/waldo +_UDEV_DEVLINK=/dev/alias1 +_UDEV_DEVLINK=/dev/alias2 +BINARY=this is a binary value \a +LARGE=this is a super large value (let's pretend at least, for the sake of this example) +``` + +This translates into the following JSON Object: +```json +{ + "MESSAGE" : "Hello World", + "_UDEV_DEVNODE" : "/dev/waldo", + "_UDEV_DEVLINK" : [ "/dev/alias1", "/dev/alias2" ], + "BINARY" : [ 116, 104, 105, 115, 32, 105, 115, 32, 97, 32, 98, 105, 110, 97, 114, 121, 32, 118, 97, 108, 117, 101, 32, 7 ], + "LARGE" : null +} +``` diff --git a/docs/JOURNAL_FILE_FORMAT.md b/docs/JOURNAL_FILE_FORMAT.md index f9c9fcb31..0036ee45d 100644 --- a/docs/JOURNAL_FILE_FORMAT.md +++ b/docs/JOURNAL_FILE_FORMAT.md @@ -297,7 +297,7 @@ STATE_ARCHIVED. If a writer is asked to write to a file that is not in STATE_OFFLINE it should immediately rotate the file and start a new one, without changing the file. -After and before the state field is changed `fdatasync()` should be executed on +After and before the state field is changed, `fdatasync()` should be executed on the file to ensure the dirty state hits disk. diff --git a/docs/JOURNAL_NATIVE_PROTOCOL.md b/docs/JOURNAL_NATIVE_PROTOCOL.md index 657eca25a..77fb27fa0 100644 --- a/docs/JOURNAL_NATIVE_PROTOCOL.md +++ b/docs/JOURNAL_NATIVE_PROTOCOL.md @@ -17,7 +17,7 @@ SPDX-License-Identifier: LGPL-2.1-or-later The latter is what this document is about: if you are developing a program and want to pass structured log data to `journald`, it's the Journal's native -protocol what you want to use. The systemd project provides the +protocol that you want to use. The systemd project provides the [`sd_journal_print(3)`](https://www.freedesktop.org/software/systemd/man/sd_journal_print.html) API that implements the client side of this protocol. This document explains what this interface does behind the scenes, in case you'd like to implement a @@ -60,7 +60,7 @@ bytes however, as well as any other binary data. Keys may not include the `=` or newline characters (or any other control characters or non-ASCII characters) and may not be empty. -Serialization into the datagram payload or `memfd` is straight-forward: each +Serialization into the datagram payload or `memfd` is straightforward: each key/value pair is serialized via one of two methods: * The first method inserts a `=` character between key and value, and suffixes @@ -185,7 +185,7 @@ took place for the current program. If you are looking for alternative implementations of this protocol (besides systemd's own in `sd_journal_print()`), consider -[GLib's](https://gitlab.gnome.org/GNOME/glib/-/blob/master/glib/gmessages.c) or +[GLib's](https://gitlab.gnome.org/GNOME/glib/-/blob/main/glib/gmessages.c) or [`dbus-broker`'s](https://github.com/bus1/dbus-broker/blob/main/src/util/log.c). And that's already all there is to it. diff --git a/docs/PASSWORD_AGENTS.md b/docs/PASSWORD_AGENTS.md index 7d810fbbd..ed204911b 100644 --- a/docs/PASSWORD_AGENTS.md +++ b/docs/PASSWORD_AGENTS.md @@ -34,7 +34,7 @@ It is easy to write additional agents. The basic algorithm to follow looks like Again, it is essential that you stop showing the password box/notification/status icon if the `ask.xxx` file is removed or when `NotAfter=` elapses (if it is set `!= 0`)! -It may happen that multiple password entries are pending at the same time. Your agent needs to be able to deal with that. Depending on your environment you may either choose to show all outstanding passwords at the same time or instead only one and as soon as the user replied to that one go on to the next one. +It may happen that multiple password entries are pending at the same time. Your agent needs to be able to deal with that. Depending on your environment you may either choose to show all outstanding passwords at the same time or instead only one and as soon as the user has replied to that one go on to the next one. You may test this all with manually invoking the "`systemd-ask-password`" tool on the command line. Pass `--no-tty` to ensure the password is asked via the agent system. Note that only privileged users may use this tool (after all this is intended purely for system-level passwords). diff --git a/docs/PORTABILITY_AND_STABILITY.md b/docs/PORTABILITY_AND_STABILITY.md index 0b92fda27..bfe5b6aa8 100644 --- a/docs/PORTABILITY_AND_STABILITY.md +++ b/docs/PORTABILITY_AND_STABILITY.md @@ -101,6 +101,7 @@ And now, here's the list of (hopefully) all APIs that we have introduced with sy | [Link file format](https://www.freedesktop.org/software/systemd/man/systemd.link.html) | File format | yes | yes | no | no | - | no | | [Journal File Format](https://systemd.io/JOURNAL_FILE_FORMAT) | File format | yes | yes | - | maybe | - | no | | [Journal Export Format](https://systemd.io/JOURNAL_EXPORT_FORMATS#journal-export-format) | File format | yes | yes | - | yes | - | no | +| [Journal JSON Format](https://systemd.io/JOURNAL_EXPORT_FORMATS#journal-json-format) | File format | yes | yes | - | yes | - | no | | [Cooperation in cgroup tree](https://www.freedesktop.org/wiki/Software/systemd/PaxControlGroups) | Treaty | yes | yes | libvirt | yes | libvirt | no | | [Password Agents](https://systemd.io/PASSWORD_AGENTS) | Socket+Files | yes | yes | - | yes | - | no | | [udev multi-seat properties](https://www.freedesktop.org/software/systemd/man/sd-login.html) | udev Property | yes | yes | X11, gdm | no | - | no | diff --git a/docs/PORTABLE_SERVICES.md b/docs/PORTABLE_SERVICES.md index dd9164126..7f67be2b0 100644 --- a/docs/PORTABLE_SERVICES.md +++ b/docs/PORTABLE_SERVICES.md @@ -284,9 +284,12 @@ following must be also be observed: 4. The upper extension(s) image(s) must at least contain one matching unit file each, with the right name prefix and suffix (see above). +5. As with the base/OS image, the upper extension(s) image(s) must be a plain + sub-directory, a btrfs subvolume or a raw disk image. + ``` # portablectl attach --extension foobar_0.7.23.raw debian-runtime_11.1.raw foobar -# portablectl attach --extension barbaz_7.0.23.raw debian-runtime_11.1.raw barbaz +# portablectl attach --extension barbaz_7.0.23/ debian-runtime_11.1.raw barbaz ``` ## Execution Environment @@ -331,3 +334,10 @@ behaviour, by setting the `ProtectSystem=strict` option. In this case writable service data may be placed on the host file system. Use `StateDirectory=` in the unit files to enable such behaviour and add a local data directory to the services copied onto the host. + +## Links + +[`portablectl(1)`](https://www.freedesktop.org/software/systemd/man/portablectl.html)
+[`systemd-portabled.service(8)`](https://www.freedesktop.org/software/systemd/man/systemd-portabled.service.html)
+[Walkthrough for Portable Services](https://0pointer.net/blog/walkthrough-for-portable-services.html)
+[Repo with examples](https://github.com/systemd/portable-walkthrough) diff --git a/docs/PORTING_TO_NEW_ARCHITECTURES.md b/docs/PORTING_TO_NEW_ARCHITECTURES.md index 964eccb85..5c6148148 100644 --- a/docs/PORTING_TO_NEW_ARCHITECTURES.md +++ b/docs/PORTING_TO_NEW_ARCHITECTURES.md @@ -53,9 +53,6 @@ architecture. support booting into OS trees that have an empty root directory with only `/usr/` mounted in. -7. If your architecture has a CPU opcode similar to x86' RDRAND consider adding - native support for it to `src/basic/random-util.c`'s `rdrand()` function. - -8. If your architecture supports VM virtualization and provides CPU opcodes +7. If your architecture supports VM virtualization and provides CPU opcodes similar to x86' CPUID consider adding native support for detecting VMs this way to `src/basic/virt.c`. diff --git a/docs/RANDOM_SEEDS.md b/docs/RANDOM_SEEDS.md index 347321405..1c3897303 100644 --- a/docs/RANDOM_SEEDS.md +++ b/docs/RANDOM_SEEDS.md @@ -144,33 +144,11 @@ acquired. ## Keeping `systemd'`s Demand on the Kernel Entropy Pool Minimal Since most of systemd's own use of random numbers do not require -cryptographic-grade RNGs, it tries to avoid reading entropy from the kernel -entropy pool if possible. If it succeeds this has the benefit that there's no -need to delay the early boot process until entropy is available, and noisy -kernel log messages about early reading from `/dev/urandom` are avoided -too. Specifically: - -1. When generating [Type 4 - UUIDs](https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_\(random\)), - systemd tries to use Intel's and AMD's RDRAND CPU opcode directly, if - available. While some doubt the quality and trustworthiness of the entropy - provided by these opcodes, they should be good enough for generating UUIDs, - if not key material (though, as mentioned, today's big distributions opted - to trust it for that too, now, see above — but we are not going to make that - decision for you, and for anything key material related will only use the - kernel's entropy pool). If RDRAND is not available or doesn't work, it will - use synchronous `getrandom()` as fallback, and `/dev/urandom` on old kernels - where that system call doesn't exist yet. This means on non-Intel/AMD - systems UUID generation will block on kernel entropy initialization. - -2. For seeding hash tables, and all the other similar purposes systemd first - tries RDRAND, and if that's not available will try to use asynchronous - `getrandom()` (if the kernel doesn't support this system call, - `/dev/urandom` is used). This may fail too in case the pool is not - initialized yet, in which case it will fall back to glibc's internal rand() - calls, i.e. weak pseudo-random numbers. This should make sure we use good - random bytes if we can, but neither delay boot nor trigger noisy kernel log - messages during early boot for these use-cases. +cryptographic-grade RNGs, it tries to avoid blocking reads to the kernel's RNG, +opting instead for using `getrandom(GRND_INSECURE)`. After the pool is +initialized, this is identical to `getrandom(0)`, returning cryptographically +secure random numbers, but before it's initialized it has the nice effect of +not blocking system boot. ## `systemd`'s Support for Filling the Kernel Entropy Pool @@ -280,10 +258,8 @@ early-boot entropy in most cases. Specifically: hosting provider if they don't. For VMs used in testing environments, `systemd.random_seed=` may be used as an alternative to a virtualized RNG. -3. On Intel/AMD systems systemd's own reliance on the kernel entropy pool is - minimal (as RDRAND is used on those for UUID generation). This only works if - the CPU has RDRAND of course, which most physical CPUs do (but I hear many - virtualized CPUs do not. Pity.) +3. In general, systemd's own reliance on the kernel entropy pool is minimal + (due to the use of `GRND_INSECURE`). 4. In all other cases, `systemd-random-seed.service` will help a bit, but — as mentioned — is too late to help with early boot. diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 168f957c7..c5da09b62 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -21,4 +21,4 @@ SPDX-License-Identifier: LGPL-2.1-or-later 12. "Draft" a new release on github (https://github.com/systemd/systemd/releases/new), mark "This is a pre-release" if appropriate. 13. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically. 14. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released`) -15. [FINAL] Push commits to stable, create an empty -stable branch: `git push systemd-stable origin/master:master origin/master:refs/heads/${version}-stable`, and change the default branch to latest release (https://github.com/systemd/systemd-stable/settings/branches). +15. [FINAL] Push commits to stable, create an empty -stable branch: `git push systemd-stable --atomic origin/main:main origin/main:refs/heads/${version}-stable`, and change the default branch to latest release (https://github.com/systemd/systemd-stable/settings/branches). diff --git a/docs/RESOLVED-VPNS.md b/docs/RESOLVED-VPNS.md index 89a5cdfac..e06fbc3d2 100644 --- a/docs/RESOLVED-VPNS.md +++ b/docs/RESOLVED-VPNS.md @@ -66,7 +66,7 @@ a network interface may configure. differentiate them. i.e. `~foo.com` is a configured routing domain, while `foo.com` would be a configured search domain. - One routing domain is particular interesting: `~.` — the catch-all routing + One routing domain is particularly interesting: `~.` — the catch-all routing domain. (The *dot* domain `.` is how DNS denotes the "root" domain, i.e. the parent domain of all domains, but itself.) When used on an interface any DNS traffic is preferably routed to its DNS servers. (A search domain – i.e. `.` diff --git a/docs/UIDS-GIDS.md b/docs/UIDS-GIDS.md index ea7ec6396..e90d6f059 100644 --- a/docs/UIDS-GIDS.md +++ b/docs/UIDS-GIDS.md @@ -81,7 +81,7 @@ available during earliest boot, including in the initial RAM disk). above). However, it does define some special group/GID assignments, which are primarily used for `systemd-udevd`'s device management. The precise list of the currently defined groups is found in this `sysusers.d` snippet: -[basic.conf](https://raw.githubusercontent.com/systemd/systemd/master/sysusers.d/basic.conf.in) +[basic.conf](https://raw.githubusercontent.com/systemd/systemd/main/sysusers.d/basic.conf.in) It's strongly recommended that downstream distributions include these groups in their default group databases. @@ -176,7 +176,7 @@ Systemd has compile-time default for these boundaries. Using those defaults is recommended. It will nevertheless query `/etc/login.defs` at runtime, when compiled with `-Dcompat-mutable-uid-boundaries=true` and that file is present. Support for this is considered only a compatibility feature and should not be -used except when upgrading systems which were creating with different defaults. +used except when upgrading systems which were created with different defaults. ## Considerations for container managers @@ -233,6 +233,27 @@ safely use the NSS user database as allocation check, too. Note that if you follow this scheme no changes to `/etc/passwd` need to be made, thus minimizing the artifacts the container manager persistently leaves in the system. +5. `systemd-homed` by default mounts the home directories it manages with UID +mapping applied. It will map four UID ranges into that uidmap, and leave +everything else unmapped: the range from 0…60000, the user's own UID, the range +60514…65534, and the container range 524288…1879048191. This means +files/directories in home directories managed by `systemd-homed` cannot be +owned by UIDs/GIDs outside of these four ranges (attempts to `chown()` files to +UIDs outside of these ranges will fail). Thus, if container trees are to be +placed within a home directory managed by `systemd-homed` they should take +these ranges into consideration and either place the trees at base UID 0 (and +then map them to a higher UID range for use in user namespacing via another +level of UID mapped mounts, at *runtime*) or at a base UID from the container +UID range. That said, placing container trees (and in fact any +files/directories not owned by the home directory's user) in home directories +is generally a questionable idea (regardless of whether `systemd-homed` is used +or not), given this typically breaks quota assumptions, makes it impossible for +users to properly manage all files in their own home directory due to +permission problems, introduces security issues around SETUID and severely +restricts compatibility with networked home directories. Typically, it's a much +better idea to place container images outside of the home directory, +i.e. somewhere below `/var/` or similar. + ## Summary | UID/GID | Purpose | Defined By | Listed in | @@ -255,18 +276,19 @@ the artifacts the container manager persistently leaves in the system. | 2147483648…4294967294 | HIC SVNT LEONES | | | | 4294967295 | 32bit `(uid_t) -1` | Linux | | -Note that "Unused" in the table above doesn't meant that these ranges are +Note that "Unused" in the table above doesn't mean that these ranges are really unused. It just means that these ranges have no well-established pre-defined purposes between Linux, generic low-level distributions and `systemd`. There might very well be other packages that allocate from these ranges. Note that the range 2147483648…4294967294 (i.e. 2^31…2^32-2) should be handled -with care. Various programs (including kernel file systems, see `devpts`) have -trouble with UIDs outside of the signed 32bit range, i.e any UIDs equal to or -above 2147483648. It is thus strongly recommended to stay away from this range -in order to avoid complications. This range should be considered reserved for -future, special purposes. +with care. Various programs (including kernel file systems — see `devpts` — or +even kernel syscalls – see `setfsuid()`) have trouble with UIDs outside of the +signed 32bit range, i.e any UIDs equal to or above 2147483648. It is thus +strongly recommended to stay away from this range in order to avoid +complications. This range should be considered reserved for future, special +purposes. ## Notes on resolvability of user and group names diff --git a/docs/USER_GROUP_API.md b/docs/USER_GROUP_API.md index cefe6d3dc..ccd9f9b3d 100644 --- a/docs/USER_GROUP_API.md +++ b/docs/USER_GROUP_API.md @@ -241,7 +241,7 @@ about existence or non-existence of a record can be returned nor any user record at all. (The `service` field is defined in order to allow implementation of daemons that provide multiple distinct user/group services over the same `AF_UNIX` socket: in order to correctly determine which service a client wants -to talk to the client needs to provide the name in each request.) +to talk to, the client needs to provide the name in each request.) The `GetGroupRecord` method call works analogously but for groups. @@ -257,7 +257,7 @@ with `more` set, so that multiple replies can be returned (since typically there are multiple members per group and also multiple groups a user is member of). As with `GetUserRecord` and `GetGroupRecord` the `service` parameter needs to contain the name of the service being talked to, in order to -allow implementation of multiple service within the same IPC socket. In case no +allow implementation of multiple services within the same IPC socket. In case no matching membership is known `NoRecordFound` is returned. The other two errors are also generated in the same cases as for `GetUserRecord` and `GetGroupRecord`. @@ -270,7 +270,7 @@ before the complete list is acquired. Note that only the `GetMemberships` call is authoritative about memberships of users in groups. i.e. it should not be considered sufficient to check the `memberOf` field of user records and the `members` field of group records to -acquire the full list of memberships. The full list can only bet determined by +acquire the full list of memberships. The full list can only be determined by `GetMemberships`, and as mentioned requires merging of these lists of all local services. Result of this is that it can be one service that defines a user A, and another service that defines a group B, and a third service that declares diff --git a/docs/USER_NAMES.md b/docs/USER_NAMES.md index 1757c5b78..bef5f4877 100644 --- a/docs/USER_NAMES.md +++ b/docs/USER_NAMES.md @@ -114,7 +114,7 @@ warning is shown if the specified user name does not qualify by the strict rules above. * No embedded NUL bytes (rationale: handling in C must be possible and - straight-forward) + straightforward) * No names consisting fully of digits (rationale: avoid confusion with numeric UID/GID specifications) diff --git a/docs/USER_RECORD.md b/docs/USER_RECORD.md index bac0ce172..da911d5e7 100644 --- a/docs/USER_RECORD.md +++ b/docs/USER_RECORD.md @@ -333,7 +333,7 @@ values, which is then inherited by all the user's processes, see [`setrlimit()`](http://man7.org/linux/man-pages/man2/setrlimit.2.html) for more information. -`locked` → A boolean value. If true the user account is locked, the user may +`locked` → A boolean value. If true, the user account is locked, the user may not log in. If this field is missing it should be assumed to be false, i.e. logins are permitted. This field corresponds to the `sp_expire` field of `struct spwd` (i.e. the `/etc/shadow` data for a user) being set to zero or @@ -359,11 +359,11 @@ directory, also containing the `~/.identity` user record; `luks` is a per-user LUKS volume that is mounted as home directory, and `cifs` a home directory mounted from a Windows File Share. The five latter types are primarily used by `systemd-homed` when managing home directories, but may be used if other -managers are used too. If this is not set `classic` is the implied default. +managers are used too. If this is not set, `classic` is the implied default. `diskSize` → An unsigned 64bit integer, indicating the intended home directory disk space in bytes to assign to the user. Depending on the selected storage -type this might be implement differently: for `luks` this is the intended size +type this might be implemented differently: for `luks` this is the intended size of the file system and LUKS volume, while for the others this likely translates to classic file system quota settings. @@ -425,7 +425,7 @@ the top-level directory of the CIFS share is used. `imagePath` → A string with an absolute file system path to the file, directory or block device to use for storage backing the home directory. If the `luks` -storage is used this refers to the loopback file or block device node to store +storage is used, this refers to the loopback file or block device node to store the LUKS volume on. For `fscrypt`, `directory`, `subvolume` this refers to the directory to bind mount as home directory on login. Not defined for `classic` or `cifs`. @@ -465,7 +465,7 @@ relevant when the storage mechanism used is `luks`. referencing the file system UUID the home directory is located in. This is primarily relevant when the storage mechanism used is `luks`. -`luksDiscard` → A boolean. If true and `luks` storage is used controls whether +`luksDiscard` → A boolean. If true and `luks` storage is used, controls whether the loopback block devices, LUKS and the file system on top shall be used in `discard` mode, i.e. erased sectors should always be returned to the underlying storage. If false and `luks` storage is used turns this behavior off. In @@ -579,7 +579,7 @@ against all plugged in security tokens and if there's exactly one matching private key found with it it is used. `fido2HmacCredential` → An array of strings, each with a Base64-encoded FIDO2 -credential ID that shell be used for authentication with FIDO2 devices that +credential ID that shall be used for authentication with FIDO2 devices that implement the `hmac-secret` extension. The salt to pass to the FIDO2 device is found in `fido2HmacSalt`. diff --git a/docs/_includes/footer.html b/docs/_includes/footer.html index ebde9dc7b..b107e77fb 100644 --- a/docs/_includes/footer.html +++ b/docs/_includes/footer.html @@ -1,7 +1,7 @@ diff --git a/factory/templates/locale.conf.in b/factory/templates/locale.conf.in new file mode 100644 index 000000000..af93d043e --- /dev/null +++ b/factory/templates/locale.conf.in @@ -0,0 +1,3 @@ +# This is the fallback locale configuration provided by systemd. + +LANG="{{ SYSTEMD_DEFAULT_LOCALE }}" diff --git a/factory/templates/meson.build b/factory/templates/meson.build new file mode 100644 index 000000000..821f176a7 --- /dev/null +++ b/factory/templates/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +factory_etc_dir = factorydir / 'etc' + +custom_target( + 'locale.conf', + input : 'locale.conf.in', + output : 'locale.conf', + command : [meson_render_jinja2, config_h, '@INPUT@', '@OUTPUT@'], + install : true, + install_dir : factory_etc_dir) diff --git a/hwdb.d/20-OUI.hwdb b/hwdb.d/20-OUI.hwdb index a97406d45..4b8f83cc9 100644 --- a/hwdb.d/20-OUI.hwdb +++ b/hwdb.d/20-OUI.hwdb @@ -573,7 +573,7 @@ OUI:0000BC* ID_OUI_FROM_DATABASE=Rockwell Automation OUI:0000BD* - ID_OUI_FROM_DATABASE=Mitsubishi Cable Industries, Ltd. / Ryosei Systems + ID_OUI_FROM_DATABASE=RYOSEI, Ltd. OUI:0000BE* ID_OUI_FROM_DATABASE=THE NTI GROUP @@ -10413,7 +10413,7 @@ OUI:000DA8* ID_OUI_FROM_DATABASE=Teletronics Technology Corporation OUI:000DA9* - ID_OUI_FROM_DATABASE=T.E.A.M. S.L. + ID_OUI_FROM_DATABASE=INGETEAM OUI:000DAA* ID_OUI_FROM_DATABASE=S.A.Tehnology co.,Ltd. @@ -11922,7 +11922,7 @@ OUI:000F9F* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. OUI:000FA0* - ID_OUI_FROM_DATABASE=CANON KOREA BUSINESS SOLUTIONS INC. + ID_OUI_FROM_DATABASE=Canon Korea Inc. OUI:000FA1* ID_OUI_FROM_DATABASE=Gigabit Systems Inc. @@ -14187,7 +14187,7 @@ OUI:001292* ID_OUI_FROM_DATABASE=Griffin Technology OUI:001293* - ID_OUI_FROM_DATABASE=ABB Power Protection (CH) + ID_OUI_FROM_DATABASE=ABB Switzerland Ltd. OUI:001294* ID_OUI_FROM_DATABASE=SUMITOMO ELECTRIC DEVICE INNOVATIONS, INC @@ -14961,7 +14961,7 @@ OUI:001394* ID_OUI_FROM_DATABASE=Infohand Co.,Ltd OUI:001395* - ID_OUI_FROM_DATABASE=congatec AG + ID_OUI_FROM_DATABASE=congatec GmbH OUI:001396* ID_OUI_FROM_DATABASE=Acbel Polytech Inc. @@ -15054,7 +15054,7 @@ OUI:0013B3* ID_OUI_FROM_DATABASE=Ecom Communications Technology Co., Ltd. OUI:0013B4* - ID_OUI_FROM_DATABASE=Appear TV + ID_OUI_FROM_DATABASE=Appear AS OUI:0013B5* ID_OUI_FROM_DATABASE=Wavesat @@ -17307,7 +17307,7 @@ OUI:0016A2* ID_OUI_FROM_DATABASE=CentraLite Systems, Inc. OUI:0016A3* - ID_OUI_FROM_DATABASE=Ingeteam Transmission&Distribution, S.A. + ID_OUI_FROM_DATABASE=INGETEAM OUI:0016A4* ID_OUI_FROM_DATABASE=Ezurio Ltd @@ -29529,7 +29529,7 @@ OUI:0025C9* ID_OUI_FROM_DATABASE=SHENZHEN HUAPU DIGITAL CO., LTD OUI:0025CA* - ID_OUI_FROM_DATABASE=LS Research, LLC + ID_OUI_FROM_DATABASE=Laird Connectivity OUI:0025CB* ID_OUI_FROM_DATABASE=Reiner SCT @@ -29700,7 +29700,7 @@ OUI:002603* ID_OUI_FROM_DATABASE=Shenzhen Wistar Technology Co., Ltd OUI:002604* - ID_OUI_FROM_DATABASE=Audio Processing Technology Ltd + ID_OUI_FROM_DATABASE=WorldCast Systems OUI:002605* ID_OUI_FROM_DATABASE=CC Systems AB @@ -35768,6 +35768,9 @@ OUI:009569* OUI:0097FF* ID_OUI_FROM_DATABASE=Heimann Sensor GmbH +OUI:00991D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:009ACD* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -36563,6 +36566,9 @@ OUI:00A0FF* OUI:00A1DE* ID_OUI_FROM_DATABASE=ShenZhen ShiHua Technology CO.,LTD +OUI:00A265* + ID_OUI_FROM_DATABASE=M2Motive Technology Inc. + OUI:00A289* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -36593,6 +36599,9 @@ OUI:00A45F* OUI:00A509* ID_OUI_FROM_DATABASE=WigWag Inc. +OUI:00A554* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:00A5BF* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -37694,6 +37703,9 @@ OUI:00CB00* OUI:00CB51* ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS +OUI:00CB7A* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + OUI:00CBB4* ID_OUI_FROM_DATABASE=SHENZHEN ATEKO PHOTOELECTRICITY CO.,LTD @@ -38507,6 +38519,9 @@ OUI:00D318* OUI:00D38D* ID_OUI_FROM_DATABASE=Hotel Technology Next Generation +OUI:00D49E* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:00D632* ID_OUI_FROM_DATABASE=GE Energy @@ -39401,6 +39416,9 @@ OUI:00E421* OUI:00E5E4* ID_OUI_FROM_DATABASE=Sichuan Tianyi Comheart Telecom Co.,LTD +OUI:00E5F1* + ID_OUI_FROM_DATABASE=BUFFALO.INC + OUI:00E666* ID_OUI_FROM_DATABASE=ARIMA Communications Corp. @@ -39728,6 +39746,9 @@ OUI:0425C5* OUI:0425E0* ID_OUI_FROM_DATABASE=Taicang T&W Electronics +OUI:0425F0* + ID_OUI_FROM_DATABASE=Nokia + OUI:042605* ID_OUI_FROM_DATABASE=GFR Gesellschaft für Regelungstechnik und Energieeinsparung mbH @@ -39932,6 +39953,9 @@ OUI:046273* OUI:0462D7* ID_OUI_FROM_DATABASE=ALSTOM HYDRO FRANCE +OUI:0463D0* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:0463E0* ID_OUI_FROM_DATABASE=Nome Oy @@ -39944,6 +39968,9 @@ OUI:046785* OUI:046865* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:04698F* + ID_OUI_FROM_DATABASE=Juniper Networks + OUI:0469F8* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -40061,6 +40088,9 @@ OUI:047AAE* OUI:047BCB* ID_OUI_FROM_DATABASE=Universal Global Scientific Industrial Co., Ltd. +OUI:047C16* + ID_OUI_FROM_DATABASE=Micro-Star INTL CO., LTD. + OUI:047D50* ID_OUI_FROM_DATABASE=Shenzhen Kang Ying Technology Co.Ltd. @@ -40151,6 +40181,9 @@ OUI:0498F3* OUI:0499B9* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:0499BB* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:0499E6* ID_OUI_FROM_DATABASE=Shenzhen Yoostar Technology Co., Ltd @@ -40226,9 +40259,15 @@ OUI:04B466* OUI:04B648* ID_OUI_FROM_DATABASE=ZENNER +OUI:04B6BE* + ID_OUI_FROM_DATABASE=CIG SHANGHAI CO LTD + OUI:04B86A* ID_OUI_FROM_DATABASE=BSkyB Ltd +OUI:04B97D* + ID_OUI_FROM_DATABASE=AiVIS Co., Itd. + OUI:04B9E3* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -40241,12 +40280,18 @@ OUI:04BA36* OUI:04BA8D* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:04BAD6* + ID_OUI_FROM_DATABASE=D-Link Corporation + OUI:04BBF9* ID_OUI_FROM_DATABASE=Pavilion Data Systems Inc OUI:04BC87* ID_OUI_FROM_DATABASE=Shenzhen JustLink Technology Co., LTD +OUI:04BC9F* + ID_OUI_FROM_DATABASE=Calix Inc. + OUI:04BD70* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -40517,6 +40562,9 @@ OUI:04E229* OUI:04E2F8* ID_OUI_FROM_DATABASE=AEP Ticketing solutions srl +OUI:04E31A* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + OUI:04E451* ID_OUI_FROM_DATABASE=Texas Instruments @@ -40547,6 +40595,9 @@ OUI:04E77E* OUI:04E795* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:04E892* + ID_OUI_FROM_DATABASE=SHENNAN CIRCUITS CO.,LTD + OUI:04E9E5* ID_OUI_FROM_DATABASE=PJRC.COM, LLC @@ -41274,7 +41325,7 @@ OUI:0826AE9* ID_OUI_FROM_DATABASE=Annapurna labs OUI:0826AEA* - ID_OUI_FROM_DATABASE=Flextronics International Kft. + ID_OUI_FROM_DATABASE=Flextronics International Kft OUI:0826AEB* ID_OUI_FROM_DATABASE=F-Plus Mobile LLC @@ -41318,6 +41369,9 @@ OUI:082FE9* OUI:08306B* ID_OUI_FROM_DATABASE=Palo Alto Networks +OUI:0830CE* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + OUI:08318B* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -41432,6 +41486,9 @@ OUI:084FA9* OUI:084FF9* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:085104* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:085114* ID_OUI_FROM_DATABASE=QINGDAO TOPSCOMM COMMUNICATION CO., LTD @@ -41891,6 +41948,9 @@ OUI:08E4DF* OUI:08E5DA* ID_OUI_FROM_DATABASE=NANJING FUJITSU COMPUTER PRODUCTS CO.,LTD. +OUI:08E63B* + ID_OUI_FROM_DATABASE=zte corporation + OUI:08E672* ID_OUI_FROM_DATABASE=JEBSEE ELECTRONICS CO.,LTD. @@ -41921,6 +41981,9 @@ OUI:08EB74* OUI:08EBED* ID_OUI_FROM_DATABASE=World Elite Technology Co.,LTD +OUI:08EBF6* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:08ECA9* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -42521,12 +42584,18 @@ OUI:0C771A* OUI:0C7A15* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:0C7BC8* + ID_OUI_FROM_DATABASE=Cisco Meraki + OUI:0C7C28* ID_OUI_FROM_DATABASE=Nokia Solutions and Networks GmbH & Co. KG OUI:0C7D7C* ID_OUI_FROM_DATABASE=Kexiang Information Technology Co, Ltd. +OUI:0C7FB2* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:0C8063* ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. @@ -42575,6 +42644,54 @@ OUI:0C8525* OUI:0C8610* ID_OUI_FROM_DATABASE=Juniper Networks +OUI:0C86290* + ID_OUI_FROM_DATABASE=Shanghai Prophet Electronic Technology Co.,Ltd + +OUI:0C86291* + ID_OUI_FROM_DATABASE=Beijing Qinmu Data Technology Co., Ltd. + +OUI:0C86292* + ID_OUI_FROM_DATABASE=BADA SYSTEM co., Ltd + +OUI:0C86293* + ID_OUI_FROM_DATABASE=Annapurna labs + +OUI:0C86294* + ID_OUI_FROM_DATABASE=Ag Express Electronics + +OUI:0C86295* + ID_OUI_FROM_DATABASE=Shenzhen protostellar technology Co., Ltd + +OUI:0C86296* + ID_OUI_FROM_DATABASE=C&A Marketing, INC. + +OUI:0C86297* + ID_OUI_FROM_DATABASE=HagerEnergy GmbH + +OUI:0C86298* + ID_OUI_FROM_DATABASE=MyGregor Ltd + +OUI:0C86299* + ID_OUI_FROM_DATABASE=HONGKONG SAINT TECH INDUSTRIAL LIMITED + +OUI:0C8629A* + ID_OUI_FROM_DATABASE=Nipron Co.,Ltd + +OUI:0C8629B* + ID_OUI_FROM_DATABASE=Akribis Systems + +OUI:0C8629C* + ID_OUI_FROM_DATABASE=SHENZHEN YINGMU TECHNOLOGY.,LTD + +OUI:0C8629D* + ID_OUI_FROM_DATABASE=BEIJING BEIBIANZHIDA TECHNOLOGY CO.,LTD + +OUI:0C8629E* + ID_OUI_FROM_DATABASE=FX TECHNOLOGY LIMITED + +OUI:0C86C7* + ID_OUI_FROM_DATABASE=Jabil Circuit (Guangzhou) Limited + OUI:0C8910* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -42584,6 +42701,9 @@ OUI:0C8A87* OUI:0C8B7D* ID_OUI_FROM_DATABASE=Vizio, Inc +OUI:0C8B95* + ID_OUI_FROM_DATABASE=Espressif Inc. + OUI:0C8BD3* ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED @@ -42647,6 +42767,9 @@ OUI:0C96CD* OUI:0C96E6* ID_OUI_FROM_DATABASE=Cloud Network Technology (Samoa) Limited +OUI:0C975F* + ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company + OUI:0C9838* ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd @@ -42695,6 +42818,9 @@ OUI:0CAAEE* OUI:0CAC05* ID_OUI_FROM_DATABASE=Unitend Technologies Inc. +OUI:0CAC8A* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + OUI:0CAE7D* ID_OUI_FROM_DATABASE=Texas Instruments @@ -43127,6 +43253,9 @@ OUI:100645* OUI:1006ED* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:10071D* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + OUI:1007230* ID_OUI_FROM_DATABASE=RippleTek Tech Ltd @@ -43274,6 +43403,9 @@ OUI:101F74* OUI:102279* ID_OUI_FROM_DATABASE=ZeroDesktop, Inc. +OUI:102407* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:102779* ID_OUI_FROM_DATABASE=Sadel S.p.A. @@ -43436,6 +43568,9 @@ OUI:104A7D* OUI:104B46* ID_OUI_FROM_DATABASE=Mitsubishi Electric Corporation +OUI:104D15* + ID_OUI_FROM_DATABASE=Viaanix Inc + OUI:104D77* ID_OUI_FROM_DATABASE=Innovative Computer Engineering @@ -43466,6 +43601,12 @@ OUI:10521C* OUI:105403* ID_OUI_FROM_DATABASE=INTARSO GmbH +OUI:1054D20* + ID_OUI_FROM_DATABASE=GIPS Technology Co., Ltd. + +OUI:1054D21* + ID_OUI_FROM_DATABASE=Jiangxi Ofilm&Jvneng IoT Tech Co., Ltd. + OUI:1054D22* ID_OUI_FROM_DATABASE=ComNav Technology Ltd. @@ -43475,6 +43616,12 @@ OUI:1054D23* OUI:1054D24* ID_OUI_FROM_DATABASE=Raylogic Control Systems Private Limited +OUI:1054D25* + ID_OUI_FROM_DATABASE=Sybersense + +OUI:1054D26* + ID_OUI_FROM_DATABASE=Lanao Communication Technology Limited + OUI:1054D27* ID_OUI_FROM_DATABASE=SHENZHEN CARSAFE TECHNOLOGY DEVELOPMENT CO.,LTD @@ -43484,6 +43631,9 @@ OUI:1054D28* OUI:1054D29* ID_OUI_FROM_DATABASE=Bamboo Dynamics Corporation., Ltd. +OUI:1054D2A* + ID_OUI_FROM_DATABASE=Embion B.V. + OUI:1054D2B* ID_OUI_FROM_DATABASE=Shenzhen Dinstech Technology Co.,Ltd. @@ -43493,6 +43643,9 @@ OUI:1054D2C* OUI:1054D2D* ID_OUI_FROM_DATABASE=Sun wealth technology corporation limited +OUI:1054D2E* + ID_OUI_FROM_DATABASE=COSMO AIOT TECHNOLOGY CO LTD + OUI:1055E4* ID_OUI_FROM_DATABASE=Shenzhen Skyworth Digital Technology CO., Ltd @@ -43553,6 +43706,9 @@ OUI:1062E5* OUI:1062EB* ID_OUI_FROM_DATABASE=D-Link International +OUI:10634B* + ID_OUI_FROM_DATABASE=SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + OUI:1063C8* ID_OUI_FROM_DATABASE=Liteon Technology Corporation @@ -43589,6 +43745,9 @@ OUI:1070FD* OUI:107100* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. +OUI:1071B3* + ID_OUI_FROM_DATABASE=Zyxel Communications Corporation + OUI:1071F9* ID_OUI_FROM_DATABASE=Cloud Telecomputers, LLC @@ -43706,6 +43865,9 @@ OUI:1094BB* OUI:10954B* ID_OUI_FROM_DATABASE=Megabyte Ltd. +OUI:10961A* + ID_OUI_FROM_DATABASE=CHIPSEA TECHNOLOGIES (SHENZHEN) CORP. + OUI:109693* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. @@ -43757,6 +43919,9 @@ OUI:10A4DA* OUI:10A51D* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:10A562* + ID_OUI_FROM_DATABASE=Iton Technology Corp. + OUI:10A5D0* ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. @@ -43784,6 +43949,9 @@ OUI:10B1DF* OUI:10B1F8* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:10B232* + ID_OUI_FROM_DATABASE=Qingdao Intelligent&Precise Electronics Co.,Ltd. + OUI:10B26B* ID_OUI_FROM_DATABASE=base Co.,Ltd. @@ -43940,6 +44108,9 @@ OUI:10D7B0* OUI:10DA43* ID_OUI_FROM_DATABASE=NETGEAR +OUI:10DA49* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:10DC4A* ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD @@ -44003,6 +44174,9 @@ OUI:10DF8B* OUI:10DFFC* ID_OUI_FROM_DATABASE=Siemens AG +OUI:10E177* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:10E2D5* ID_OUI_FROM_DATABASE=Qi Hardware Inc. @@ -44051,6 +44225,9 @@ OUI:10EED9* OUI:10F005* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:10F068* + ID_OUI_FROM_DATABASE=Ruckus Wireless + OUI:10F163* ID_OUI_FROM_DATABASE=TNK CO.,LTD @@ -44069,6 +44246,9 @@ OUI:10F49A* OUI:10F605* ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd. +OUI:10F60A* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:10F681* ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. @@ -44165,6 +44345,9 @@ OUI:141114* OUI:14115D* ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd +OUI:14130B* + ID_OUI_FROM_DATABASE=Garmin International + OUI:141330* ID_OUI_FROM_DATABASE=Anakreon UK LLP @@ -44318,6 +44501,9 @@ OUI:142C78* OUI:142D27* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. +OUI:142D4D* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:142D8B* ID_OUI_FROM_DATABASE=Incipio Technologies, Inc @@ -44405,6 +44591,9 @@ OUI:144319* OUI:14444A* ID_OUI_FROM_DATABASE=Apollo Seiko Ltd. +OUI:14448F* + ID_OUI_FROM_DATABASE=Edgecore Networks Corporation + OUI:144658* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -44546,6 +44735,9 @@ OUI:145A83* OUI:145AFC* ID_OUI_FROM_DATABASE=Liteon Technology Corporation +OUI:145BB9* + ID_OUI_FROM_DATABASE=ConMet + OUI:145BD1* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. @@ -44603,6 +44795,9 @@ OUI:147373* OUI:147411* ID_OUI_FROM_DATABASE=RIM +OUI:14755B* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:147590* ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. @@ -44693,6 +44888,9 @@ OUI:14942F* OUI:149448* ID_OUI_FROM_DATABASE=BLU CASTLE S.A. +OUI:14946C* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:1495CE* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -44717,6 +44915,9 @@ OUI:149B2F* OUI:149BD7* ID_OUI_FROM_DATABASE=MULI MUWAI FURNITURE QIDONG CO., LTD +OUI:149BF3* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + OUI:149D09* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -45023,12 +45224,18 @@ OUI:14EFCF* OUI:14F0C5* ID_OUI_FROM_DATABASE=Xtremio Ltd. +OUI:14F287* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:14F28E* ID_OUI_FROM_DATABASE=ShenYang ZhongKe-Allwin Technology Co.LTD OUI:14F42A* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:14F592* + ID_OUI_FROM_DATABASE=Shenzhen SDG DONZHI Technology Co., Ltd + OUI:14F65A* ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd @@ -45242,6 +45449,9 @@ OUI:18339D* OUI:183451* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:1834AF* + ID_OUI_FROM_DATABASE=Kaonmedia CO., LTD. + OUI:1835D1* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. @@ -45278,6 +45488,9 @@ OUI:183A48* OUI:183BD2* ID_OUI_FROM_DATABASE=BYD Precision Manufacture Company Ltd. +OUI:183C98* + ID_OUI_FROM_DATABASE=Shenzhen Hengyi Technology Co., LTD + OUI:183CB7* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. @@ -45518,6 +45731,9 @@ OUI:1866DA* OUI:1866E3* ID_OUI_FROM_DATABASE=Veros Systems, Inc. +OUI:1866F0* + ID_OUI_FROM_DATABASE=Jupiter Systems + OUI:18673F* ID_OUI_FROM_DATABASE=Hanover Displays Limited @@ -45536,6 +45752,9 @@ OUI:186882* OUI:1868CB* ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. +OUI:1869D4* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:1869D8* ID_OUI_FROM_DATABASE=Tuya Smart Inc. @@ -45806,6 +46025,51 @@ OUI:18A3E8* OUI:18A4A9* ID_OUI_FROM_DATABASE=Vanu Inc. +OUI:18A59C0* + ID_OUI_FROM_DATABASE=Omwave + +OUI:18A59C1* + ID_OUI_FROM_DATABASE=Cuman + +OUI:18A59C2* + ID_OUI_FROM_DATABASE=Actiontec Electronics Inc. + +OUI:18A59C3* + ID_OUI_FROM_DATABASE=Beijing QS Medical Technology Co., Ltd. + +OUI:18A59C4* + ID_OUI_FROM_DATABASE=IT-1 + +OUI:18A59C5* + ID_OUI_FROM_DATABASE=Thermia AB + +OUI:18A59C6* + ID_OUI_FROM_DATABASE=INTEGRAL PLUS + +OUI:18A59C7* + ID_OUI_FROM_DATABASE=ePower Network Solution Co., Ltd. + +OUI:18A59C8* + ID_OUI_FROM_DATABASE=Residence Control Ltd + +OUI:18A59C9* + ID_OUI_FROM_DATABASE=estun automation co.,ltd + +OUI:18A59CA* + ID_OUI_FROM_DATABASE=Erba Lachema s.r.o. + +OUI:18A59CB* + ID_OUI_FROM_DATABASE=CAL-COMP INDUSTRIA E COMERCIO DE ELETRONICOS E INFORMATICA LTDA + +OUI:18A59CC* + ID_OUI_FROM_DATABASE=BlueEyes Technology + +OUI:18A59CD* + ID_OUI_FROM_DATABASE=Annapurna labs + +OUI:18A59CE* + ID_OUI_FROM_DATABASE=BMC Messsysteme GmbH + OUI:18A6F7* ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. @@ -45860,6 +46124,9 @@ OUI:18AF9F* OUI:18B169* ID_OUI_FROM_DATABASE=Sonicwall +OUI:18B185* + ID_OUI_FROM_DATABASE=Qiao Information Technology (Zhengzhou) Co., Ltd. + OUI:18B209* ID_OUI_FROM_DATABASE=Torrey Pines Logic, Inc @@ -45890,12 +46157,18 @@ OUI:18B905* OUI:18B96E* ID_OUI_FROM_DATABASE=Dongguan Liesheng Electronic Co., Ltd. +OUI:18BB1C* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:18BB26* ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED OUI:18BB41* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. +OUI:18BC57* + ID_OUI_FROM_DATABASE=ADVA Optical Networking Ltd. + OUI:18BC5A* ID_OUI_FROM_DATABASE=Zhejiang Tmall Technology Co., Ltd. @@ -45911,6 +46184,9 @@ OUI:18BF1C* OUI:18BFB3* ID_OUI_FROM_DATABASE=Samsung Electronics Co., Ltd., Memory Division +OUI:18C007* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:18C04D* ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. @@ -46169,6 +46445,9 @@ OUI:18FC26* OUI:18FC9F* ID_OUI_FROM_DATABASE=Changhe Electronics Co., Ltd. +OUI:18FD74* + ID_OUI_FROM_DATABASE=Routerboard.com + OUI:18FDCB0* ID_OUI_FROM_DATABASE=Shenzhen Rui jiali Electronic Technology Co. Ltd. @@ -46244,6 +46523,9 @@ OUI:1C08C1* OUI:1C0B52* ID_OUI_FROM_DATABASE=EPICOM S.A +OUI:1C0D7D* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:1C0FAF* ID_OUI_FROM_DATABASE=Lucid Vision Labs @@ -46514,6 +46796,9 @@ OUI:1C45C2* OUI:1C472F* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. +OUI:1C47F6* + ID_OUI_FROM_DATABASE=Zhidao Network Technology(Shenzhen) Co.,Ltd + OUI:1C4840* ID_OUI_FROM_DATABASE=IMS Messsysteme GmbH @@ -46580,6 +46865,51 @@ OUI:1C57D8* OUI:1C57DC* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:1C59740* + ID_OUI_FROM_DATABASE=Shenzhen Hanshine Technology Co.Ltd. + +OUI:1C59741* + ID_OUI_FROM_DATABASE=Logical Infrastructure PTY LTD + +OUI:1C59742* + ID_OUI_FROM_DATABASE=Chongqing Taishan Cable Co., Ltd + +OUI:1C59743* + ID_OUI_FROM_DATABASE=Jiangsu Welm Technology Co.,Ltd + +OUI:1C59744* + ID_OUI_FROM_DATABASE=Syntax technology(tianjin)Co.,LTD + +OUI:1C59745* + ID_OUI_FROM_DATABASE=Shenzhen Shi Fang Communication Technology Co., Ltd + +OUI:1C59746* + ID_OUI_FROM_DATABASE=Square Inc. + +OUI:1C59747* + ID_OUI_FROM_DATABASE=Lynxi Technologies Co.,Ltd. + +OUI:1C59748* + ID_OUI_FROM_DATABASE=Topway Global Technology Limited + +OUI:1C59749* + ID_OUI_FROM_DATABASE=Shanghai Laisi Information Technology Co.,Ltd + +OUI:1C5974A* + ID_OUI_FROM_DATABASE=Council Rock + +OUI:1C5974B* + ID_OUI_FROM_DATABASE=Beijing Flintec Electronic Technology Co.,Ltd. + +OUI:1C5974C* + ID_OUI_FROM_DATABASE=King-On Technology Ltd. + +OUI:1C5974D* + ID_OUI_FROM_DATABASE=Shenzhen Geshem Technology Co Ltd + +OUI:1C5974E* + ID_OUI_FROM_DATABASE=Globe Tracker ApS + OUI:1C599B* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -46619,6 +46949,9 @@ OUI:1C60D2* OUI:1C60DE* ID_OUI_FROM_DATABASE=MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. +OUI:1C61B4* + ID_OUI_FROM_DATABASE=TP-Link Corporation Limited + OUI:1C62B8* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -46700,6 +47033,9 @@ OUI:1C7508* OUI:1C76CA* ID_OUI_FROM_DATABASE=Terasic Technologies Inc. +OUI:1C76F2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:1C77F6* ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD @@ -47168,6 +47504,9 @@ OUI:1CA0EFE* OUI:1CA2B1* ID_OUI_FROM_DATABASE=ruwido austria gmbh +OUI:1CA410* + ID_OUI_FROM_DATABASE=Amlogic, Inc. + OUI:1CA532* ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT @@ -47249,6 +47588,9 @@ OUI:1CAECB* OUI:1CAF05* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:1CAF4A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:1CAFF7* ID_OUI_FROM_DATABASE=D-Link International @@ -47288,6 +47630,9 @@ OUI:1CBA8C* OUI:1CBBA8* ID_OUI_FROM_DATABASE=OJSC Ufimskiy Zavod Promsvyaz +OUI:1CBCEC* + ID_OUI_FROM_DATABASE=silex technology, Inc. + OUI:1CBD0E* ID_OUI_FROM_DATABASE=Amplified Engineering Pty Ltd @@ -47618,6 +47963,9 @@ OUI:200505* OUI:2005E8* ID_OUI_FROM_DATABASE=OOO InProMedia +OUI:200889* + ID_OUI_FROM_DATABASE=zte corporation + OUI:2008ED* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -47669,6 +48017,9 @@ OUI:200A0DE* OUI:200A5E* ID_OUI_FROM_DATABASE=Xiangshan Giant Eagle Technology Developing Co., Ltd. +OUI:200B16* + ID_OUI_FROM_DATABASE=Texas Instruments + OUI:200BC7* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -47978,6 +48329,9 @@ OUI:206432* OUI:2064CB* ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +OUI:2064DE* + ID_OUI_FROM_DATABASE=Sunitec Enterprise Co.,Ltd + OUI:20658E* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -48158,9 +48512,15 @@ OUI:208984* OUI:208986* ID_OUI_FROM_DATABASE=zte corporation +OUI:20898A* + ID_OUI_FROM_DATABASE=Shenzhen Skyworth Digital Technology CO., Ltd + OUI:208B37* ID_OUI_FROM_DATABASE=Skyworth Digital Technology(Shenzhen) Co.,Ltd +OUI:208BD1* + ID_OUI_FROM_DATABASE=NXP Semiconductor (Tianjin) LTD. + OUI:208C47* ID_OUI_FROM_DATABASE=Tenstorrent Inc @@ -48545,6 +48905,9 @@ OUI:20F85E* OUI:20FABB* ID_OUI_FROM_DATABASE=Cambridge Executive Limited +OUI:20FADB* + ID_OUI_FROM_DATABASE=Huahao Kunpeng Technology (chengDu) Co.,Ltd. + OUI:20FDF1* ID_OUI_FROM_DATABASE=3COM EUROPE LTD @@ -48584,6 +48947,9 @@ OUI:2405F5* OUI:2406AA* ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +OUI:2406F2* + ID_OUI_FROM_DATABASE=Sichuan Tianyi Comheart Telecom Co.,LTD + OUI:24085D* ID_OUI_FROM_DATABASE=Continental Aftermarket & Services GmbH @@ -48626,6 +48992,9 @@ OUI:240D6C* OUI:240DC2* ID_OUI_FROM_DATABASE=TCT mobile ltd +OUI:240F5E* + ID_OUI_FROM_DATABASE=Shenzhen z-router Technology Co., Ltd + OUI:240F9B* ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. @@ -48704,6 +49073,9 @@ OUI:24169D* OUI:24181D* ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) +OUI:2418C0* + ID_OUI_FROM_DATABASE=E. Wehrle GmbH + OUI:2418C6* ID_OUI_FROM_DATABASE=HUNAN FN-LINK TECHNOLOGY LIMITED @@ -48737,6 +49109,9 @@ OUI:241F2C* OUI:241FA0* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:241FBD* + ID_OUI_FROM_DATABASE=Extreme Networks, Inc. + OUI:2420C7* ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS @@ -48755,12 +49130,18 @@ OUI:242642* OUI:2426BA* ID_OUI_FROM_DATABASE=Shenzhen Toptel Technology Co., Ltd. +OUI:2426D6* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:2428FD* ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. OUI:2429FE* ID_OUI_FROM_DATABASE=KYOCERA Corporation +OUI:242CFE* + ID_OUI_FROM_DATABASE=Zhejiang Tmall Technology Co., Ltd. + OUI:242E02* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -49046,6 +49427,9 @@ OUI:2469A5* OUI:246AAB* ID_OUI_FROM_DATABASE=IT-IS International +OUI:246C60* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:246C8A* ID_OUI_FROM_DATABASE=YUKAI Engineering @@ -49070,6 +49454,9 @@ OUI:247260* OUI:2474F7* ID_OUI_FROM_DATABASE=GoPro +OUI:24753A* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + OUI:247625* ID_OUI_FROM_DATABASE=Texas Instruments @@ -49346,6 +49733,9 @@ OUI:24CE33* OUI:24CF21* ID_OUI_FROM_DATABASE=Shenzhen State Micro Technology Co., Ltd +OUI:24CF24* + ID_OUI_FROM_DATABASE=Beijing Xiaomi Mobile Software Co., Ltd + OUI:24D0DF* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -49445,6 +49835,9 @@ OUI:24EA40* OUI:24EB65* ID_OUI_FROM_DATABASE=SAET I.S. S.r.l. +OUI:24EBED* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:24EC51* ID_OUI_FROM_DATABASE=ADF Technologies Sdn Bhd @@ -49580,6 +49973,9 @@ OUI:2811A8* OUI:2811EC* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:281293* + ID_OUI_FROM_DATABASE=Honor Device Co., Ltd. + OUI:281471* ID_OUI_FROM_DATABASE=Lantis co., LTD. @@ -49856,6 +50252,12 @@ OUI:283B96* OUI:283CE4* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:283DC2* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:283E0C* + ID_OUI_FROM_DATABASE=Preferred Robotics, Inc. + OUI:283E76* ID_OUI_FROM_DATABASE=Common Networks @@ -49976,6 +50378,9 @@ OUI:286AB8* OUI:286ABA* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:286B35* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:286C07* ID_OUI_FROM_DATABASE=XIAOMI Electronics,CO.,LTD @@ -49988,6 +50393,9 @@ OUI:286DCD* OUI:286ED4* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:286F40* + ID_OUI_FROM_DATABASE=Tonly Technology Co. Ltd + OUI:286F7F* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -50000,6 +50408,9 @@ OUI:2872C5* OUI:2872F0* ID_OUI_FROM_DATABASE=ATHENA +OUI:2874F5* + ID_OUI_FROM_DATABASE=Nokia Solutions and Networks GmbH & Co. KG + OUI:2875D8* ID_OUI_FROM_DATABASE=FUJIAN STAR-NET COMMUNICATION CO.,LTD @@ -50039,6 +50450,9 @@ OUI:288088* OUI:2880A2* ID_OUI_FROM_DATABASE=Novatel Wireless Solutions, Inc. +OUI:28827C* + ID_OUI_FROM_DATABASE=Bosch Automative products(Suzhou)Co.,Ltd Changzhou Branch + OUI:288335* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -50135,6 +50549,12 @@ OUI:28A241* OUI:28A24B* ID_OUI_FROM_DATABASE=Juniper Networks +OUI:28A331* + ID_OUI_FROM_DATABASE=Sierra Wireless + +OUI:28A53F* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + OUI:28A574* ID_OUI_FROM_DATABASE=Miller Electric Mfg. Co. @@ -50255,6 +50675,9 @@ OUI:28BD89* OUI:28BE03* ID_OUI_FROM_DATABASE=TCT mobile ltd +OUI:28BE43* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + OUI:28BE9B* ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. @@ -50330,6 +50753,9 @@ OUI:28CD4C* OUI:28CD9C* ID_OUI_FROM_DATABASE=Shenzhen Dynamax Software Development Co.,Ltd. +OUI:28CDC1* + ID_OUI_FROM_DATABASE=Raspberry Pi Trading Ltd + OUI:28CDC4* ID_OUI_FROM_DATABASE=CHONGQING FUGUI ELECTRONICS CO.,LTD. @@ -50537,9 +50963,15 @@ OUI:28F537D* OUI:28F537E* ID_OUI_FROM_DATABASE=Performance Motion Devices +OUI:28F5D1* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:28F606* ID_OUI_FROM_DATABASE=Syes srl +OUI:28F7D6* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + OUI:28FA19* ID_OUI_FROM_DATABASE=Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd @@ -50660,6 +51092,9 @@ OUI:2C073C* OUI:2C0786* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. +OUI:2C07F6* + ID_OUI_FROM_DATABASE=SKG Health Technologies Co., Ltd. + OUI:2C081C* ID_OUI_FROM_DATABASE=OVH @@ -50990,6 +51425,9 @@ OUI:2C3AE8* OUI:2C3AFD* ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH +OUI:2C3B70* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + OUI:2C3BFD* ID_OUI_FROM_DATABASE=Netstor Technology Co., Ltd. @@ -51129,7 +51567,7 @@ OUI:2C54CF* ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) OUI:2C553C* - ID_OUI_FROM_DATABASE=Gainspeed, Inc. + ID_OUI_FROM_DATABASE=Vecima Networks Inc. OUI:2C557C* ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd @@ -51188,6 +51626,9 @@ OUI:2C5FF3* OUI:2C600C* ID_OUI_FROM_DATABASE=Quanta Computer Inc. +OUI:2C60CD* + ID_OUI_FROM_DATABASE=NR ELECTRIC CO., LTD + OUI:2C6104* ID_OUI_FROM_DATABASE=SHENZHEN FENGLIAN TECHNOLOGY CO., LTD. @@ -51335,6 +51776,9 @@ OUI:2C8065* OUI:2C8158* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. +OUI:2C8217* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:2C86D2* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -51386,6 +51830,9 @@ OUI:2C9AA4* OUI:2C9D1E* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:2C9D65* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + OUI:2C9E5F* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. @@ -51428,6 +51875,9 @@ OUI:2CA59C* OUI:2CA780* ID_OUI_FROM_DATABASE=True Technologies Inc. +OUI:2CA79E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:2CA835* ID_OUI_FROM_DATABASE=RIM @@ -51629,6 +52079,9 @@ OUI:2CD974* OUI:2CDB07* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:2CDC78* + ID_OUI_FROM_DATABASE=Descartes Systems (USA) LLC + OUI:2CDCAD* ID_OUI_FROM_DATABASE=Wistron Neweb Corporation @@ -51713,6 +52166,9 @@ OUI:2CF89B* OUI:2CFAA2* ID_OUI_FROM_DATABASE=Alcatel-Lucent Enterprise +OUI:2CFC8B* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + OUI:2CFCE4* ID_OUI_FROM_DATABASE=CTEK Sweden AB @@ -51740,6 +52196,9 @@ OUI:2CFFEE* OUI:3003C8* ID_OUI_FROM_DATABASE=CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. +OUI:30045C* + ID_OUI_FROM_DATABASE=Shenzhen SuperElectron Technology Co.,Ltd. + OUI:30053F* ID_OUI_FROM_DATABASE=JTI Co.,Ltd. @@ -51980,6 +52439,9 @@ OUI:302952* OUI:3029BE* ID_OUI_FROM_DATABASE=Shanghai MRDcom Co.,Ltd +OUI:302BDC* + ID_OUI_FROM_DATABASE=Top-Unum Electronics Co., LTD + OUI:302DE8* ID_OUI_FROM_DATABASE=JDA, LLC (JDA Systems) @@ -52040,9 +52502,15 @@ OUI:303ABA* OUI:303D08* ID_OUI_FROM_DATABASE=GLINTT TES S.A. +OUI:303EA7* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:303EAD* ID_OUI_FROM_DATABASE=Sonavox Canada Inc +OUI:303F5D* + ID_OUI_FROM_DATABASE=PT HAN SUNG ELECTORONICS INDONESIA + OUI:303F7B* ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd @@ -52061,6 +52529,51 @@ OUI:304240* OUI:3042A1* ID_OUI_FROM_DATABASE=ilumisys Inc. DBA Toggled +OUI:3043D70* + ID_OUI_FROM_DATABASE=SYMES SA + +OUI:3043D71* + ID_OUI_FROM_DATABASE=Shenzhen juduoping Technology Co.,Ltd + +OUI:3043D72* + ID_OUI_FROM_DATABASE=Apollo Infoways Private Limited + +OUI:3043D73* + ID_OUI_FROM_DATABASE=Luxshare Electronic Technology (Kunshan) LTD + +OUI:3043D74* + ID_OUI_FROM_DATABASE=FIBERME COMMUNICATIONS LLC + +OUI:3043D75* + ID_OUI_FROM_DATABASE=Shenzhen Mees Hi-Tech Co., Ltd + +OUI:3043D76* + ID_OUI_FROM_DATABASE=Sprocomm Technologies Co., Ltd.Guangming Branch + +OUI:3043D77* + ID_OUI_FROM_DATABASE=DIGICITI Technology Co.,Ltd + +OUI:3043D78* + ID_OUI_FROM_DATABASE=Kesu (Shanghai) Electronic Technology Co., Ltd + +OUI:3043D79* + ID_OUI_FROM_DATABASE=PK Solutions LLC + +OUI:3043D7A* + ID_OUI_FROM_DATABASE=Bodhi + +OUI:3043D7B* + ID_OUI_FROM_DATABASE=Motec GmbH + +OUI:3043D7C* + ID_OUI_FROM_DATABASE=Xiaoniu network technology (Shanghai) Co., Ltd. + +OUI:3043D7D* + ID_OUI_FROM_DATABASE=Annapurna labs + +OUI:3043D7E* + ID_OUI_FROM_DATABASE=Guangdong Hongqin Telecom Technology Co. Ltd. + OUI:304449* ID_OUI_FROM_DATABASE=PLATH GmbH @@ -52301,6 +52814,9 @@ OUI:307CB2* OUI:307ECB* ID_OUI_FROM_DATABASE=SFR +OUI:307F10* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + OUI:30809B* ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd @@ -52334,6 +52850,9 @@ OUI:308841* OUI:308944* ID_OUI_FROM_DATABASE=DEVA Broadcast Ltd. +OUI:30894A* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:308976* ID_OUI_FROM_DATABASE=DALIAN LAMBA TECHNOLOGY CO.,LTD @@ -52388,6 +52907,9 @@ OUI:3095E3* OUI:309610* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. +OUI:30963B* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:3096FB* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -52511,6 +53033,9 @@ OUI:30B930* OUI:30B9B0* ID_OUI_FROM_DATABASE=Intracom Asia Co., Ltd +OUI:30BB7D* + ID_OUI_FROM_DATABASE=OnePlus Technology (Shenzhen) Co., Ltd + OUI:30BE3B* ID_OUI_FROM_DATABASE=Mitsubishi Electric Corporation @@ -52541,6 +53066,9 @@ OUI:30C82A* OUI:30C9AB* ID_OUI_FROM_DATABASE=CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. +OUI:30CB36* + ID_OUI_FROM_DATABASE=Belden Singapore Pte. Ltd. + OUI:30CBC7* ID_OUI_FROM_DATABASE=Cambium Networks Limited @@ -52599,7 +53127,7 @@ OUI:30DF8D* ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT OUI:30E090* - ID_OUI_FROM_DATABASE=Linctronix Ltd, + ID_OUI_FROM_DATABASE=Genevisio Ltd. OUI:30E171* ID_OUI_FROM_DATABASE=Hewlett Packard @@ -52625,6 +53153,9 @@ OUI:30E4DB* OUI:30E7BC* ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +OUI:30E8E4* + ID_OUI_FROM_DATABASE=Qorvo International Pte. Ltd. + OUI:30E98E* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -52919,6 +53450,9 @@ OUI:34243E* OUI:34255D* ID_OUI_FROM_DATABASE=Shenzhen Loadcom Technology Co.,Ltd +OUI:3425BE* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + OUI:342606* ID_OUI_FROM_DATABASE=CarePredict, Inc. @@ -53162,6 +53696,9 @@ OUI:345D10* OUI:345D9E* ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS +OUI:345DA8* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:3460F9* ID_OUI_FROM_DATABASE=TP-Link Corporation Limited @@ -53351,6 +53888,9 @@ OUI:348F27* OUI:34916F* ID_OUI_FROM_DATABASE=UserGate Ltd. +OUI:3492C2* + ID_OUI_FROM_DATABASE=Square Route Co., Ltd. + OUI:349342* ID_OUI_FROM_DATABASE=TTE Corporation @@ -53456,6 +53996,12 @@ OUI:34AB37* OUI:34AB95* ID_OUI_FROM_DATABASE=Espressif Inc. +OUI:34AC11* + ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd. + +OUI:34AD61* + ID_OUI_FROM_DATABASE=CELESTICA INC. + OUI:34ADE4* ID_OUI_FROM_DATABASE=Shanghai Chint Power Systems Co., Ltd. @@ -53513,6 +54059,9 @@ OUI:34BB26* OUI:34BCA6* ID_OUI_FROM_DATABASE=Beijing Ding Qing Technology, Ltd. +OUI:34BD20* + ID_OUI_FROM_DATABASE=Hangzhou Hikrobot Technology Co., Ltd. + OUI:34BDC8* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -53585,6 +54134,9 @@ OUI:34CE69* OUI:34CE94* ID_OUI_FROM_DATABASE=Parsec (Pty) Ltd +OUI:34CF6C* + ID_OUI_FROM_DATABASE=Hangzhou Taili wireless communication equipment Co.,Ltd + OUI:34CFF6* ID_OUI_FROM_DATABASE=Intel Corporate @@ -53675,6 +54227,9 @@ OUI:34DB9C* OUI:34DBFD* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:34DD04* + ID_OUI_FROM_DATABASE=Minut AB + OUI:34DD7E* ID_OUI_FROM_DATABASE=Umeox Innovations Co.,Ltd @@ -53789,6 +54344,9 @@ OUI:34ED0B* OUI:34ED1B* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:34EE2A* + ID_OUI_FROM_DATABASE=ConMet + OUI:34EF44* ID_OUI_FROM_DATABASE=2Wire Inc @@ -53936,6 +54494,9 @@ OUI:3810D5* OUI:3810F0* ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company +OUI:38127B* + ID_OUI_FROM_DATABASE=Crenet Labs Co., Ltd. + OUI:381428* ID_OUI_FROM_DATABASE=Dell Inc. @@ -53984,6 +54545,51 @@ OUI:381DD9* OUI:381EC7* ID_OUI_FROM_DATABASE=Chipsea Technologies(Shenzhen) Corp. +OUI:381F260* + ID_OUI_FROM_DATABASE=JAESUNG INFORMATION & COMMUNICATION CO.LTD + +OUI:381F261* + ID_OUI_FROM_DATABASE=SERNET (SUZHOU) TECHNOLOGIES CORPORATION + +OUI:381F262* + ID_OUI_FROM_DATABASE=Synamedia + +OUI:381F263* + ID_OUI_FROM_DATABASE=Bosch Automotive Electronics India Pvt. Ltd. + +OUI:381F264* + ID_OUI_FROM_DATABASE=Airmaster A/S + +OUI:381F265* + ID_OUI_FROM_DATABASE=Zhejiang Huazhou Intelligent Equipment Co,. Ltd + +OUI:381F266* + ID_OUI_FROM_DATABASE=NOITAC sp. z o.o. sp.k. + +OUI:381F267* + ID_OUI_FROM_DATABASE=RCE systems s.r.o. + +OUI:381F268* + ID_OUI_FROM_DATABASE=Avon Protection + +OUI:381F269* + ID_OUI_FROM_DATABASE=SMS Evoko Group AB + +OUI:381F26A* + ID_OUI_FROM_DATABASE=Sercomm Corporation. + +OUI:381F26B* + ID_OUI_FROM_DATABASE=Deutronic Elektronik GmbH + +OUI:381F26C* + ID_OUI_FROM_DATABASE=Jade Bird Fire Co., Ltd. + +OUI:381F26D* + ID_OUI_FROM_DATABASE=HWACHANG CORPORATION + +OUI:381F26E* + ID_OUI_FROM_DATABASE=Annapurna labs + OUI:381F8D* ID_OUI_FROM_DATABASE=Tuya Smart Inc. @@ -54257,6 +54863,9 @@ OUI:386893* OUI:3868A4* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,LTD +OUI:3868BE* + ID_OUI_FROM_DATABASE=Sichuan Tianyi Comheart Telecom Co.,LTD + OUI:3868DD* ID_OUI_FROM_DATABASE=INVENTEC CORPORATION @@ -54407,12 +55016,18 @@ OUI:388E7A* OUI:388EE7* ID_OUI_FROM_DATABASE=Fanhattan LLC +OUI:388F30* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:389052* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD OUI:3890A5* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:3891B7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:3891D5* ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited @@ -54542,6 +55157,9 @@ OUI:38A9EA* OUI:38AA3C* ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. +OUI:38AB41* + ID_OUI_FROM_DATABASE=Texas Instruments + OUI:38AC3D* ID_OUI_FROM_DATABASE=Nephos Inc @@ -54962,6 +55580,9 @@ OUI:38FB14* OUI:38FC98* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:38FDF5* + ID_OUI_FROM_DATABASE=Renesas Electronics (Penang) Sdn. Bhd. + OUI:38FDF8* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -55202,6 +55823,9 @@ OUI:3C25D7* OUI:3C26D5* ID_OUI_FROM_DATABASE=Sotera Wireless +OUI:3C26E4* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:3C2763* ID_OUI_FROM_DATABASE=SLE quality engineering GmbH & Co. KG @@ -55379,6 +56003,9 @@ OUI:3C438E* OUI:3C457A* ID_OUI_FROM_DATABASE=BSkyB Ltd +OUI:3C4645* + ID_OUI_FROM_DATABASE=Shanghai Infinity Wireless Technologies Co.,Ltd. + OUI:3C46D8* ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. @@ -55493,6 +56120,9 @@ OUI:3C678C* OUI:3C6816* ID_OUI_FROM_DATABASE=VXi Corporation +OUI:3C69D1* + ID_OUI_FROM_DATABASE=ADC Automotive Distance Control System GmbH + OUI:3C6A2C0* ID_OUI_FROM_DATABASE=Rio Lago Technologies LLC @@ -55613,6 +56243,9 @@ OUI:3C80AA* OUI:3C81D8* ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS +OUI:3C82C0* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + OUI:3C831E* ID_OUI_FROM_DATABASE=CKD Corporation @@ -55898,6 +56531,9 @@ OUI:3CCD5D* OUI:3CCD93* ID_OUI_FROM_DATABASE=LG ELECTRONICS INC +OUI:3CCE0D* + ID_OUI_FROM_DATABASE=Shenzhen juduoping Technology Co.,Ltd + OUI:3CCE15* ID_OUI_FROM_DATABASE=Mercedes-Benz USA, LLC @@ -56237,6 +56873,9 @@ OUI:401C83* OUI:401D59* ID_OUI_FROM_DATABASE=Biometric Associates, LP +OUI:402230* + ID_OUI_FROM_DATABASE=Shenzhen SuperElectron Technology Co.,Ltd. + OUI:4022ED* ID_OUI_FROM_DATABASE=Digital Projection Ltd @@ -56339,9 +56978,15 @@ OUI:40331A* OUI:40336C* ID_OUI_FROM_DATABASE=Godrej & Boyce Mfg. co. ltd +OUI:4035E6* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:4037AD* ID_OUI_FROM_DATABASE=Macro Image Technology, Inc. +OUI:403B7B* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:403CFC* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -56372,6 +57017,9 @@ OUI:404101* OUI:404229* ID_OUI_FROM_DATABASE=Layer3TV, Inc +OUI:404244* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:4044FD* ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd. @@ -56654,6 +57302,9 @@ OUI:408C4C* OUI:408D5C* ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. +OUI:408EDF* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:408F9D* ID_OUI_FROM_DATABASE=Juniper Networks @@ -56798,6 +57449,9 @@ OUI:40AC8D* OUI:40ACBF* ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. +OUI:40B02F* + ID_OUI_FROM_DATABASE=Miele & Cie. KG + OUI:40B034* ID_OUI_FROM_DATABASE=Hewlett Packard @@ -56963,6 +57617,9 @@ OUI:40D63C* OUI:40D855* ID_OUI_FROM_DATABASE=IEEE Registration Authority +OUI:40D95A* + ID_OUI_FROM_DATABASE=AMPAK Technology,Inc. + OUI:40DC9D* ID_OUI_FROM_DATABASE=HAJEN @@ -57341,6 +57998,9 @@ OUI:4427F3* OUI:4428A3* ID_OUI_FROM_DATABASE=Jiangsu fulian Communication Technology Co., Ltd. +OUI:44291E* + ID_OUI_FROM_DATABASE=AltoBeam (China) Inc. + OUI:442938* ID_OUI_FROM_DATABASE=NietZsche enterprise Co.Ltd. @@ -57362,6 +58022,9 @@ OUI:443192* OUI:44322A* ID_OUI_FROM_DATABASE=Avaya Inc +OUI:4432C2* + ID_OUI_FROM_DATABASE=GOAL Co., Ltd. + OUI:4432C8* ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. @@ -57405,7 +58068,7 @@ OUI:443C88* ID_OUI_FROM_DATABASE=FICOSA MAROC INTERNATIONAL OUI:443C9C* - ID_OUI_FROM_DATABASE=Pintsch Tiefenbach GmbH + ID_OUI_FROM_DATABASE=Pintsch GmbH OUI:443D21* ID_OUI_FROM_DATABASE=Nuvolt @@ -57584,6 +58247,9 @@ OUI:446D57* OUI:446D6C* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:446D7F* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + OUI:446EE5* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -57701,6 +58367,9 @@ OUI:4487DB* OUI:4487FC* ID_OUI_FROM_DATABASE=Elitegroup Computer Systems Co.,Ltd. +OUI:448816* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:4488CB* ID_OUI_FROM_DATABASE=Camco Technologies NV @@ -57905,6 +58574,9 @@ OUI:44B433* OUI:44B462* ID_OUI_FROM_DATABASE=Flextronics Tech.(Ind) Pvt Ltd +OUI:44B4B2* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + OUI:44B6BE* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -58349,6 +59021,9 @@ OUI:4826E8* OUI:482759* ID_OUI_FROM_DATABASE=Levven Electronics Ltd. +OUI:4827C5* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:4827EA* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -58439,6 +59114,9 @@ OUI:4844F7* OUI:484520* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:48468D* + ID_OUI_FROM_DATABASE=Zepcam B.V. + OUI:4846C1* ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED @@ -58514,6 +59192,9 @@ OUI:48555F* OUI:485702* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:4857D2* + ID_OUI_FROM_DATABASE=Broadcom Limited + OUI:4857DD* ID_OUI_FROM_DATABASE=Facebook Inc @@ -58778,6 +59459,9 @@ OUI:489A42* OUI:489BD5* ID_OUI_FROM_DATABASE=Extreme Networks, Inc. +OUI:489BE0* + ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd. + OUI:489BE2* ID_OUI_FROM_DATABASE=SCI Innovations Ltd @@ -58862,6 +59546,9 @@ OUI:48B25D* OUI:48B423* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. +OUI:48B4C3* + ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company + OUI:48B5A7* ID_OUI_FROM_DATABASE=Glory Horse Industries Ltd. @@ -58937,6 +59624,9 @@ OUI:48CAC6* OUI:48CB6E* ID_OUI_FROM_DATABASE=Cello Electronics (UK) Ltd +OUI:48CDD3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:48D0CF* ID_OUI_FROM_DATABASE=Universal Electronics, Inc. @@ -58994,6 +59684,9 @@ OUI:48DB50* OUI:48DC2D* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:48DC9D* + ID_OUI_FROM_DATABASE=Grandprint(Beijing) Technology Co., LTD. + OUI:48DCFB* ID_OUI_FROM_DATABASE=Nokia Corporation @@ -59153,6 +59846,9 @@ OUI:4C09B4* OUI:4C09D4* ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation +OUI:4C09FA* + ID_OUI_FROM_DATABASE=FRONTIER SMART TECHNOLOGIES LTD + OUI:4C0A3D* ID_OUI_FROM_DATABASE=ADNACOM INC. @@ -59267,6 +59963,12 @@ OUI:4C2C80* OUI:4C2C83* ID_OUI_FROM_DATABASE=Zhejiang KaNong Network Technology Co.,Ltd. +OUI:4C2E5E* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + +OUI:4C2EB4* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:4C2EFE* ID_OUI_FROM_DATABASE=Shenzhen Comnect Technology Co.,LTD @@ -59420,6 +60122,9 @@ OUI:4C5262* OUI:4C52EC* ID_OUI_FROM_DATABASE=SOLARWATT GmbH +OUI:4C5369* + ID_OUI_FROM_DATABASE=YanFeng Visteon(ChongQing) Automotive Electronic Co.,Ltd + OUI:4C53FD* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. @@ -59561,6 +60266,9 @@ OUI:4C7274* OUI:4C72B9* ID_OUI_FROM_DATABASE=PEGATRON CORPORATION +OUI:4C734F* + ID_OUI_FROM_DATABASE=Juniper Networks + OUI:4C7367* ID_OUI_FROM_DATABASE=Genius Bytes Software Solutions GmbH @@ -59774,6 +60482,12 @@ OUI:4C962D* OUI:4C98EF* ID_OUI_FROM_DATABASE=Zeo +OUI:4C9D22* + ID_OUI_FROM_DATABASE=ACES Co.,Ltd + +OUI:4C9E6C* + ID_OUI_FROM_DATABASE=BROADEX TECHNOLOGIES CO.LTD + OUI:4C9E80* ID_OUI_FROM_DATABASE=KYOKKO ELECTRIC Co., Ltd. @@ -60020,6 +60734,9 @@ OUI:4CD08A* OUI:4CD0CB* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:4CD0DD* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:4CD1A1* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -60380,6 +61097,9 @@ OUI:502690* OUI:5027C7* ID_OUI_FROM_DATABASE=TECHNART Co.,Ltd +OUI:50284A* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:502873* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. @@ -60455,6 +61175,9 @@ OUI:5033F0* OUI:50382F* ID_OUI_FROM_DATABASE=ASE Group Chung-Li +OUI:50392F* + ID_OUI_FROM_DATABASE=INGRAM MICRO SERVICES + OUI:503955* ID_OUI_FROM_DATABASE=Cisco SPVTG @@ -60503,6 +61226,9 @@ OUI:50411C* OUI:5041B9* ID_OUI_FROM_DATABASE=I-O DATA DEVICE,INC. +OUI:504289* + ID_OUI_FROM_DATABASE=zte corporation + OUI:504348* ID_OUI_FROM_DATABASE=ThingsMatrix Inc. @@ -60713,6 +61439,9 @@ OUI:5067F0* OUI:50680A* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:5068AC* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:506A03* ID_OUI_FROM_DATABASE=NETGEAR @@ -60908,6 +61637,9 @@ OUI:509F3B* OUI:50A009* ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd +OUI:50A015* + ID_OUI_FROM_DATABASE=Shenzhen Yipingfang Network Technology Co., Ltd. + OUI:50A0300* ID_OUI_FROM_DATABASE=Gopod Group Limited @@ -61091,6 +61823,9 @@ OUI:50C006* OUI:50C0F0* ID_OUI_FROM_DATABASE=Artek Microelectronics Co.,Ltd. +OUI:50C1F0* + ID_OUI_FROM_DATABASE=NXP Semiconductor (Tianjin) LTD. + OUI:50C271* ID_OUI_FROM_DATABASE=SECURETECH INC @@ -61184,6 +61919,9 @@ OUI:50DAD6* OUI:50DB3F* ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT +OUI:50DCD0* + ID_OUI_FROM_DATABASE=Observint Technologies, Inc. + OUI:50DCE7* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. @@ -61265,6 +62003,9 @@ OUI:50E24E* OUI:50E549* ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. +OUI:50E636* + ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH + OUI:50E666* ID_OUI_FROM_DATABASE=Shenzhen Techtion Electronics Co., Ltd. @@ -61368,7 +62109,7 @@ OUI:50FF990* ID_OUI_FROM_DATABASE=Simicon OUI:50FF991* - ID_OUI_FROM_DATABASE=Coyote Sytem + ID_OUI_FROM_DATABASE=COYOTE SYSTEM OUI:50FF992* ID_OUI_FROM_DATABASE=SHENZHEN KINGVT ELECTRONICS CO.,LTD @@ -61571,6 +62312,9 @@ OUI:542BDE* OUI:542CEA* ID_OUI_FROM_DATABASE=PROTECTRON +OUI:542F04* + ID_OUI_FROM_DATABASE=Shanghai Longcheer Technology Co., Ltd. + OUI:542F89* ID_OUI_FROM_DATABASE=Euclid Laboratories, Inc. @@ -61622,6 +62366,9 @@ OUI:5440AD* OUI:544249* ID_OUI_FROM_DATABASE=Sony Corporation +OUI:5443B2* + ID_OUI_FROM_DATABASE=Espressif Inc. + OUI:544408* ID_OUI_FROM_DATABASE=Nokia Corporation @@ -61790,6 +62537,9 @@ OUI:54778A* OUI:54781A* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:5478C9* + ID_OUI_FROM_DATABASE=AMPAK Technology,Inc. + OUI:547975* ID_OUI_FROM_DATABASE=Nokia Corporation @@ -62036,6 +62786,9 @@ OUI:54A6DB* OUI:54A703* ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. +OUI:54A9C8* + ID_OUI_FROM_DATABASE=Home Control Singapore Pte Ltd + OUI:54A9D4* ID_OUI_FROM_DATABASE=Minibar Systems @@ -62198,6 +62951,9 @@ OUI:54E061* OUI:54E140* ID_OUI_FROM_DATABASE=INGENICO +OUI:54E15B* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:54E1AD* ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology co., ltd @@ -62309,6 +63065,9 @@ OUI:54FF82* OUI:54FFCF* ID_OUI_FROM_DATABASE=Mopria Alliance +OUI:580032* + ID_OUI_FROM_DATABASE=Genexis B.V. + OUI:5800BB* ID_OUI_FROM_DATABASE=Juniper Networks @@ -62351,6 +63110,9 @@ OUI:58108C* OUI:5810B7* ID_OUI_FROM_DATABASE=Infinix mobility limited +OUI:581122* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + OUI:581243* ID_OUI_FROM_DATABASE=AcSiP Technology Corp. @@ -62375,6 +63137,9 @@ OUI:581CBD* OUI:581D91* ID_OUI_FROM_DATABASE=Advanced Mobile Telecom co.,ltd. +OUI:581DD8* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + OUI:581F28* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -62615,6 +63380,9 @@ OUI:58528A* OUI:5853C0* ID_OUI_FROM_DATABASE=Beijing Guang Runtong Technology Development Company co.,Ltd +OUI:585595* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:5855CA* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -62693,6 +63461,9 @@ OUI:587521* OUI:587675* ID_OUI_FROM_DATABASE=Beijing ECHO Technologies Co.,Ltd +OUI:5876AC* + ID_OUI_FROM_DATABASE=SERNET (SUZHOU) TECHNOLOGIES CORPORATION + OUI:5876C5* ID_OUI_FROM_DATABASE=DIGI I'S LTD @@ -62750,6 +63521,9 @@ OUI:588694* OUI:58874C* ID_OUI_FROM_DATABASE=LITE-ON CLEAN ENERGY TECHNOLOGY CORP. +OUI:58879F* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:5887E2* ID_OUI_FROM_DATABASE=Shenzhen Coship Electronics Co., Ltd. @@ -62981,6 +63755,9 @@ OUI:58C17A* OUI:58C232* ID_OUI_FROM_DATABASE=NEC Corporation +OUI:58C356* + ID_OUI_FROM_DATABASE=EM Microelectronic + OUI:58C38B* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -63272,6 +64049,9 @@ OUI:5C0272* OUI:5C0339* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:5C045A* + ID_OUI_FROM_DATABASE=Company NA Stage & Light + OUI:5C076F* ID_OUI_FROM_DATABASE=Thought Creator @@ -63338,6 +64118,9 @@ OUI:5C18B5* OUI:5C1A6F* ID_OUI_FROM_DATABASE=Cambridge Industries(Group) Co.,Ltd. +OUI:5C1BF4* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:5C1CB9* ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. @@ -63359,6 +64142,9 @@ OUI:5C2443* OUI:5C2479* ID_OUI_FROM_DATABASE=Baltech AG +OUI:5C24E2* + ID_OUI_FROM_DATABASE=Suzhou Denbom Electronic S&T Co., Ltd + OUI:5C254C* ID_OUI_FROM_DATABASE=Avire Global Pte Ltd @@ -63488,6 +64274,9 @@ OUI:5C521E* OUI:5C5230* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:5C53C3* + ID_OUI_FROM_DATABASE=Ubee Interactive Co., Limited + OUI:5C546D* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -63722,6 +64511,9 @@ OUI:5C89D4* OUI:5C8A38* ID_OUI_FROM_DATABASE=Hewlett Packard +OUI:5C8C30* + ID_OUI_FROM_DATABASE=Taicang T&W Electronics + OUI:5C8D2D* ID_OUI_FROM_DATABASE=Shanghai Wellpay Information Technology Co., Ltd @@ -63800,6 +64592,9 @@ OUI:5CA48A* OUI:5CA4A4* ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD +OUI:5CA4F4* + ID_OUI_FROM_DATABASE=zte corporation + OUI:5CA5BC* ID_OUI_FROM_DATABASE=eero inc. @@ -63905,6 +64700,9 @@ OUI:5CC307* OUI:5CC336* ID_OUI_FROM_DATABASE=ittim +OUI:5CC563* + ID_OUI_FROM_DATABASE=HUNAN FN-LINK TECHNOLOGY LIMITED + OUI:5CC5D4* ID_OUI_FROM_DATABASE=Intel Corporate @@ -63923,6 +64721,9 @@ OUI:5CC8E3* OUI:5CC999* ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd +OUI:5CC9C0* + ID_OUI_FROM_DATABASE=Renesas Electronics (Penang) Sdn. Bhd. + OUI:5CC9D3* ID_OUI_FROM_DATABASE=PALLADIUM ENERGY ELETRONICA DA AMAZONIA LTDA @@ -64061,6 +64862,9 @@ OUI:5CE8B7* OUI:5CE8EB* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:5CE91E* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:5CEA1D* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. @@ -64073,6 +64877,9 @@ OUI:5CEB68* OUI:5CED8C* ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise +OUI:5CEDF4* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:5CEE79* ID_OUI_FROM_DATABASE=Global Digitech Co LTD @@ -64172,6 +64979,9 @@ OUI:5CF9F0* OUI:5CF9FD* ID_OUI_FROM_DATABASE=Taicang T&W Electronics +OUI:5CFA25* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + OUI:5CFAFB* ID_OUI_FROM_DATABASE=Acubit @@ -64316,6 +65126,9 @@ OUI:601803* OUI:60182E* ID_OUI_FROM_DATABASE=ShenZhen Protruly Electronic Ltd co. +OUI:60183A* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:601888* ID_OUI_FROM_DATABASE=zte corporation @@ -64346,6 +65159,9 @@ OUI:601D9D* OUI:601E02* ID_OUI_FROM_DATABASE=EltexAlatau +OUI:601E98* + ID_OUI_FROM_DATABASE=Axevast Technology + OUI:602101* ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD @@ -64529,6 +65345,9 @@ OUI:605718* OUI:60577D* ID_OUI_FROM_DATABASE=eero inc. +OUI:605B30* + ID_OUI_FROM_DATABASE=Dell Inc. + OUI:605BB4* ID_OUI_FROM_DATABASE=AzureWave Technology Inc. @@ -64628,6 +65447,9 @@ OUI:607771* OUI:6077E2* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:607D09* + ID_OUI_FROM_DATABASE=Luxshare Precision Industry Co., Ltd + OUI:607DDD* ID_OUI_FROM_DATABASE=Shenzhen Shichuangyi Electronics Co.,Ltd @@ -64724,6 +65546,9 @@ OUI:6092F5* OUI:609316* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:6095BD* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:6095CE0* ID_OUI_FROM_DATABASE=Siema Applications @@ -64907,6 +65732,9 @@ OUI:60BC4C* OUI:60BD91* ID_OUI_FROM_DATABASE=Move Innovation +OUI:60BEB4* + ID_OUI_FROM_DATABASE=S-Bluetech co., limited + OUI:60BEB5* ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company @@ -65111,6 +65939,9 @@ OUI:60E85B* OUI:60E956* ID_OUI_FROM_DATABASE=Ayla Networks, Inc +OUI:60E9AA* + ID_OUI_FROM_DATABASE=CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. + OUI:60EB5A* ID_OUI_FROM_DATABASE=Asterfusion Data Technologies Co.,Ltd @@ -65273,6 +66104,9 @@ OUI:640F28* OUI:641084* ID_OUI_FROM_DATABASE=HEXIUM Technical Development Co., Ltd. +OUI:6411A4* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + OUI:641225* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -65423,6 +66257,9 @@ OUI:643139E* OUI:643150* ID_OUI_FROM_DATABASE=Hewlett Packard +OUI:643172* + ID_OUI_FROM_DATABASE=ZHEJIANG HISING TECHNOLOGY CO.,LTD + OUI:64317E* ID_OUI_FROM_DATABASE=Dexin Corporation @@ -65513,6 +66350,9 @@ OUI:6444D5* OUI:6447E0* ID_OUI_FROM_DATABASE=Feitian Technologies Co., Ltd +OUI:64497D* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:644BC3* ID_OUI_FROM_DATABASE=Shanghai WOASiS Telecommunications Ltd., Co. @@ -65594,6 +66434,9 @@ OUI:645D92* OUI:645DD7* ID_OUI_FROM_DATABASE=Shenzhen Lifesense Medical Electronics Co., Ltd. +OUI:645DF4* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:645E10* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -65822,6 +66665,9 @@ OUI:649714* OUI:649829* ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. +OUI:64989E* + ID_OUI_FROM_DATABASE=TRINNOV AUDIO + OUI:64995D* ID_OUI_FROM_DATABASE=LGE @@ -65972,6 +66818,9 @@ OUI:64BE63* OUI:64BF6B* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:64C269* + ID_OUI_FROM_DATABASE=eero inc. + OUI:64C2DE* ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) @@ -65987,6 +66836,9 @@ OUI:64C3D6* OUI:64C403* ID_OUI_FROM_DATABASE=Quectel Wireless Solutions Co.,Ltd. +OUI:64C582* + ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd. + OUI:64C5AA* ID_OUI_FROM_DATABASE=South African Broadcasting Corporation @@ -66263,6 +67115,9 @@ OUI:64FB92* OUI:64FC8C* ID_OUI_FROM_DATABASE=Zonar Systems +OUI:64FD96* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + OUI:64FF0A* ID_OUI_FROM_DATABASE=Wistron Neweb Corporation @@ -66317,6 +67172,9 @@ OUI:681605* OUI:681729* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:6818D9* + ID_OUI_FROM_DATABASE=Hill AFB - CAPRE Group + OUI:68193F* ID_OUI_FROM_DATABASE=Digital Airways @@ -66479,6 +67337,9 @@ OUI:684B88* OUI:684CA8* ID_OUI_FROM_DATABASE=Shenzhen Herotel Tech. Co., Ltd. +OUI:684E05* + ID_OUI_FROM_DATABASE=HUNAN FN-LINK TECHNOLOGY LIMITED + OUI:684F64* ID_OUI_FROM_DATABASE=Dell Inc. @@ -66533,6 +67394,9 @@ OUI:685B36* OUI:685D43* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:685E1C* + ID_OUI_FROM_DATABASE=Texas Instruments + OUI:685E6B* ID_OUI_FROM_DATABASE=PowerRay Co., Ltd. @@ -66848,6 +67712,9 @@ OUI:68A47D* OUI:68A682* ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd +OUI:68A7B4* + ID_OUI_FROM_DATABASE=Honor Device Co., Ltd. + OUI:68A828* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -66896,6 +67763,12 @@ OUI:68B43A* OUI:68B599* ID_OUI_FROM_DATABASE=Hewlett Packard +OUI:68B691* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + +OUI:68B6B3* + ID_OUI_FROM_DATABASE=Espressif Inc. + OUI:68B6FC* ID_OUI_FROM_DATABASE=Hitron Technologies. Inc @@ -67016,6 +67889,9 @@ OUI:68E41F* OUI:68E478* ID_OUI_FROM_DATABASE=Qingdao Haier Technology Co.,Ltd +OUI:68E74A* + ID_OUI_FROM_DATABASE=Texas Instruments + OUI:68E7C2* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -67121,6 +67997,9 @@ OUI:6C05D5* OUI:6C06D6* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. +OUI:6C0831* + ID_OUI_FROM_DATABASE=ANALOG SYSTEMS + OUI:6C090A* ID_OUI_FROM_DATABASE=GEMATICA SRL @@ -67151,6 +68030,9 @@ OUI:6C0EE6* OUI:6C0F0B* ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd. +OUI:6C0F61* + ID_OUI_FROM_DATABASE=Hypervolt Ltd + OUI:6C0F6A* ID_OUI_FROM_DATABASE=JDC Tech Co., Ltd. @@ -67172,6 +68054,51 @@ OUI:6C146E* OUI:6C14F7* ID_OUI_FROM_DATABASE=Erhardt+Leimer GmbH +OUI:6C15240* + ID_OUI_FROM_DATABASE=DEFA AS + +OUI:6C15241* + ID_OUI_FROM_DATABASE=Telsonic AG + +OUI:6C15242* + ID_OUI_FROM_DATABASE=Linkplay + +OUI:6C15243* + ID_OUI_FROM_DATABASE=Forcite Helmet Systems Pty Ltd + +OUI:6C15244* + ID_OUI_FROM_DATABASE=Magicyo Technology CO., LTD. + +OUI:6C15245* + ID_OUI_FROM_DATABASE=Shenzhen Electron Technology Co., LTD. + +OUI:6C15246* + ID_OUI_FROM_DATABASE=Kunshan Abram Software Technology Co.,Ltd. + +OUI:6C15247* + ID_OUI_FROM_DATABASE=Motium Pty Ltd + +OUI:6C15248* + ID_OUI_FROM_DATABASE=ShenZhen Chainway Information Technology Co., Ltd. + +OUI:6C15249* + ID_OUI_FROM_DATABASE=D-HOME SMAART + +OUI:6C1524A* + ID_OUI_FROM_DATABASE=STERIS + +OUI:6C1524B* + ID_OUI_FROM_DATABASE=Annapurna labs + +OUI:6C1524C* + ID_OUI_FROM_DATABASE=CORAL-TAIYI + +OUI:6C1524D* + ID_OUI_FROM_DATABASE=SYMLINK CORPORATION + +OUI:6C1524E* + ID_OUI_FROM_DATABASE=AEC s.r.l. + OUI:6C15F9* ID_OUI_FROM_DATABASE=Nautronix Limited @@ -67226,6 +68153,9 @@ OUI:6C23B9* OUI:6C23CB* ID_OUI_FROM_DATABASE=Wattty Corporation +OUI:6C2408* + ID_OUI_FROM_DATABASE=LCFC(Hefei) Electronics Technology Co., Ltd + OUI:6C2483* ID_OUI_FROM_DATABASE=Microsoft Mobile Oy @@ -67508,6 +68438,9 @@ OUI:6C639C* OUI:6C641A* ID_OUI_FROM_DATABASE=Penguin Computing +OUI:6C67EF* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:6C6A77* ID_OUI_FROM_DATABASE=Intel Corporate @@ -67610,6 +68543,51 @@ OUI:6C9106* OUI:6C92BF* ID_OUI_FROM_DATABASE=Inspur Electronic Information Industry Co.,Ltd. +OUI:6C93080* + ID_OUI_FROM_DATABASE=Braums + +OUI:6C93081* + ID_OUI_FROM_DATABASE=WATERFORD CONSULTANTS LLC + +OUI:6C93082* + ID_OUI_FROM_DATABASE=ZHEJIANG XIAN DA Environmental Technology Co., Ltd + +OUI:6C93083* + ID_OUI_FROM_DATABASE=LightnTec GmbH + +OUI:6C93084* + ID_OUI_FROM_DATABASE=Estelar s.r.o + +OUI:6C93085* + ID_OUI_FROM_DATABASE=Shenzhen C & D Electronics Co., Ltd. + +OUI:6C93086* + ID_OUI_FROM_DATABASE=Uconfree technology(shenzhen)limited + +OUI:6C93087* + ID_OUI_FROM_DATABASE=Liberty AV Solutions + +OUI:6C93088* + ID_OUI_FROM_DATABASE=Hangzhou Risco System Co.,Ltd + +OUI:6C93089* + ID_OUI_FROM_DATABASE=Shenzhen DOOGEE Hengtong Technology CO., LTD + +OUI:6C9308A* + ID_OUI_FROM_DATABASE=Shenzhen TOPWAY Technology Co.,LTD + +OUI:6C9308B* + ID_OUI_FROM_DATABASE=Shenzhen EZpro Sound & Light Technology Co., Ltd. + +OUI:6C9308C* + ID_OUI_FROM_DATABASE=Shenzhen haichangxing Technology Co., Ltd. + +OUI:6C9308D* + ID_OUI_FROM_DATABASE=Annapurna labs + +OUI:6C9308E* + ID_OUI_FROM_DATABASE=ANDDORO LLC + OUI:6C9354* ID_OUI_FROM_DATABASE=Yaojin Technology (Shenzhen) Co., LTD. @@ -67628,6 +68606,9 @@ OUI:6C9522* OUI:6C96CF* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:6C976D* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + OUI:6C98EB* ID_OUI_FROM_DATABASE=Riverbed Technology, Inc. @@ -67637,6 +68618,9 @@ OUI:6C9961* OUI:6C9989* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:6C999D* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + OUI:6C9AC9* ID_OUI_FROM_DATABASE=Valentine Research, Inc. @@ -67661,6 +68645,9 @@ OUI:6CA0B4* OUI:6CA100* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:6CA401* + ID_OUI_FROM_DATABASE=essensys plc + OUI:6CA4D1* ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD @@ -67727,6 +68714,9 @@ OUI:6CADF8* OUI:6CAE8B* ID_OUI_FROM_DATABASE=IBM Corporation +OUI:6CAEE3* + ID_OUI_FROM_DATABASE=Nokia + OUI:6CAEF6* ID_OUI_FROM_DATABASE=eero inc. @@ -67739,12 +68729,18 @@ OUI:6CB0CE* OUI:6CB0FD* ID_OUI_FROM_DATABASE=Shenzhen Xinghai Iot Technology Co.,Ltd +OUI:6CB158* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + OUI:6CB227* ID_OUI_FROM_DATABASE=Sony Video & Sound Products Inc. OUI:6CB2AE* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:6CB2FD* + ID_OUI_FROM_DATABASE=Texas Instruments + OUI:6CB311* ID_OUI_FROM_DATABASE=Shenzhen Lianrui Electronics Co.,Ltd @@ -68222,6 +69218,9 @@ OUI:70305D* OUI:70305E* ID_OUI_FROM_DATABASE=Nanjing Zhongke Menglian Information Technology Co.,LTD +OUI:70317F* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:703187* ID_OUI_FROM_DATABASE=ACX GmbH @@ -68345,6 +69344,51 @@ OUI:704FB8* OUI:7050AF* ID_OUI_FROM_DATABASE=BSkyB Ltd +OUI:7050E70* + ID_OUI_FROM_DATABASE=Shenzhen C & D Electronics Co., Ltd. + +OUI:7050E71* + ID_OUI_FROM_DATABASE=Annapurna labs + +OUI:7050E72* + ID_OUI_FROM_DATABASE=Electronic's Time SRL + +OUI:7050E73* + ID_OUI_FROM_DATABASE=Skychers Creations ShenZhen Limited + +OUI:7050E74* + ID_OUI_FROM_DATABASE=Quantumdoor Technologies, Inc. + +OUI:7050E75* + ID_OUI_FROM_DATABASE=Wall Box Chargers, S.L. + +OUI:7050E76* + ID_OUI_FROM_DATABASE=Nippon Pulse America, Inc. + +OUI:7050E77* + ID_OUI_FROM_DATABASE=Yoctopuce + +OUI:7050E78* + ID_OUI_FROM_DATABASE=Shenzhen Dangs Science and Technology CO.,Ltd. + +OUI:7050E79* + ID_OUI_FROM_DATABASE=Elastics.cloud + +OUI:7050E7A* + ID_OUI_FROM_DATABASE=Guangzhou Tianhe High Tech Industrial Development Zone Zhongsheng Electrical Limited Company + +OUI:7050E7B* + ID_OUI_FROM_DATABASE=Beijing Shannoncyber Technology Co.,Ltd + +OUI:7050E7C* + ID_OUI_FROM_DATABASE=shenzhen newbridge communication equipment CO.,LTD + +OUI:7050E7D* + ID_OUI_FROM_DATABASE=Eta Compute Inc. + +OUI:7050E7E* + ID_OUI_FROM_DATABASE=KFBIO (KONFOONG BIOINFORMATION TECH CO.,LTD) + OUI:7052C5* ID_OUI_FROM_DATABASE=Avaya Inc @@ -68447,6 +69491,9 @@ OUI:7065A3* OUI:70661B* ID_OUI_FROM_DATABASE=Sonova AG +OUI:70662A* + ID_OUI_FROM_DATABASE=Sony Interactive Entertainment Inc. + OUI:706655* ID_OUI_FROM_DATABASE=AzureWave Technology Inc. @@ -68531,6 +69578,9 @@ OUI:70708B* OUI:7070AA* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. +OUI:7070FC* + ID_OUI_FROM_DATABASE=GOLD&WATER INDUSTRIAL LIMITED + OUI:7071B3* ID_OUI_FROM_DATABASE=Brain Corporation @@ -68618,6 +69668,9 @@ OUI:708540* OUI:7085C2* ID_OUI_FROM_DATABASE=ASRock Incorporation +OUI:7085C4* + ID_OUI_FROM_DATABASE=Ruijie Networks Co.,LTD + OUI:7085C6* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. @@ -68765,6 +69818,9 @@ OUI:70A56A* OUI:70A66A* ID_OUI_FROM_DATABASE=Prox Dynamics AS +OUI:70A6BD* + ID_OUI_FROM_DATABASE=Honor Device Co., Ltd. + OUI:70A6CC* ID_OUI_FROM_DATABASE=Intel Corporate @@ -68780,9 +69836,15 @@ OUI:70A8D3* OUI:70A8E3* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:70A983* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:70AAB2* ID_OUI_FROM_DATABASE=BlackBerry RTS +OUI:70AC08* + ID_OUI_FROM_DATABASE=Silicon Laboratories + OUI:70ACD7* ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd @@ -79665,7 +80727,7 @@ OUI:70B3D5E2E* ID_OUI_FROM_DATABASE=Merz s.r.o. OUI:70B3D5E2F* - ID_OUI_FROM_DATABASE=Flextronics International Kft. + ID_OUI_FROM_DATABASE=Flextronics International Kft OUI:70B3D5E30* ID_OUI_FROM_DATABASE=QUISS AG @@ -79875,7 +80937,7 @@ OUI:70B3D5E74* ID_OUI_FROM_DATABASE=Exfrontier Co., Ltd. OUI:70B3D5E75* - ID_OUI_FROM_DATABASE=Nke + ID_OUI_FROM_DATABASE=Watteco OUI:70B3D5E76* ID_OUI_FROM_DATABASE=Dorsett Technologies Inc @@ -81161,6 +82223,9 @@ OUI:70D5E7* OUI:70D6B6* ID_OUI_FROM_DATABASE=Metrum Technologies +OUI:70D823* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:70D880* ID_OUI_FROM_DATABASE=Upos System sp. z o.o. @@ -81365,6 +82430,9 @@ OUI:74042B* OUI:7404F0* ID_OUI_FROM_DATABASE=Mobiwire Mobiles (NingBo) Co., LTD +OUI:7404F1* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:7405A5* ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. @@ -81677,6 +82745,9 @@ OUI:74547D* OUI:745612* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. +OUI:74563C* + ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. + OUI:745798* ID_OUI_FROM_DATABASE=TRUMPF Laser GmbH + Co. KG @@ -81764,6 +82835,9 @@ OUI:745F90* OUI:745FAE* ID_OUI_FROM_DATABASE=TSL PPL +OUI:74604C* + ID_OUI_FROM_DATABASE=RØDE + OUI:7460FA* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -81788,6 +82862,9 @@ OUI:746630* OUI:7467F7* ID_OUI_FROM_DATABASE=Extreme Networks, Inc. +OUI:74694A* + ID_OUI_FROM_DATABASE=Sichuan Tianyi Comheart Telecom Co.,LTD + OUI:746A3A* ID_OUI_FROM_DATABASE=Aperi Corporation @@ -81812,6 +82889,9 @@ OUI:746F19* OUI:746F3D* ID_OUI_FROM_DATABASE=Contec GmbH +OUI:746F88* + ID_OUI_FROM_DATABASE=zte corporation + OUI:746FF7* ID_OUI_FROM_DATABASE=Wistron Neweb Corporation @@ -81821,6 +82901,9 @@ OUI:747069* OUI:7470FD* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:74718B* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:74721E* ID_OUI_FROM_DATABASE=Edison Labs Inc. @@ -81845,6 +82928,9 @@ OUI:747548* OUI:74765B* ID_OUI_FROM_DATABASE=Quectel Wireless Solutions Co.,Ltd. +OUI:74767D* + ID_OUI_FROM_DATABASE=shenzhen kexint technology co.,ltd + OUI:747818* ID_OUI_FROM_DATABASE=Jurumani Solutions @@ -81881,6 +82967,9 @@ OUI:7483C2* OUI:7483EF* ID_OUI_FROM_DATABASE=Arista Networks +OUI:748469* + ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd + OUI:7484E1* ID_OUI_FROM_DATABASE=Dongguan Haoyuan Electronics Co.,Ltd @@ -81974,6 +83063,9 @@ OUI:7495EC* OUI:749637* ID_OUI_FROM_DATABASE=Todaair Electronic Co., Ltd +OUI:749779* + ID_OUI_FROM_DATABASE=CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. + OUI:749781* ID_OUI_FROM_DATABASE=zte corporation @@ -82073,6 +83165,9 @@ OUI:74B587* OUI:74B6B6* ID_OUI_FROM_DATABASE=eero inc. +OUI:74B725* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:74B7B3* ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd @@ -82163,6 +83258,9 @@ OUI:74D285* OUI:74D435* ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. +OUI:74D4DD* + ID_OUI_FROM_DATABASE=Quanta Computer Inc. + OUI:74D637* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. @@ -82187,6 +83285,9 @@ OUI:74D83E* OUI:74D850* ID_OUI_FROM_DATABASE=Evrisko Systems +OUI:74D9EB* + ID_OUI_FROM_DATABASE=Petabit Scale, Inc. + OUI:74DA38* ID_OUI_FROM_DATABASE=Edimax Technology Co. Ltd. @@ -82202,6 +83303,9 @@ OUI:74DAEA* OUI:74DBD1* ID_OUI_FROM_DATABASE=Ebay Inc +OUI:74DDCB* + ID_OUI_FROM_DATABASE=China Leadshine Technology Co.,Ltd + OUI:74DE2B* ID_OUI_FROM_DATABASE=Liteon Technology Corporation @@ -82463,6 +83567,9 @@ OUI:7802B7* OUI:7802F8* ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd +OUI:78034F* + ID_OUI_FROM_DATABASE=Nokia + OUI:780473* ID_OUI_FROM_DATABASE=Texas Instruments @@ -82565,6 +83672,9 @@ OUI:781305E* OUI:7813E0* ID_OUI_FROM_DATABASE=FUJIAN STAR-NET COMMUNICATION CO.,LTD +OUI:78152D* + ID_OUI_FROM_DATABASE=UNION CHIP TECHNOLOGY LIMITED + OUI:781735* ID_OUI_FROM_DATABASE=Nokia Shanghai Bell Co., Ltd. @@ -82607,6 +83717,9 @@ OUI:782079* OUI:7820A5* ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd +OUI:7820BD* + ID_OUI_FROM_DATABASE=Polysense (Beijing) Technologies Co. Ltd + OUI:782184* ID_OUI_FROM_DATABASE=Espressif Inc. @@ -82682,6 +83795,9 @@ OUI:78321B* OUI:78324F* ID_OUI_FROM_DATABASE=Millennium Group, Inc. +OUI:783486* + ID_OUI_FROM_DATABASE=Nokia + OUI:7835A0* ID_OUI_FROM_DATABASE=Zurn Industries LLC @@ -82835,6 +83951,9 @@ OUI:78595E* OUI:785968* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. +OUI:785B64* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:785C28* ID_OUI_FROM_DATABASE=Prime Motion Inc. @@ -82919,6 +84038,9 @@ OUI:78653B* OUI:786559* ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS +OUI:78669D* + ID_OUI_FROM_DATABASE=Hui Zhou Gaoshengda Technology Co.,LTD + OUI:7866AE* ID_OUI_FROM_DATABASE=ZTEC Instruments, Inc. @@ -82952,6 +84074,9 @@ OUI:786DEB* OUI:787052* ID_OUI_FROM_DATABASE=Welotec GmbH +OUI:787104* + ID_OUI_FROM_DATABASE=Sichuan Tianyi Comheart Telecom Co.,LTD + OUI:78719C* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. @@ -83090,6 +84215,9 @@ OUI:788E33* OUI:7890A2* ID_OUI_FROM_DATABASE=zte corporation +OUI:7891DE* + ID_OUI_FROM_DATABASE=Guangdong ACIGA Science&Technology Co.,Ltd + OUI:7891E9* ID_OUI_FROM_DATABASE=Raisecom Technology CO.,LTD @@ -83222,6 +84350,9 @@ OUI:78ACC0* OUI:78AE0C* ID_OUI_FROM_DATABASE=Far South Networks +OUI:78AF08* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:78AF58* ID_OUI_FROM_DATABASE=GIMASI SA @@ -83469,7 +84600,7 @@ OUI:78D4F10* ID_OUI_FROM_DATABASE=Burisch Elektronik Bauteile GmbH OUI:78D4F11* - ID_OUI_FROM_DATABASE=Cartender + ID_OUI_FROM_DATABASE=Silla Industries OUI:78D4F12* ID_OUI_FROM_DATABASE=Lyngsoe Systems @@ -84137,6 +85268,9 @@ OUI:7C696B* OUI:7C69F6* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:7C6A60* + ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd. + OUI:7C6AB3* ID_OUI_FROM_DATABASE=IBC TECHNOLOGIES INC. @@ -84216,7 +85350,7 @@ OUI:7C70BC9* ID_OUI_FROM_DATABASE=dogtra OUI:7C70BCA* - ID_OUI_FROM_DATABASE=Ametek VIS + ID_OUI_FROM_DATABASE=Motec GmbH OUI:7C70BCB* ID_OUI_FROM_DATABASE=Tohan Engineering Corporation @@ -84809,6 +85943,9 @@ OUI:7CDFA1* OUI:7CE044* ID_OUI_FROM_DATABASE=NEON Inc +OUI:7CE152* + ID_OUI_FROM_DATABASE=THE GOODYEAR TIRE & RUBBER COMPANY + OUI:7CE1FF* ID_OUI_FROM_DATABASE=Computer Performance, Inc. DBA Digital Loggers, Inc. @@ -84845,12 +85982,18 @@ OUI:7CEC79* OUI:7CEC9B* ID_OUI_FROM_DATABASE=Fuzhou Teraway Information Technology Co.,Ltd +OUI:7CECB1* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:7CED8D* ID_OUI_FROM_DATABASE=Microsoft OUI:7CEF18* ID_OUI_FROM_DATABASE=Creative Product Design Pty. Ltd. +OUI:7CEF40* + ID_OUI_FROM_DATABASE=Nextorage Corporation + OUI:7CEF61* ID_OUI_FROM_DATABASE=STR Elektronik Josef Schlechtinger GmbH @@ -84947,6 +86090,27 @@ OUI:80029C* OUI:8002DF* ID_OUI_FROM_DATABASE=ORA Inc. +OUI:8002F40* + ID_OUI_FROM_DATABASE=BK Networks Co,. Ltd. + +OUI:8002F41* + ID_OUI_FROM_DATABASE=Sichuan lookout environment protection technology co.,Ltd + +OUI:8002F43* + ID_OUI_FROM_DATABASE=Shenzhen Suanzi Technology Co., Ltd + +OUI:8002F45* + ID_OUI_FROM_DATABASE=Sichuan Fanyi Technology Co. Ltd. + +OUI:8002F49* + ID_OUI_FROM_DATABASE=XUNDI(XIAMEN) ELECTRONIC TECHNOLOGY CO.,LTD. + +OUI:8002F4A* + ID_OUI_FROM_DATABASE=PassiveLogic + +OUI:8002F4C* + ID_OUI_FROM_DATABASE=Wuhan Glory Road Intelligent Technology Co., Ltd. + OUI:800384* ID_OUI_FROM_DATABASE=Ruckus Wireless @@ -85172,6 +86336,9 @@ OUI:803B9A* OUI:803BF6* ID_OUI_FROM_DATABASE=LOOK EASY INTERNATIONAL LIMITED +OUI:803C20* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:803E48* ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT @@ -85259,6 +86426,9 @@ OUI:8059FD* OUI:805A04* ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) +OUI:805B65* + ID_OUI_FROM_DATABASE=LG Innotek + OUI:805E0C* ID_OUI_FROM_DATABASE=YEALINK(XIAMEN) NETWORK TECHNOLOGY CO.,LTD. @@ -85274,6 +86444,9 @@ OUI:805FC5* OUI:806007* ID_OUI_FROM_DATABASE=RIM +OUI:806036* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:8060B7* ID_OUI_FROM_DATABASE=CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. @@ -85304,6 +86477,9 @@ OUI:8065E9* OUI:806629* ID_OUI_FROM_DATABASE=Prescope Technologies CO.,LTD. +OUI:80691A* + ID_OUI_FROM_DATABASE=Belkin International Inc. + OUI:806933* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -85535,6 +86711,9 @@ OUI:8096CA* OUI:80971B* ID_OUI_FROM_DATABASE=Altenergy Power System,Inc. +OUI:809733* + ID_OUI_FROM_DATABASE=Shenzhen Elebao Technology Co., Ltd + OUI:809B20* ID_OUI_FROM_DATABASE=Intel Corporate @@ -85644,7 +86823,7 @@ OUI:80C16E* ID_OUI_FROM_DATABASE=Hewlett Packard OUI:80C3BA* - ID_OUI_FROM_DATABASE=Sennheiser electronic GmbH & Co. KG + ID_OUI_FROM_DATABASE=Sennheiser Consumer Audio GmbH OUI:80C501* ID_OUI_FROM_DATABASE=OctoGate IT Security Systems GmbH @@ -86300,6 +87479,9 @@ OUI:8468C8* OUI:846991* ID_OUI_FROM_DATABASE=Nokia +OUI:846993* + ID_OUI_FROM_DATABASE=HP Inc. + OUI:846A66* ID_OUI_FROM_DATABASE=Sumitomo Kizai Co.,Ltd. @@ -86315,6 +87497,9 @@ OUI:846EB1* OUI:846FCE* ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +OUI:8470D7* + ID_OUI_FROM_DATABASE=eero inc. + OUI:847127* ID_OUI_FROM_DATABASE=Silicon Laboratories @@ -86408,6 +87593,9 @@ OUI:848506* OUI:84850A* ID_OUI_FROM_DATABASE=Hella Sonnen- und Wetterschutztechnik GmbH +OUI:848553* + ID_OUI_FROM_DATABASE=Biznes Systema Telecom, LLC + OUI:8485E6* ID_OUI_FROM_DATABASE=Guangdong Asano Technology CO.,Ltd. @@ -86540,6 +87728,9 @@ OUI:84930C* OUI:8493A0* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. +OUI:8493B2* + ID_OUI_FROM_DATABASE=zte corporation + OUI:84948C* ID_OUI_FROM_DATABASE=Hitron Technologies. Inc @@ -86711,6 +87902,9 @@ OUI:84C3E8* OUI:84C5A6* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:84C692* + ID_OUI_FROM_DATABASE=Texas Instruments + OUI:84C727* ID_OUI_FROM_DATABASE=Gnodal Ltd @@ -86918,6 +88112,9 @@ OUI:84F129* OUI:84F147* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:84F1D0* + ID_OUI_FROM_DATABASE=EHOOME IOT PRIVATE LIMITED + OUI:84F3EB* ID_OUI_FROM_DATABASE=Espressif Inc. @@ -86960,6 +88157,12 @@ OUI:880118* OUI:8801F2* ID_OUI_FROM_DATABASE=Vitec System Engineering Inc. +OUI:8801F9* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:88034C* + ID_OUI_FROM_DATABASE=WEIFANG GOERTEK ELECTRONICS CO.,LTD + OUI:880355* ID_OUI_FROM_DATABASE=Arcadyan Technology Corporation @@ -87128,6 +88331,9 @@ OUI:883C1C* OUI:883D24* ID_OUI_FROM_DATABASE=Google, Inc. +OUI:883F0C* + ID_OUI_FROM_DATABASE=system a.v. co., ltd. + OUI:883F4A* ID_OUI_FROM_DATABASE=Texas Instruments @@ -87359,6 +88565,9 @@ OUI:8866A5* OUI:88685C* ID_OUI_FROM_DATABASE=Shenzhen ChuangDao & Perpetual Eternal Technology Co.,Ltd +OUI:88693D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:886AB1* ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. @@ -87482,6 +88691,9 @@ OUI:888E68* OUI:888E7F* ID_OUI_FROM_DATABASE=ATOP CORPORATION +OUI:888FA4* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:889009* ID_OUI_FROM_DATABASE=Juniper Networks @@ -87722,6 +88934,9 @@ OUI:88BFE4* OUI:88C08B* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:88C174* + ID_OUI_FROM_DATABASE=zte corporation + OUI:88C227* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -87797,6 +89012,9 @@ OUI:88C9B3E* OUI:88C9D0* ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) +OUI:88C9E8* + ID_OUI_FROM_DATABASE=Sony Corporation + OUI:88CB87* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -87941,6 +89159,9 @@ OUI:88F031* OUI:88F077* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:88F2BD* + ID_OUI_FROM_DATABASE=GD Midea Air-Conditioning Equipment Co.,Ltd. + OUI:88F488* ID_OUI_FROM_DATABASE=cellon communications technology(shenzhen)Co.,Ltd. @@ -87959,6 +89180,9 @@ OUI:88F7C7* OUI:88F872* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:88FC5D* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:88FCA6* ID_OUI_FROM_DATABASE=devolo AG @@ -88079,12 +89303,18 @@ OUI:8C147DE* OUI:8C14B4* ID_OUI_FROM_DATABASE=zte corporation +OUI:8C1553* + ID_OUI_FROM_DATABASE=Beijing Memblaze Technology Co Ltd + OUI:8C15C7* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD OUI:8C1645* ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology co., ltd +OUI:8C1759* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:8C17B6* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. @@ -88193,6 +89423,9 @@ OUI:8C1CDAE* OUI:8C1D96* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:8C1E80* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:8C1ED9* ID_OUI_FROM_DATABASE=Beijing Unigroup Tsingteng Microsystem Co., LTD. @@ -88202,6 +89435,24 @@ OUI:8C1F64000* OUI:8C1F64003* ID_OUI_FROM_DATABASE=Brighten Controls LLP +OUI:8C1F64017* + ID_OUI_FROM_DATABASE=Farmote Limited + +OUI:8C1F6401A* + ID_OUI_FROM_DATABASE=Paragraf + +OUI:8C1F6401E* + ID_OUI_FROM_DATABASE=SCIREQ Scientific Respiratory Equipment Inc + +OUI:8C1F64045* + ID_OUI_FROM_DATABASE=VEILUX INC. + +OUI:8C1F64059* + ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme + +OUI:8C1F6405F* + ID_OUI_FROM_DATABASE=ESCAD AUTOMATION GmbH + OUI:8C1F6406D* ID_OUI_FROM_DATABASE=Monnit Corporation @@ -88223,6 +89474,9 @@ OUI:8C1F64086* OUI:8C1F6408B* ID_OUI_FROM_DATABASE=Shanghai Shenxu Technology Co., Ltd +OUI:8C1F6408F* + ID_OUI_FROM_DATABASE=AixControl GmbH + OUI:8C1F64099* ID_OUI_FROM_DATABASE=Pantherun Technologies Pvt Ltd @@ -88238,9 +89492,18 @@ OUI:8C1F640A8* OUI:8C1F640AB* ID_OUI_FROM_DATABASE=Norbit ODM AS +OUI:8C1F640AF* + ID_OUI_FROM_DATABASE=FORSEE POWER + +OUI:8C1F640B0* + ID_OUI_FROM_DATABASE=Bunka Shutter Co., Ltd. + OUI:8C1F640B8* ID_OUI_FROM_DATABASE=Signatrol Ltd +OUI:8C1F640C0* + ID_OUI_FROM_DATABASE=Active Research Limited + OUI:8C1F640C5* ID_OUI_FROM_DATABASE=TechnipFMC @@ -88250,6 +89513,9 @@ OUI:8C1F640D6* OUI:8C1F640E0* ID_OUI_FROM_DATABASE=Autopharma +OUI:8C1F640E6* + ID_OUI_FROM_DATABASE=Cleanwatts Digital, S.A. + OUI:8C1F640EA* ID_OUI_FROM_DATABASE=SmartSky Networks LLC @@ -88268,6 +89534,9 @@ OUI:8C1F64103* OUI:8C1F64111* ID_OUI_FROM_DATABASE=ISAC SRL +OUI:8C1F64115* + ID_OUI_FROM_DATABASE=Neuralog LP + OUI:8C1F64118* ID_OUI_FROM_DATABASE=Automata GmbH & Co. KG @@ -88277,6 +89546,9 @@ OUI:8C1F6411F* OUI:8C1F64128* ID_OUI_FROM_DATABASE=YULISTA INTEGRATED SOLUTION +OUI:8C1F6412B* + ID_OUI_FROM_DATABASE=Beijing Tongtech Technology Co., Ltd. + OUI:8C1F64135* ID_OUI_FROM_DATABASE=Yuval Fichman @@ -88304,6 +89576,9 @@ OUI:8C1F64177* OUI:8C1F64193* ID_OUI_FROM_DATABASE=Sicon srl +OUI:8C1F64194* + ID_OUI_FROM_DATABASE=TIFLEX + OUI:8C1F6419B* ID_OUI_FROM_DATABASE=FeedFlo @@ -88325,6 +89600,9 @@ OUI:8C1F641BF* OUI:8C1F641C2* ID_OUI_FROM_DATABASE=Solid Invent Ltda. +OUI:8C1F641CB* + ID_OUI_FROM_DATABASE=SASYS e.K. + OUI:8C1F641D1* ID_OUI_FROM_DATABASE=AS Strömungstechnik GmbH @@ -88337,6 +89615,9 @@ OUI:8C1F641E1* OUI:8C1F641E3* ID_OUI_FROM_DATABASE=WBNet +OUI:8C1F641EF* + ID_OUI_FROM_DATABASE=Tantronic AG + OUI:8C1F64204* ID_OUI_FROM_DATABASE=castcore @@ -88352,9 +89633,15 @@ OUI:8C1F64224* OUI:8C1F64227* ID_OUI_FROM_DATABASE=Digilens +OUI:8C1F6422E* + ID_OUI_FROM_DATABASE=Jide Car Rastreamento e Monitoramento LTDA + OUI:8C1F64242* ID_OUI_FROM_DATABASE=GIORDANO CONTROLS SPA +OUI:8C1F64254* + ID_OUI_FROM_DATABASE=Zhuhai Yunzhou Intelligence Technology Ltd. + OUI:8C1F64256* ID_OUI_FROM_DATABASE=Landinger @@ -88370,9 +89657,15 @@ OUI:8C1F64264* OUI:8C1F64270* ID_OUI_FROM_DATABASE=Xi‘an Hangguang Satellite and Control Technology Co.,Ltd +OUI:8C1F64274* + ID_OUI_FROM_DATABASE=INVIXIUM ACCESS INC + OUI:8C1F6428A* ID_OUI_FROM_DATABASE=Arcopie +OUI:8C1F6428C* + ID_OUI_FROM_DATABASE=Sakura Seiki Co.,Ltd. + OUI:8C1F64296* ID_OUI_FROM_DATABASE=Roog zhi tong Technology(Beijing) Co.,Ltd @@ -88391,6 +89684,9 @@ OUI:8C1F642B6* OUI:8C1F642C2* ID_OUI_FROM_DATABASE=TEX COMPUTER SRL +OUI:8C1F642C3* + ID_OUI_FROM_DATABASE=TeraDiode / Panasonic + OUI:8C1F642C8* ID_OUI_FROM_DATABASE=BRS Sistemas Eletrônicos @@ -88403,6 +89699,9 @@ OUI:8C1F642EF* OUI:8C1F642F5* ID_OUI_FROM_DATABASE=Florida R&D Associates LLC +OUI:8C1F642FD* + ID_OUI_FROM_DATABASE=Enestone Corporation + OUI:8C1F64304* ID_OUI_FROM_DATABASE=Jemac Sweden AB @@ -88412,6 +89711,9 @@ OUI:8C1F64306* OUI:8C1F6430A* ID_OUI_FROM_DATABASE=XCOM Labs +OUI:8C1F64316* + ID_OUI_FROM_DATABASE=Potter Electric Signal Company + OUI:8C1F6431A* ID_OUI_FROM_DATABASE=Asiga Pty Ltd @@ -88421,6 +89723,9 @@ OUI:8C1F64328* OUI:8C1F64330* ID_OUI_FROM_DATABASE=Vision Systems Safety Tech +OUI:8C1F6435C* + ID_OUI_FROM_DATABASE=Opgal Optronic Industries ltd + OUI:8C1F6435D* ID_OUI_FROM_DATABASE=Security&Best @@ -88436,9 +89741,15 @@ OUI:8C1F64382* OUI:8C1F64385* ID_OUI_FROM_DATABASE=Multilane Inc +OUI:8C1F6438B* + ID_OUI_FROM_DATABASE=Borrell USA Corp + OUI:8C1F6438D* ID_OUI_FROM_DATABASE=Wilson Electronics +OUI:8C1F6438E* + ID_OUI_FROM_DATABASE=Wartsila Voyage Limited + OUI:8C1F64391* ID_OUI_FROM_DATABASE=CPC (UK) @@ -88454,6 +89765,9 @@ OUI:8C1F643A4* OUI:8C1F643AD* ID_OUI_FROM_DATABASE=TowerIQ +OUI:8C1F643B2* + ID_OUI_FROM_DATABASE=Real Digital + OUI:8C1F643B5* ID_OUI_FROM_DATABASE=SVMS @@ -88463,18 +89777,30 @@ OUI:8C1F643C4* OUI:8C1F643C6* ID_OUI_FROM_DATABASE=Wavestream Corp +OUI:8C1F643D1* + ID_OUI_FROM_DATABASE=EMIT GmbH + OUI:8C1F643E0* ID_OUI_FROM_DATABASE=YPP Corporation OUI:8C1F643E8* ID_OUI_FROM_DATABASE=Ruichuangte +OUI:8C1F643F4* + ID_OUI_FROM_DATABASE=ACTELSER S.L. + OUI:8C1F643FE* ID_OUI_FROM_DATABASE=Plum sp. z.o.o. OUI:8C1F643FF* ID_OUI_FROM_DATABASE=UISEE(SHANGHAI) AUTOMOTIVE TECHNOLOGIES LTD. +OUI:8C1F6440C* + ID_OUI_FROM_DATABASE=Sichuan Aiyijan Technology Company Ltd. + +OUI:8C1F6440E* + ID_OUI_FROM_DATABASE=Baker Hughes EMEA + OUI:8C1F64414* ID_OUI_FROM_DATABASE=INSEVIS GmbH @@ -88484,6 +89810,9 @@ OUI:8C1F64417* OUI:8C1F6441D* ID_OUI_FROM_DATABASE=Aspen Spectra Sdn Bhd +OUI:8C1F64429* + ID_OUI_FROM_DATABASE=Abbott Diagnostics Technologies AS + OUI:8C1F6442B* ID_OUI_FROM_DATABASE=Gamber Johnson-LLC @@ -88508,12 +89837,21 @@ OUI:8C1F64466* OUI:8C1F64472* ID_OUI_FROM_DATABASE=Surge Networks, Inc. +OUI:8C1F6447A* + ID_OUI_FROM_DATABASE=Missing Link Electronics, Inc. + +OUI:8C1F64489* + ID_OUI_FROM_DATABASE=HUPI + OUI:8C1F64493* ID_OUI_FROM_DATABASE=Security Products International, LLC OUI:8C1F64498* ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd +OUI:8C1F644AC* + ID_OUI_FROM_DATABASE=Vekto + OUI:8C1F644B0* ID_OUI_FROM_DATABASE=U -MEI-DAH INT'L ENTERPRISE CO.,LTD. @@ -88529,6 +89867,9 @@ OUI:8C1F644C7* OUI:8C1F644CD* ID_OUI_FROM_DATABASE=Guan Show Technologe Co., Ltd. +OUI:8C1F644D6* + ID_OUI_FROM_DATABASE=Dan Smith LLC + OUI:8C1F644DA* ID_OUI_FROM_DATABASE=DTDS Technology Pte Ltd @@ -88538,6 +89879,12 @@ OUI:8C1F644DB* OUI:8C1F644DD* ID_OUI_FROM_DATABASE=Griffyn Robotech Private Limited +OUI:8C1F644E0* + ID_OUI_FROM_DATABASE=PuS GmbH und Co. KG + +OUI:8C1F644E5* + ID_OUI_FROM_DATABASE=Renukas Castle Hard- and Software + OUI:8C1F644EC* ID_OUI_FROM_DATABASE=XOR UK Corporation Limited @@ -88547,6 +89894,9 @@ OUI:8C1F644F0* OUI:8C1F644FA* ID_OUI_FROM_DATABASE=Sanskruti +OUI:8C1F64504* + ID_OUI_FROM_DATABASE=EA Elektroautomatik GmbH & Co. KG + OUI:8C1F6450A* ID_OUI_FROM_DATABASE=BELLCO TRADING COMPANY (PVT) LTD @@ -88562,6 +89912,12 @@ OUI:8C1F64517* OUI:8C1F64521* ID_OUI_FROM_DATABASE=MP-SENSOR GmbH +OUI:8C1F64525* + ID_OUI_FROM_DATABASE=United States Technologies Inc. + +OUI:8C1F6452D* + ID_OUI_FROM_DATABASE=Cubic ITS, Inc. dba GRIDSMART Technologies + OUI:8C1F64534* ID_OUI_FROM_DATABASE=SURYA ELECTRONICS @@ -88574,6 +89930,9 @@ OUI:8C1F64536* OUI:8C1F6453A* ID_OUI_FROM_DATABASE=TPVision Europe B.V +OUI:8C1F6453B* + ID_OUI_FROM_DATABASE=REFU Storage System GmbH + OUI:8C1F6453D* ID_OUI_FROM_DATABASE=NEXCONTECH @@ -88589,6 +89948,15 @@ OUI:8C1F64549* OUI:8C1F6454C* ID_OUI_FROM_DATABASE=Gemini Electronics B.V. +OUI:8C1F6454F* + ID_OUI_FROM_DATABASE=Toolplanet Co., Ltd. + +OUI:8C1F64552* + ID_OUI_FROM_DATABASE=Proterra, Inc + +OUI:8C1F64557* + ID_OUI_FROM_DATABASE=In-lite Design BV + OUI:8C1F6455E* ID_OUI_FROM_DATABASE=HANATEKSYSTEM @@ -88607,9 +89975,18 @@ OUI:8C1F6457A* OUI:8C1F6457B* ID_OUI_FROM_DATABASE=Potter Electric Signal Company +OUI:8C1F64581* + ID_OUI_FROM_DATABASE=SpectraDynamics, Inc. + +OUI:8C1F6459F* + ID_OUI_FROM_DATABASE=Delta Computers LLC. + OUI:8C1F645AE* ID_OUI_FROM_DATABASE=Suzhou Motorcomm Electronic Technology Co., Ltd +OUI:8C1F645B3* + ID_OUI_FROM_DATABASE=eumig industrie-TV GmbH. + OUI:8C1F645BC* ID_OUI_FROM_DATABASE=HEITEC AG @@ -88640,6 +90017,12 @@ OUI:8C1F64619* OUI:8C1F6461F* ID_OUI_FROM_DATABASE=Lightworks GmbH +OUI:8C1F64622* + ID_OUI_FROM_DATABASE=Logical Product + +OUI:8C1F64634* + ID_OUI_FROM_DATABASE=AML + OUI:8C1F64638* ID_OUI_FROM_DATABASE=THUNDER DATA TAIWAN CO., LTD. @@ -88664,15 +90047,27 @@ OUI:8C1F64656* OUI:8C1F6465F* ID_OUI_FROM_DATABASE=Astrometric Instruments, Inc. +OUI:8C1F64660* + ID_OUI_FROM_DATABASE=LLC NTPC + OUI:8C1F64663* ID_OUI_FROM_DATABASE=mal-tech Technological Solutions Ltd/CRISP OUI:8C1F6466C* ID_OUI_FROM_DATABASE=LINEAGE POWER PVT LTD., +OUI:8C1F64672* + ID_OUI_FROM_DATABASE=Farmobile LLC + OUI:8C1F64675* ID_OUI_FROM_DATABASE=Transit Solutions, LLC. +OUI:8C1F6467A* + ID_OUI_FROM_DATABASE=MG s.r.l. + +OUI:8C1F64697* + ID_OUI_FROM_DATABASE=Sontay Ltd. + OUI:8C1F6469E* ID_OUI_FROM_DATABASE=AT-Automation Technology GmbH @@ -88709,9 +90104,18 @@ OUI:8C1F646EA* OUI:8C1F646F4* ID_OUI_FROM_DATABASE=Elsist Srl +OUI:8C1F646F9* + ID_OUI_FROM_DATABASE=ANDDORO LLC + OUI:8C1F646FC* ID_OUI_FROM_DATABASE=HM Systems A/S +OUI:8C1F64702* + ID_OUI_FROM_DATABASE=AIDirections + +OUI:8C1F64703* + ID_OUI_FROM_DATABASE=Calnex Solutions plc + OUI:8C1F64707* ID_OUI_FROM_DATABASE=OAS AG @@ -88724,6 +90128,9 @@ OUI:8C1F6470E* OUI:8C1F64712* ID_OUI_FROM_DATABASE=Nexion Data Systems P/L +OUI:8C1F64721* + ID_OUI_FROM_DATABASE=M/S MILIND RAMACHANDRA RAJWADE + OUI:8C1F64726* ID_OUI_FROM_DATABASE=DAVE SRL @@ -88733,6 +90140,9 @@ OUI:8C1F6472A* OUI:8C1F6472C* ID_OUI_FROM_DATABASE=Antai technology Co.,Ltd +OUI:8C1F6473C* + ID_OUI_FROM_DATABASE=REO AG + OUI:8C1F6473D* ID_OUI_FROM_DATABASE=NewAgeMicro @@ -88745,12 +90155,18 @@ OUI:8C1F64747* OUI:8C1F6475F* ID_OUI_FROM_DATABASE=ASTRACOM Co. Ltd +OUI:8C1F64765* + ID_OUI_FROM_DATABASE=Micro Electroninc Products + OUI:8C1F64768* ID_OUI_FROM_DATABASE=mapna group OUI:8C1F64774* ID_OUI_FROM_DATABASE=navXperience GmbH +OUI:8C1F64775* + ID_OUI_FROM_DATABASE=Becton Dickinson + OUI:8C1F6477C* ID_OUI_FROM_DATABASE=Orange Tree Technologies Ltd @@ -88778,6 +90194,15 @@ OUI:8C1F647A1* OUI:8C1F647A6* ID_OUI_FROM_DATABASE=OTMetric +OUI:8C1F647A7* + ID_OUI_FROM_DATABASE=Timegate Instruments Ltd. + +OUI:8C1F647AA* + ID_OUI_FROM_DATABASE=XSENSOR Technology Corp. + +OUI:8C1F647B7* + ID_OUI_FROM_DATABASE=Weidmann Tecnologia Electrica de Mexico + OUI:8C1F647B8* ID_OUI_FROM_DATABASE=TimeMachines Inc. @@ -88790,12 +90215,18 @@ OUI:8C1F647C8* OUI:8C1F647D2* ID_OUI_FROM_DATABASE=Enlaps +OUI:8C1F647D3* + ID_OUI_FROM_DATABASE=Suntech Engineering + OUI:8C1F647D6* ID_OUI_FROM_DATABASE=Algodue Elettronica Srl OUI:8C1F647DD* ID_OUI_FROM_DATABASE=TAKASAKI KYODO COMPUTING CENTER Co.,LTD. +OUI:8C1F647DE* + ID_OUI_FROM_DATABASE=SOCNOC AI Inc + OUI:8C1F647EC* ID_OUI_FROM_DATABASE=Methods2Business B.V. @@ -88805,12 +90236,18 @@ OUI:8C1F647F1* OUI:8C1F64801* ID_OUI_FROM_DATABASE=Zhejiang Laolan Information Technology Co., Ltd +OUI:8C1F64807* + ID_OUI_FROM_DATABASE=GIORDANO CONTROLS SPA + OUI:8C1F6481A* ID_OUI_FROM_DATABASE=Gemini Electronics B.V. OUI:8C1F64820* ID_OUI_FROM_DATABASE=TIAMA +OUI:8C1F64837* + ID_OUI_FROM_DATABASE=Rumble, Inc + OUI:8C1F6483A* ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG @@ -88820,9 +90257,15 @@ OUI:8C1F6483C* OUI:8C1F64848* ID_OUI_FROM_DATABASE=Jena-Optronik GmbH +OUI:8C1F6484C* + ID_OUI_FROM_DATABASE=AvMap srlu + OUI:8C1F6484E* ID_OUI_FROM_DATABASE=West Pharmaceutical Services, Inc. +OUI:8C1F64855* + ID_OUI_FROM_DATABASE=e.kundenservice Netz GmbH + OUI:8C1F64856* ID_OUI_FROM_DATABASE=Garten Automation @@ -88832,12 +90275,18 @@ OUI:8C1F6485B* OUI:8C1F64878* ID_OUI_FROM_DATABASE=Green Access Ltd +OUI:8C1F64883* + ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH + OUI:8C1F6488D* ID_OUI_FROM_DATABASE=Pantherun Technologies Pvt Ltd OUI:8C1F64892* ID_OUI_FROM_DATABASE=MDI Industrial +OUI:8C1F6489E* + ID_OUI_FROM_DATABASE=Cinetix Srl + OUI:8C1F648A4* ID_OUI_FROM_DATABASE=Genesis Technologies AG @@ -88865,27 +90314,42 @@ OUI:8C1F648C2* OUI:8C1F648C4* ID_OUI_FROM_DATABASE=Hermes Network Inc +OUI:8C1F648CF* + ID_OUI_FROM_DATABASE=Diffraction Limited + OUI:8C1F648D1* ID_OUI_FROM_DATABASE=Orlaco Products B.V. OUI:8C1F648D4* ID_OUI_FROM_DATABASE=Recab Sweden AB +OUI:8C1F648D9* + ID_OUI_FROM_DATABASE=Pietro Fiorentini Spa + OUI:8C1F648E2* ID_OUI_FROM_DATABASE=ALPHA Corporation +OUI:8C1F648E9* + ID_OUI_FROM_DATABASE=Vesperix Corporation + OUI:8C1F648EE* ID_OUI_FROM_DATABASE=Abbott Diagnostics Technologies AS OUI:8C1F64903* ID_OUI_FROM_DATABASE=Portrait Displays, Inc. +OUI:8C1F64905* + ID_OUI_FROM_DATABASE=Qualitrol LLC + OUI:8C1F64909* ID_OUI_FROM_DATABASE=MATELEX OUI:8C1F6490E* ID_OUI_FROM_DATABASE=Xacti Corporation +OUI:8C1F64911* + ID_OUI_FROM_DATABASE=EOLANE + OUI:8C1F64918* ID_OUI_FROM_DATABASE=Abbott Diagnostics Technologies AS @@ -88898,15 +90362,30 @@ OUI:8C1F6492A* OUI:8C1F6492D* ID_OUI_FROM_DATABASE=IVOR Intelligent Electrical Appliance Co., Ltd +OUI:8C1F64939* + ID_OUI_FROM_DATABASE=SPIT Technology, Inc + +OUI:8C1F64943* + ID_OUI_FROM_DATABASE=Autark GmbH + OUI:8C1F64947* ID_OUI_FROM_DATABASE=LLC TC Vympel +OUI:8C1F6494E* + ID_OUI_FROM_DATABASE=Monnit Corporation + OUI:8C1F64956* ID_OUI_FROM_DATABASE=Paulmann Licht GmbH +OUI:8C1F64958* + ID_OUI_FROM_DATABASE=Sanchar Telesystems limited + OUI:8C1F6495A* ID_OUI_FROM_DATABASE=Shenzhen Longyun Lighting Electric Appliances Co., Ltd +OUI:8C1F64967* + ID_OUI_FROM_DATABASE=DAVE SRL + OUI:8C1F64971* ID_OUI_FROM_DATABASE=INFRASAFE/ ADVANTOR SYSTEMS @@ -88925,6 +90404,12 @@ OUI:8C1F64998* OUI:8C1F649A6* ID_OUI_FROM_DATABASE=INSTITUTO DE GESTÃO, REDES TECNOLÓGICAS E NERGIAS +OUI:8C1F649BA* + ID_OUI_FROM_DATABASE=WINTUS SYSTEM + +OUI:8C1F649BD* + ID_OUI_FROM_DATABASE=ATM SOLUTIONS + OUI:8C1F649C1* ID_OUI_FROM_DATABASE=RealWear @@ -88937,6 +90422,9 @@ OUI:8C1F649CE* OUI:8C1F649CF* ID_OUI_FROM_DATABASE=ASAP Electronics GmbH +OUI:8C1F649D4* + ID_OUI_FROM_DATABASE=Wolfspyre Labs + OUI:8C1F649D8* ID_OUI_FROM_DATABASE=Integer.pl S.A. @@ -88946,6 +90434,9 @@ OUI:8C1F649F0* OUI:8C1F649F2* ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme +OUI:8C1F649FA* + ID_OUI_FROM_DATABASE=METRONA-Union GmbH + OUI:8C1F649FD* ID_OUI_FROM_DATABASE=Vishay Nobel AB @@ -88958,6 +90449,9 @@ OUI:8C1F64A01* OUI:8C1F64A07* ID_OUI_FROM_DATABASE=GJD Manufacturing +OUI:8C1F64A1B* + ID_OUI_FROM_DATABASE=Zilica Limited + OUI:8C1F64A29* ID_OUI_FROM_DATABASE=Ringtail Security @@ -88970,8 +90464,14 @@ OUI:8C1F64A32* OUI:8C1F64A38* ID_OUI_FROM_DATABASE=NuGrid Power +OUI:8C1F64A42* + ID_OUI_FROM_DATABASE=Rodgers Instruments US LLC + +OUI:8C1F64A44* + ID_OUI_FROM_DATABASE=Rapidev Pvt Ltd + OUI:8C1F64A4C* - ID_OUI_FROM_DATABASE=Flextronics International Kft. + ID_OUI_FROM_DATABASE=Flextronics International Kft OUI:8C1F64A4E* ID_OUI_FROM_DATABASE=Syscom Instruments SA @@ -88982,12 +90482,27 @@ OUI:8C1F64A57* OUI:8C1F64A5C* ID_OUI_FROM_DATABASE=Prosys +OUI:8C1F64A5D* + ID_OUI_FROM_DATABASE=Shenzhen zhushida Technology lnformation Co.,Ltd + +OUI:8C1F64A6A* + ID_OUI_FROM_DATABASE=Sphere Com Services Pvt Ltd + +OUI:8C1F64A6D* + ID_OUI_FROM_DATABASE=CyberneX Co., Ltd + OUI:8C1F64A76* ID_OUI_FROM_DATABASE=DEUTA-WERKE GmbH +OUI:8C1F64A84* + ID_OUI_FROM_DATABASE=Beijing Wenrise Technology Co., Ltd. + OUI:8C1F64A94* ID_OUI_FROM_DATABASE=Future wave ultra tech Company +OUI:8C1F64A97* + ID_OUI_FROM_DATABASE=Integer.pl S.A. + OUI:8C1F64A9A* ID_OUI_FROM_DATABASE=Signasystems Elektronik San. ve Tic. Ltd. Sti. @@ -88997,9 +90512,18 @@ OUI:8C1F64AA4* OUI:8C1F64AAB* ID_OUI_FROM_DATABASE=BlueSword Intelligent Technology Co., Ltd. +OUI:8C1F64AB4* + ID_OUI_FROM_DATABASE=Beijing Zhongchen Microelectronics Co.,Ltd + OUI:8C1F64AB5* ID_OUI_FROM_DATABASE=JUSTMORPH PTE. LTD. +OUI:8C1F64AC0* + ID_OUI_FROM_DATABASE=AIQuatro + +OUI:8C1F64AC5* + ID_OUI_FROM_DATABASE=Forever Engineering Systems Pvt. Ltd. + OUI:8C1F64ACE* ID_OUI_FROM_DATABASE=Rayhaan Networks @@ -89009,6 +90533,9 @@ OUI:8C1F64AD2* OUI:8C1F64AE1* ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd +OUI:8C1F64AE8* + ID_OUI_FROM_DATABASE=ADETEC SAS + OUI:8C1F64AED* ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme @@ -89018,6 +90545,9 @@ OUI:8C1F64AEF* OUI:8C1F64AF7* ID_OUI_FROM_DATABASE=ard sa +OUI:8C1F64B01* + ID_OUI_FROM_DATABASE=noah + OUI:8C1F64B03* ID_OUI_FROM_DATABASE=Shenzhen Pisoftware Technology Co.,Ltd. @@ -89033,6 +90563,9 @@ OUI:8C1F64B22* OUI:8C1F64B2C* ID_OUI_FROM_DATABASE=SANMINA ISRAEL MEDICAL SYSTEMS LTD +OUI:8C1F64B3B* + ID_OUI_FROM_DATABASE=Sicon srl + OUI:8C1F64B3D* ID_OUI_FROM_DATABASE=RealD, Inc. @@ -89051,6 +90584,12 @@ OUI:8C1F64B64* OUI:8C1F64B77* ID_OUI_FROM_DATABASE=Carestream Dental LLC +OUI:8C1F64B7B* + ID_OUI_FROM_DATABASE=Gateview Technologies + +OUI:8C1F64B7C* + ID_OUI_FROM_DATABASE=EVERNET CO,.LTD TAIWAN + OUI:8C1F64B82* ID_OUI_FROM_DATABASE=Seed Core Co., LTD. @@ -89060,6 +90599,9 @@ OUI:8C1F64B84* OUI:8C1F64B8D* ID_OUI_FROM_DATABASE=Tongye lnnovation Science and Technology (Shenzhen) Co.,Ltd +OUI:8C1F64B92* + ID_OUI_FROM_DATABASE=Neurable + OUI:8C1F64B97* ID_OUI_FROM_DATABASE=Gemini Electronics B.V. @@ -89072,18 +90614,33 @@ OUI:8C1F64BA3* OUI:8C1F64BC0* ID_OUI_FROM_DATABASE=GS Elektromedizinsiche Geräte G. Stemple GmbH +OUI:8C1F64BC2* + ID_OUI_FROM_DATABASE=Huz Electronics Ltd + OUI:8C1F64BC6* ID_OUI_FROM_DATABASE=Chengdu ZiChen Time&Frequency Technology Co.,Ltd +OUI:8C1F64BD3* + ID_OUI_FROM_DATABASE=IO Master Technology + +OUI:8C1F64BD6* + ID_OUI_FROM_DATABASE=NOVA Products GmbH + OUI:8C1F64BD7* ID_OUI_FROM_DATABASE=Union Electronic. OUI:8C1F64BEE* ID_OUI_FROM_DATABASE=Sirius LLC +OUI:8C1F64BF0* + ID_OUI_FROM_DATABASE=Newtec A/S + OUI:8C1F64BF4* ID_OUI_FROM_DATABASE=Fluid Components Intl +OUI:8C1F64BFB* + ID_OUI_FROM_DATABASE=TechArgos + OUI:8C1F64C01* ID_OUI_FROM_DATABASE=HORIBA ABX SAS @@ -89096,6 +90653,9 @@ OUI:8C1F64C0C* OUI:8C1F64C1F* ID_OUI_FROM_DATABASE=Esys Srl +OUI:8C1F64C24* + ID_OUI_FROM_DATABASE=Alifax S.r.l. + OUI:8C1F64C27* ID_OUI_FROM_DATABASE=Lift Ventures, Inc @@ -89105,21 +90665,39 @@ OUI:8C1F64C28* OUI:8C1F64C2F* ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L. +OUI:8C1F64C38* + ID_OUI_FROM_DATABASE=ECO-ADAPT + OUI:8C1F64C40* ID_OUI_FROM_DATABASE=Sciospec Scientific Instruments GmbH OUI:8C1F64C41* ID_OUI_FROM_DATABASE=Katronic AG & Co. KG +OUI:8C1F64C4C* + ID_OUI_FROM_DATABASE=Lumiplan Duhamel + OUI:8C1F64C50* ID_OUI_FROM_DATABASE=Spacee OUI:8C1F64C54* ID_OUI_FROM_DATABASE=First Mode +OUI:8C1F64C68* + ID_OUI_FROM_DATABASE=FIBERME COMMUNICATIONS LLC + +OUI:8C1F64C6B* + ID_OUI_FROM_DATABASE=Mediana + OUI:8C1F64C7C* ID_OUI_FROM_DATABASE=MERKLE Schweissanlagen-Technik GmbH +OUI:8C1F64C80* + ID_OUI_FROM_DATABASE=VECOS Europe B.V. + +OUI:8C1F64C8F* + ID_OUI_FROM_DATABASE=JW Froehlich Maschinenfabrik GmbH + OUI:8C1F64C97* ID_OUI_FROM_DATABASE=Magnet-Physik Dr. Steingroever GmbH @@ -89135,12 +90713,18 @@ OUI:8C1F64CAD* OUI:8C1F64CBE* ID_OUI_FROM_DATABASE=Circa Enterprises Inc +OUI:8C1F64CCB* + ID_OUI_FROM_DATABASE=suzhou yuecrown Electronic Technology Co.,LTD + OUI:8C1F64CD6* ID_OUI_FROM_DATABASE=USM Pty Ltd OUI:8C1F64CD8* ID_OUI_FROM_DATABASE=Gogo Business Aviation +OUI:8C1F64CD9* + ID_OUI_FROM_DATABASE=Fingoti Limited + OUI:8C1F64CDF* ID_OUI_FROM_DATABASE=Canway Technology GmbH @@ -89159,6 +90743,12 @@ OUI:8C1F64CF1* OUI:8C1F64CF3* ID_OUI_FROM_DATABASE=ABB S.p.A. +OUI:8C1F64D02* + ID_OUI_FROM_DATABASE=Flextronics International Kft + +OUI:8C1F64D08* + ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L. + OUI:8C1F64D0E* ID_OUI_FROM_DATABASE=Labforge Inc. @@ -89189,12 +90779,24 @@ OUI:8C1F64D54* OUI:8C1F64D56* ID_OUI_FROM_DATABASE=Wisdom Audio +OUI:8C1F64D69* + ID_OUI_FROM_DATABASE=ADiCo Corporation + OUI:8C1F64D78* ID_OUI_FROM_DATABASE=Hunan Oushi Electronic Technology Co.,Ltd +OUI:8C1F64D7C* + ID_OUI_FROM_DATABASE=QUERCUS TECHNOLOGIES, S.L. + OUI:8C1F64D7E* ID_OUI_FROM_DATABASE=Thales Belgium +OUI:8C1F64D92* + ID_OUI_FROM_DATABASE=Mitsubishi Electric India Pvt. Ltd. + +OUI:8C1F64D9A* + ID_OUI_FROM_DATABASE=Beijing Redlink Information Technology Co., Ltd. + OUI:8C1F64DAA* ID_OUI_FROM_DATABASE=Davetech Limited @@ -89210,21 +90812,36 @@ OUI:8C1F64DB9* OUI:8C1F64DBD* ID_OUI_FROM_DATABASE=GIORDANO CONTROLS SPA +OUI:8C1F64DC0* + ID_OUI_FROM_DATABASE=Pigs Can Fly Labs LLC + OUI:8C1F64DC9* ID_OUI_FROM_DATABASE=Peter Huber Kaeltemaschinenbau AG OUI:8C1F64DCA* ID_OUI_FROM_DATABASE=Porsche engineering +OUI:8C1F64DD5* + ID_OUI_FROM_DATABASE=Cardinal Scales Manufacturing Co + OUI:8C1F64DE1* ID_OUI_FROM_DATABASE=Franke Aquarotter GmbH +OUI:8C1F64DF8* + ID_OUI_FROM_DATABASE=Wittra Networks AB + OUI:8C1F64E02* ID_OUI_FROM_DATABASE=ITS Teknik A/S +OUI:8C1F64E0E* + ID_OUI_FROM_DATABASE=Nokeval Oy + OUI:8C1F64E21* ID_OUI_FROM_DATABASE=LG-LHT Aircraft Solutions GmbH +OUI:8C1F64E30* + ID_OUI_FROM_DATABASE=VMukti Solutions Private Limited + OUI:8C1F64E41* ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG @@ -89234,6 +90851,9 @@ OUI:8C1F64E43* OUI:8C1F64E49* ID_OUI_FROM_DATABASE=Samwell International Inc +OUI:8C1F64E4C* + ID_OUI_FROM_DATABASE=TTC TELEKOMUNIKACE, s.r.o. + OUI:8C1F64E52* ID_OUI_FROM_DATABASE=LcmVeloci ApS @@ -89243,9 +90863,15 @@ OUI:8C1F64E5C* OUI:8C1F64E5D* ID_OUI_FROM_DATABASE=JinYuan International Corporation +OUI:8C1F64E5E* + ID_OUI_FROM_DATABASE=BRICKMAKERS GmbH + OUI:8C1F64E61* ID_OUI_FROM_DATABASE=Stange Elektronik GmbH +OUI:8C1F64E64* + ID_OUI_FROM_DATABASE=Indefac company + OUI:8C1F64E73* ID_OUI_FROM_DATABASE=GTR Industries @@ -89255,6 +90881,9 @@ OUI:8C1F64E77* OUI:8C1F64E7B* ID_OUI_FROM_DATABASE=Dongguan Pengchen Earth Instrument CO. LT +OUI:8C1F64E7C* + ID_OUI_FROM_DATABASE=Ashinne Technology Co., Ltd + OUI:8C1F64E98* ID_OUI_FROM_DATABASE=Luxshare Electronic Technology (Kunshan) LTD @@ -89270,6 +90899,9 @@ OUI:8C1F64EAC* OUI:8C1F64EB2* ID_OUI_FROM_DATABASE=Aqua Broadcast Ltd +OUI:8C1F64EB5* + ID_OUI_FROM_DATABASE=Meiryo Denshi Corp. + OUI:8C1F64EB7* ID_OUI_FROM_DATABASE=Delta Solutions LLC @@ -89285,9 +90917,15 @@ OUI:8C1F64EC1* OUI:8C1F64ED4* ID_OUI_FROM_DATABASE=ZHEJIANG CHITIC-SAFEWAY NEW ENERGY TECHNICAL CO.,LTD. +OUI:8C1F64ED9* + ID_OUI_FROM_DATABASE=NETGEN HITECH SOLUTIONS LLP + OUI:8C1F64EE8* ID_OUI_FROM_DATABASE=Global Organ Group B.V. +OUI:8C1F64EEA* + ID_OUI_FROM_DATABASE=AMESS + OUI:8C1F64EEF* ID_OUI_FROM_DATABASE=AiUnion Co.,Ltd @@ -89303,18 +90941,36 @@ OUI:8C1F64F04* OUI:8C1F64F25* ID_OUI_FROM_DATABASE=Misaka Network, Inc. +OUI:8C1F64F27* + ID_OUI_FROM_DATABASE=Tesat-Spacecom GmbH & Co. KG + +OUI:8C1F64F2C* + ID_OUI_FROM_DATABASE=Tunstall A/S + OUI:8C1F64F31* ID_OUI_FROM_DATABASE=International Water Treatment Maritime AS OUI:8C1F64F32* ID_OUI_FROM_DATABASE=Shenzhen INVT Electric Co.,Ltd +OUI:8C1F64F3C* + ID_OUI_FROM_DATABASE=Microlynx Systems Ltd + OUI:8C1F64F3F* ID_OUI_FROM_DATABASE=Industrial Laser Machines, LLC OUI:8C1F64F41* ID_OUI_FROM_DATABASE=AUTOMATIZACION Y CONECTIVIDAD SA DE CV +OUI:8C1F64F45* + ID_OUI_FROM_DATABASE=JBF + +OUI:8C1F64F4E* + ID_OUI_FROM_DATABASE=ADAMCZEWSKI elektronische Messtechnik GmbH + +OUI:8C1F64F52* + ID_OUI_FROM_DATABASE=AMF Medical SA + OUI:8C1F64F59* ID_OUI_FROM_DATABASE=Inovonics Inc. @@ -89322,7 +90978,7 @@ OUI:8C1F64F5A* ID_OUI_FROM_DATABASE=Telco Antennas Pty Ltd OUI:8C1F64F5C* - ID_OUI_FROM_DATABASE=Flextronics International Kft. + ID_OUI_FROM_DATABASE=Flextronics International Kft OUI:8C1F64F72* ID_OUI_FROM_DATABASE=Contrader @@ -89333,6 +90989,9 @@ OUI:8C1F64F74* OUI:8C1F64F78* ID_OUI_FROM_DATABASE=Ternary Research Corporation +OUI:8C1F64F7A* + ID_OUI_FROM_DATABASE=SiEngine Technology Co., Ltd. + OUI:8C1F64F86* ID_OUI_FROM_DATABASE=INFOSTECH Co., Ltd. @@ -89345,12 +91004,21 @@ OUI:8C1F64F96* OUI:8C1F64F9E* ID_OUI_FROM_DATABASE=DREAMSWELL Technology CO.,Ltd +OUI:8C1F64FA2* + ID_OUI_FROM_DATABASE=AZD Praha s.r.o., ZOZ Olomouc + OUI:8C1F64FB0* ID_OUI_FROM_DATABASE=MARIAN GmbH OUI:8C1F64FB1* ID_OUI_FROM_DATABASE=ABB +OUI:8C1F64FB7* + ID_OUI_FROM_DATABASE=Grace Design/Lunatec LLC + +OUI:8C1F64FBA* + ID_OUI_FROM_DATABASE=Onto Innovation + OUI:8C1F64FBD* ID_OUI_FROM_DATABASE=SAN-AI Electronic Industries Co.,Ltd. @@ -89363,12 +91031,24 @@ OUI:8C1F64FD1* OUI:8C1F64FD3* ID_OUI_FROM_DATABASE=SMILICS TECHNOLOGIES, S.L. +OUI:8C1F64FD4* + ID_OUI_FROM_DATABASE=EMBSYS SISTEMAS EMBARCADOS + OUI:8C1F64FE0* ID_OUI_FROM_DATABASE=Potter Electric Signal Company OUI:8C1F64FE3* ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L. +OUI:8C1F64FED* + ID_OUI_FROM_DATABASE=GSP Sprachtechnologie GmbH + +OUI:8C1F64FF4* + ID_OUI_FROM_DATABASE=SMS group GmbH + +OUI:8C1F64FF6* + ID_OUI_FROM_DATABASE=Ascon Tecnologic S.r.l. + OUI:8C1F94* ID_OUI_FROM_DATABASE=RF Surgical System Inc. @@ -89525,6 +91205,9 @@ OUI:8C4962* OUI:8C497A* ID_OUI_FROM_DATABASE=Extreme Networks, Inc. +OUI:8C49B6* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + OUI:8C4AEE* ID_OUI_FROM_DATABASE=GIGA TMS INC @@ -89720,6 +91403,9 @@ OUI:8C6A8D* OUI:8C6AE4* ID_OUI_FROM_DATABASE=Viogem Limited +OUI:8C6BDB* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:8C6D50* ID_OUI_FROM_DATABASE=SHENZHEN MTC CO LTD @@ -89744,6 +91430,9 @@ OUI:8C736E* OUI:8C73A0* ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD +OUI:8C763F* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:8C76C1* ID_OUI_FROM_DATABASE=Goden Tech Limited @@ -89903,6 +91592,9 @@ OUI:8C965F* OUI:8C97EA* ID_OUI_FROM_DATABASE=FREEBOX SAS +OUI:8C9806* + ID_OUI_FROM_DATABASE=SHENZHEN SEI ROBOTICS CO.,LTD + OUI:8C99E6* ID_OUI_FROM_DATABASE=TCT mobile ltd @@ -90107,6 +91799,9 @@ OUI:8CC8F4D* OUI:8CC8F4E* ID_OUI_FROM_DATABASE=Evaporcool Solutions +OUI:8CCBDF* + ID_OUI_FROM_DATABASE=FOXCONN INTERCONNECT TECHNOLOGY + OUI:8CCDA2* ID_OUI_FROM_DATABASE=ACTP, Inc. @@ -90443,6 +92138,9 @@ OUI:902155* OUI:902181* ID_OUI_FROM_DATABASE=Shanghai Huaqin Telecom Technology Co.,Ltd +OUI:90235B* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + OUI:9023B4* ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd @@ -90467,6 +92165,9 @@ OUI:902BD2* OUI:902CC7* ID_OUI_FROM_DATABASE=C-MAX Asia Limited +OUI:902CFB* + ID_OUI_FROM_DATABASE=CanTops Co,.Ltd. + OUI:902E16* ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology co., ltd @@ -90560,9 +92261,15 @@ OUI:904716* OUI:90473C* ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd. +OUI:90486C* + ID_OUI_FROM_DATABASE=Ring LLC + OUI:90489A* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. +OUI:904992* + ID_OUI_FROM_DATABASE=YSTen Technology Co.,Ltd + OUI:9049FA* ID_OUI_FROM_DATABASE=Intel Corporate @@ -90674,6 +92381,9 @@ OUI:905C44* OUI:905D7C* ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd +OUI:905E44* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:905F2E* ID_OUI_FROM_DATABASE=TCT mobile ltd @@ -90770,6 +92480,9 @@ OUI:907910* OUI:907990* ID_OUI_FROM_DATABASE=Benchmark Electronics Romania SRL +OUI:9079CF* + ID_OUI_FROM_DATABASE=zte corporation + OUI:907A0A* ID_OUI_FROM_DATABASE=Gebr. Bode GmbH & Co KG @@ -90875,6 +92588,9 @@ OUI:909164* OUI:9092B4* ID_OUI_FROM_DATABASE=Diehl BGT Defence GmbH & Co. KG +OUI:90935A* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:90940A* ID_OUI_FROM_DATABASE=Analog Devices, Inc @@ -91109,6 +92825,9 @@ OUI:90CC24* OUI:90CCDF* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:90CD1F* + ID_OUI_FROM_DATABASE=Quectel Wireless Solutions Co.,Ltd. + OUI:90CDB6* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. @@ -91124,6 +92843,9 @@ OUI:90CF7D* OUI:90D11B* ID_OUI_FROM_DATABASE=Palomar Medical Technologies +OUI:90D473* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + OUI:90D74F* ID_OUI_FROM_DATABASE=Bookeen @@ -91157,6 +92879,9 @@ OUI:90DD5D* OUI:90DE80* ID_OUI_FROM_DATABASE=Shenzhen Century Xinyang Technology Co., Ltd +OUI:90DF7D* + ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd. + OUI:90DFB7* ID_OUI_FROM_DATABASE=s.m.s smart microwave sensors GmbH @@ -91292,6 +93017,9 @@ OUI:90F652* OUI:90F72F* ID_OUI_FROM_DATABASE=Phillips Machine & Welding Co., Inc. +OUI:90F7B2* + ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd + OUI:90F891* ID_OUI_FROM_DATABASE=Kaonmedia CO., LTD. @@ -91400,6 +93128,9 @@ OUI:9408C7* OUI:940937* ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. +OUI:9409C9* + ID_OUI_FROM_DATABASE=ALPSALPINE CO .,LTD + OUI:9409D3* ID_OUI_FROM_DATABASE=shenzhen maxtopic technology co.,ltd @@ -91886,6 +93617,9 @@ OUI:94AAB8* OUI:94ABDE* ID_OUI_FROM_DATABASE=OMX Technology - FZE +OUI:94ABFE* + ID_OUI_FROM_DATABASE=Nokia + OUI:94ACCA* ID_OUI_FROM_DATABASE=trivum technologies GmbH @@ -91982,6 +93716,9 @@ OUI:94C3E4* OUI:94C4E9* ID_OUI_FROM_DATABASE=PowerLayer Microsystems HongKong Limited +OUI:94C5A6* + ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED + OUI:94C691* ID_OUI_FROM_DATABASE=EliteGroup Computer Systems Co., LTD @@ -92120,6 +93857,9 @@ OUI:94D299* OUI:94D2BC* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:94D331* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + OUI:94D417* ID_OUI_FROM_DATABASE=GPI KOREA INC. @@ -92801,6 +94541,9 @@ OUI:985945* OUI:985949* ID_OUI_FROM_DATABASE=LUXOTTICA GROUP S.P.A. +OUI:98597A* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:985AEB* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -93185,6 +94928,9 @@ OUI:98A5F9* OUI:98A7B0* ID_OUI_FROM_DATABASE=MCST ZAO +OUI:98A92D* + ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd + OUI:98A942* ID_OUI_FROM_DATABASE=Guangzhou Tozed Kangwei Intelligent Technology Co., LTD @@ -93308,6 +95054,9 @@ OUI:98C5DB* OUI:98C7A4* ID_OUI_FROM_DATABASE=Shenzhen HS Fiber Communication Equipment CO., LTD +OUI:98C81C* + ID_OUI_FROM_DATABASE=BAYTEC LIMITED + OUI:98C845* ID_OUI_FROM_DATABASE=PacketAccess @@ -93371,6 +95120,9 @@ OUI:98D863* OUI:98D88C* ID_OUI_FROM_DATABASE=Nortel Networks +OUI:98D93D* + ID_OUI_FROM_DATABASE=Demant Enterprise A/S + OUI:98DA92* ID_OUI_FROM_DATABASE=Vuzix Corporation @@ -93449,6 +95201,9 @@ OUI:98F083* OUI:98F0AB* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:98F112* + ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. + OUI:98F170* ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. @@ -93689,6 +95444,9 @@ OUI:9C1E95* OUI:9C1EA4* ID_OUI_FROM_DATABASE=Renesas Electronics (Penang) Sdn. Bhd. +OUI:9C1FCA* + ID_OUI_FROM_DATABASE=Hangzhou AlmightyDigit Technology Co., Ltd + OUI:9C1FDD* ID_OUI_FROM_DATABASE=Accupix Inc. @@ -93698,6 +95456,9 @@ OUI:9C207B* OUI:9C216A* ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. +OUI:9C2183* + ID_OUI_FROM_DATABASE=Broadcom Limited + OUI:9C220E* ID_OUI_FROM_DATABASE=TASCAN Systems GmbH @@ -93852,7 +95613,7 @@ OUI:9C431ED* ID_OUI_FROM_DATABASE=HK ELEPHONE Communication Tech Co.,Limited OUI:9C431EE* - ID_OUI_FROM_DATABASE=Phoenix Audio Technologies + ID_OUI_FROM_DATABASE=SHURE INCORPORATED OUI:9C443D* ID_OUI_FROM_DATABASE=CHENGDU XUGUANG TECHNOLOGY CO, LTD @@ -93888,7 +95649,7 @@ OUI:9C4EBF* ID_OUI_FROM_DATABASE=BoxCast OUI:9C4F5F* - ID_OUI_FROM_DATABASE=TAP Sound System + ID_OUI_FROM_DATABASE=Google, Inc. OUI:9C4FCF* ID_OUI_FROM_DATABASE=TCT mobile ltd @@ -93935,6 +95696,9 @@ OUI:9C5711* OUI:9C57AD* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:9C57BC* + ID_OUI_FROM_DATABASE=eero inc. + OUI:9C583C* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -94205,6 +95969,9 @@ OUI:9C93B0* OUI:9C93E4* ID_OUI_FROM_DATABASE=Private +OUI:9C9561* + ID_OUI_FROM_DATABASE=Hui Zhou Gaoshengda Technology Co.,LTD + OUI:9C9567* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. @@ -94253,6 +96020,9 @@ OUI:9CA10A* OUI:9CA134* ID_OUI_FROM_DATABASE=Nike, Inc. +OUI:9CA2F4* + ID_OUI_FROM_DATABASE=TP-Link Corporation Limited + OUI:9CA3A9* ID_OUI_FROM_DATABASE=Guangzhou Juan Optical and Electronical Tech Joint Stock Co., Ltd @@ -94346,6 +96116,9 @@ OUI:9CBD9D* OUI:9CBEE0* ID_OUI_FROM_DATABASE=Biosoundlab Co., Ltd. +OUI:9CBFCD* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:9CC077* ID_OUI_FROM_DATABASE=PrintCounts, LLC @@ -94439,6 +96212,9 @@ OUI:9CDF03* OUI:9CDFB1* ID_OUI_FROM_DATABASE=Shenzhen Crave Communication Co., LTD +OUI:9CE041* + ID_OUI_FROM_DATABASE=Nokia + OUI:9CE063* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -94898,6 +96674,9 @@ OUI:A028ED* OUI:A02919* ID_OUI_FROM_DATABASE=Dell Inc. +OUI:A02942* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:A029BD* ID_OUI_FROM_DATABASE=Team Group Inc @@ -95042,6 +96821,9 @@ OUI:A0423F* OUI:A04246* ID_OUI_FROM_DATABASE=IT Telecom Co., Ltd. +OUI:A042D1* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:A043B0* ID_OUI_FROM_DATABASE=Hangzhou BroadLink Technology Co.,Ltd @@ -95051,6 +96833,9 @@ OUI:A043DB* OUI:A0445C* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:A04466* + ID_OUI_FROM_DATABASE=Intellics + OUI:A047D7* ID_OUI_FROM_DATABASE=Best IT World (India) Pvt Ltd @@ -95459,6 +97244,9 @@ OUI:A0B5DA* OUI:A0B662* ID_OUI_FROM_DATABASE=Acutvista Innovation Co., Ltd. +OUI:A0B765* + ID_OUI_FROM_DATABASE=Espressif Inc. + OUI:A0B8F8* ID_OUI_FROM_DATABASE=Amgen U.S.A. Inc. @@ -95591,6 +97379,9 @@ OUI:A0C5F2E* OUI:A0C6EC* ID_OUI_FROM_DATABASE=ShenZhen ANYK Technology Co.,LTD +OUI:A0C98B* + ID_OUI_FROM_DATABASE=Nokia Solutions and Networks GmbH & Co. KG + OUI:A0C9A0* ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. @@ -95729,6 +97520,9 @@ OUI:A0ECF9* OUI:A0EDCD* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:A0EDFB* + ID_OUI_FROM_DATABASE=Quectel Wireless Solutions Co.,Ltd. + OUI:A0EF84* ID_OUI_FROM_DATABASE=Seine Image Int'l Co., Ltd @@ -95840,6 +97634,9 @@ OUI:A40DBC* OUI:A40E2B* ID_OUI_FROM_DATABASE=Facebook Inc +OUI:A40F98* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + OUI:A41115* ID_OUI_FROM_DATABASE=Robert Bosch Engineering and Business Solutions pvt. Ltd. @@ -95942,6 +97739,9 @@ OUI:A41B34* OUI:A41BC0* ID_OUI_FROM_DATABASE=Fastec Imaging Corporation +OUI:A41EE1* + ID_OUI_FROM_DATABASE=Taicang T&W Electronics + OUI:A41F72* ID_OUI_FROM_DATABASE=Dell Inc. @@ -96455,6 +98255,9 @@ OUI:A47E36* OUI:A47E39* ID_OUI_FROM_DATABASE=zte corporation +OUI:A47EFA* + ID_OUI_FROM_DATABASE=Withings + OUI:A4817A* ID_OUI_FROM_DATABASE=CIG SHANGHAI CO LTD @@ -96497,6 +98300,9 @@ OUI:A48E0A* OUI:A49005* ID_OUI_FROM_DATABASE=CHINA GREATWALL COMPUTER SHENZHEN CO.,LTD +OUI:A490CE* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + OUI:A491B1* ID_OUI_FROM_DATABASE=Technicolor Delivery Technologies Belgium NV @@ -96734,6 +98540,9 @@ OUI:A4CEDA* OUI:A4CF12* ID_OUI_FROM_DATABASE=Espressif Inc. +OUI:A4CF99* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:A4CFD2* ID_OUI_FROM_DATABASE=Ubee Interactive Co., Limited @@ -96845,6 +98654,9 @@ OUI:A4DB30* OUI:A4DCBE* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:A4DD58* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:A4DE26* ID_OUI_FROM_DATABASE=Sumitomo Electric Industries, Ltd @@ -96989,6 +98801,9 @@ OUI:A4F522* OUI:A4F7D0* ID_OUI_FROM_DATABASE=LAN Accessories Co., Ltd. +OUI:A4F933* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:A4F9E4* ID_OUI_FROM_DATABASE=AirVine Scientific, Inc. @@ -97274,6 +99089,9 @@ OUI:A85081* OUI:A8515B* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:A851AB* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:A8537D* ID_OUI_FROM_DATABASE=Mist Systems, Inc. @@ -97469,6 +99287,9 @@ OUI:A8776F* OUI:A877E5* ID_OUI_FROM_DATABASE=SHENZHEN CHUANGWEI-RGB ELECTRONICS CO.,LTD +OUI:A8798D* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:A87B39* ID_OUI_FROM_DATABASE=Nokia Corporation @@ -97625,6 +99446,9 @@ OUI:A8A159* OUI:A8A198* ID_OUI_FROM_DATABASE=TCT mobile ltd +OUI:A8A237* + ID_OUI_FROM_DATABASE=Arcadyan Corporation + OUI:A8A5E2* ID_OUI_FROM_DATABASE=MSF-Vathauer Antriebstechnik GmbH & Co KG @@ -97644,7 +99468,10 @@ OUI:A8B088* ID_OUI_FROM_DATABASE=eero inc. OUI:A8B0AE* - ID_OUI_FROM_DATABASE=LEONI + ID_OUI_FROM_DATABASE=BizLink Special Cables Germany GmbH + +OUI:A8B13B* + ID_OUI_FROM_DATABASE=HP Inc. OUI:A8B1D4* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -97709,6 +99536,9 @@ OUI:A8C83A* OUI:A8C87F* ID_OUI_FROM_DATABASE=Roqos, Inc. +OUI:A8C98A* + ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd + OUI:A8CA7B* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -97838,6 +99668,9 @@ OUI:A8F5DD* OUI:A8F766* ID_OUI_FROM_DATABASE=ITE Tech Inc +OUI:A8F7D9* + ID_OUI_FROM_DATABASE=Mist Systems, Inc. + OUI:A8F7E0* ID_OUI_FROM_DATABASE=PLANET Technology Corporation @@ -98054,12 +99887,18 @@ OUI:AC2334* OUI:AC233F* ID_OUI_FROM_DATABASE=Shenzhen Minew Technologies Co., Ltd. +OUI:AC2929* + ID_OUI_FROM_DATABASE=Infinix mobility limited + OUI:AC293A* ID_OUI_FROM_DATABASE=Apple, Inc. OUI:AC2A0C* ID_OUI_FROM_DATABASE=CSR ZHUZHOU INSTITUTE CO.,LTD. +OUI:AC2AA1* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:AC2B6E* ID_OUI_FROM_DATABASE=Intel Corporate @@ -98201,6 +100040,9 @@ OUI:AC512C* OUI:AC5135* ID_OUI_FROM_DATABASE=MPI TECH +OUI:AC51AB* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:AC51EE* ID_OUI_FROM_DATABASE=Cambridge Communication Systems Ltd @@ -98375,6 +100217,9 @@ OUI:AC6FD9* OUI:AC710C* ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd. +OUI:AC712E* + ID_OUI_FROM_DATABASE=Fortinet, Inc. + OUI:AC7236* ID_OUI_FROM_DATABASE=Lexking Technology Co., Ltd. @@ -98454,7 +100299,7 @@ OUI:AC83E9* ID_OUI_FROM_DATABASE=Beijing Zile Technology Co., Ltd OUI:AC83F0* - ID_OUI_FROM_DATABASE=ImmediaTV Corporation + ID_OUI_FROM_DATABASE=Cobalt Digital Inc. OUI:AC83F3* ID_OUI_FROM_DATABASE=AMPAK Technology, Inc. @@ -98501,6 +100346,9 @@ OUI:AC8D14* OUI:AC8D34* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:AC8FA9* + ID_OUI_FROM_DATABASE=Nokia Solutions and Networks GmbH & Co. KG + OUI:AC8FF8* ID_OUI_FROM_DATABASE=Nokia @@ -98558,6 +100406,9 @@ OUI:ACA22C* OUI:ACA31E* ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company +OUI:ACA32F* + ID_OUI_FROM_DATABASE=Solidigm Technology + OUI:ACA430* ID_OUI_FROM_DATABASE=Peerless AV @@ -98603,6 +100454,9 @@ OUI:ACB313* OUI:ACB3B5* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:ACB566* + ID_OUI_FROM_DATABASE=Renesas Electronics (Penang) Sdn. Bhd. + OUI:ACB57D* ID_OUI_FROM_DATABASE=Liteon Technology Corporation @@ -98636,6 +100490,9 @@ OUI:ACBE75* OUI:ACBEB6* ID_OUI_FROM_DATABASE=Visualedge Technology Co., Ltd. +OUI:ACBF71* + ID_OUI_FROM_DATABASE=Bose Corporation + OUI:ACC1EE* ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd @@ -98651,6 +100508,9 @@ OUI:ACC33A* OUI:ACC358* ID_OUI_FROM_DATABASE=Continental Automotive Czech Republic s.r.o. +OUI:ACC4BD* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + OUI:ACC51B* ID_OUI_FROM_DATABASE=Zhuhai Pantum Electronics Co., Ltd. @@ -98690,6 +100550,9 @@ OUI:ACCB51* OUI:ACCC8E* ID_OUI_FROM_DATABASE=Axis Communications AB +OUI:ACCCFC* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + OUI:ACCE8F* ID_OUI_FROM_DATABASE=HWA YAO TECHNOLOGIES CO., LTD @@ -98711,6 +100574,9 @@ OUI:ACD180* OUI:ACD1B8* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. +OUI:ACD31D* + ID_OUI_FROM_DATABASE=Cisco Meraki + OUI:ACD364* ID_OUI_FROM_DATABASE=ABB SPA, ABB SACE DIV. @@ -98990,9 +100856,15 @@ OUI:B01F81E* OUI:B01F81F* ID_OUI_FROM_DATABASE=Private +OUI:B01F8C* + ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company + OUI:B0227A* ID_OUI_FROM_DATABASE=HP Inc. +OUI:B02347* + ID_OUI_FROM_DATABASE=Shenzhen Giant Microelectronics Company Limited + OUI:B02491* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. @@ -99011,6 +100883,9 @@ OUI:B02680* OUI:B027CF* ID_OUI_FROM_DATABASE=Extreme Networks, Inc. +OUI:B0285B* + ID_OUI_FROM_DATABASE=JUHUA Technology Inc. + OUI:B02A1F* ID_OUI_FROM_DATABASE=Wingtech Group (HongKong)Limited @@ -99056,6 +100931,9 @@ OUI:B03850* OUI:B03893* ID_OUI_FROM_DATABASE=Onda TLC GmbH +OUI:B038E2* + ID_OUI_FROM_DATABASE=Wanan Hongsheng Electronic Co.Ltd + OUI:B03956* ID_OUI_FROM_DATABASE=NETGEAR @@ -99077,6 +100955,9 @@ OUI:B03E51* OUI:B03EB0* ID_OUI_FROM_DATABASE=MICRODIA Ltd. +OUI:B03F64* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:B04089* ID_OUI_FROM_DATABASE=Senient Systems LTD @@ -99128,6 +101009,9 @@ OUI:B0495F* OUI:B04A39* ID_OUI_FROM_DATABASE=Beijing Roborock Technology Co., Ltd. +OUI:B04A6A* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:B04BBF* ID_OUI_FROM_DATABASE=PT HAN SUNG ELECTORONICS INDONESIA @@ -99221,6 +101105,9 @@ OUI:B06A41* OUI:B06CBF* ID_OUI_FROM_DATABASE=3ality Digital Systems GmbH +OUI:B06E72* + ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd. + OUI:B06EBF* ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. @@ -99407,6 +101294,9 @@ OUI:B0A454* OUI:B0A460* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:B0A4F0* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:B0A651* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -99446,6 +101336,9 @@ OUI:B0ADAA* OUI:B0AE25* ID_OUI_FROM_DATABASE=Varikorea +OUI:B0AFF7* + ID_OUI_FROM_DATABASE=Shenzhen Yipingfang Network Technology Co., Ltd. + OUI:B0B113* ID_OUI_FROM_DATABASE=Texas Instruments @@ -99725,6 +101618,9 @@ OUI:B0E2E5* OUI:B0E39D* ID_OUI_FROM_DATABASE=CAT SYSTEM CO.,LTD. +OUI:B0E45C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:B0E4D5* ID_OUI_FROM_DATABASE=Google, Inc. @@ -99809,6 +101705,9 @@ OUI:B0F963* OUI:B0FAEB* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:B0FBDD* + ID_OUI_FROM_DATABASE=Shenzhen SuperElectron Technology Co.,Ltd. + OUI:B0FC0D* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. @@ -99947,9 +101846,15 @@ OUI:B4157E* OUI:B41780* ID_OUI_FROM_DATABASE=DTI Group Ltd +OUI:B417A8* + ID_OUI_FROM_DATABASE=Facebook Technologies, LLC + OUI:B418D1* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:B41974* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:B41A1D* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -100118,6 +102023,9 @@ OUI:B439D6* OUI:B43A28* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:B43AE2* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:B43D08* ID_OUI_FROM_DATABASE=GX International BV @@ -100277,6 +102185,9 @@ OUI:B467E9* OUI:B46921* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:B4695F* + ID_OUI_FROM_DATABASE=TCT mobile ltd + OUI:B46BFC* ID_OUI_FROM_DATABASE=Intel Corporate @@ -100295,6 +102206,9 @@ OUI:B46E08* OUI:B46F2D* ID_OUI_FROM_DATABASE=Wahoo Fitness +OUI:B47064* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:B47356* ID_OUI_FROM_DATABASE=Hangzhou Treebear Networking Co., Ltd. @@ -100334,6 +102248,9 @@ OUI:B47C59* OUI:B47C9C* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. +OUI:B47D76* + ID_OUI_FROM_DATABASE=KNS Group LLC + OUI:B47F5E* ID_OUI_FROM_DATABASE=Foresight Manufacture (S) Pte Ltd @@ -100355,6 +102272,9 @@ OUI:B482C5* OUI:B482FE* ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP +OUI:B48351* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:B48547* ID_OUI_FROM_DATABASE=Amptown System Company GmbH @@ -100418,6 +102338,9 @@ OUI:B49EAC* OUI:B49EE6* ID_OUI_FROM_DATABASE=SHENZHEN TECHNOLOGY CO LTD +OUI:B49F4D* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + OUI:B4A25C* ID_OUI_FROM_DATABASE=Cambium Networks Limited @@ -100487,6 +102410,12 @@ OUI:B4A5AC* OUI:B4A5EF* ID_OUI_FROM_DATABASE=Sercomm Corporation. +OUI:B4A678* + ID_OUI_FROM_DATABASE=Zhejiang Tmall Technology Co., Ltd. + +OUI:B4A7C6* + ID_OUI_FROM_DATABASE=SERVERCOM (INDIA) PRIVATE LIMITED + OUI:B4A828* ID_OUI_FROM_DATABASE=Shenzhen Concox Information Technology Co., Ltd @@ -100922,6 +102851,9 @@ OUI:B8208E* OUI:B820E7* ID_OUI_FROM_DATABASE=Guangzhou Horizontal Information & Network Integration Co. Ltd +OUI:B8211C* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:B8224F* ID_OUI_FROM_DATABASE=Sichuan Tianyi Comheart Telecom Co.,LTD @@ -101024,6 +102956,9 @@ OUI:B83D4E* OUI:B83E59* ID_OUI_FROM_DATABASE=Roku, Inc. +OUI:B83FD2* + ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc. + OUI:B8415F* ID_OUI_FROM_DATABASE=ASP AG @@ -101063,6 +102998,9 @@ OUI:B84FD5* OUI:B85001* ID_OUI_FROM_DATABASE=Extreme Networks, Inc. +OUI:B850D8* + ID_OUI_FROM_DATABASE=Beijing Xiaomi Mobile Software Co., Ltd + OUI:B853AC* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -101102,6 +103040,9 @@ OUI:B85AFE* OUI:B85D0A* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:B85DC3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:B85E7B* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -101198,6 +103139,9 @@ OUI:B87C6F* OUI:B87CF2* ID_OUI_FROM_DATABASE=Extreme Networks, Inc. +OUI:B87EE5* + ID_OUI_FROM_DATABASE=Intelbras + OUI:B88035* ID_OUI_FROM_DATABASE=Shenzhen Qihu Intelligent Technology Company Limited @@ -101348,6 +103292,9 @@ OUI:B89EA6* OUI:B89F09* ID_OUI_FROM_DATABASE=Wistron Neweb Corporation +OUI:B89FCC* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:B8A14A* ID_OUI_FROM_DATABASE=Raisecom Technology CO.,LTD @@ -101405,6 +103352,9 @@ OUI:B8B2F8* OUI:B8B3DC* ID_OUI_FROM_DATABASE=DEREK (SHAOGUAN) LIMITED +OUI:B8B409* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:B8B42E* ID_OUI_FROM_DATABASE=Gionee Communication Equipment Co,Ltd.ShenZhen @@ -101543,6 +103493,9 @@ OUI:B8D526* OUI:B8D56B* ID_OUI_FROM_DATABASE=Mirka Ltd. +OUI:B8D61A* + ID_OUI_FROM_DATABASE=Espressif Inc. + OUI:B8D6F6* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -101675,6 +103628,9 @@ OUI:B8F009* OUI:B8F080* ID_OUI_FROM_DATABASE=SPS, INC. +OUI:B8F0B9* + ID_OUI_FROM_DATABASE=zte corporation + OUI:B8F12A* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -102212,6 +104168,9 @@ OUI:BC6D05* OUI:BC6E64* ID_OUI_FROM_DATABASE=Sony Corporation +OUI:BC6E6D* + ID_OUI_FROM_DATABASE=EM Microelectronic + OUI:BC6E76* ID_OUI_FROM_DATABASE=Green Energy Options Ltd @@ -102533,6 +104492,12 @@ OUI:BCC61A* OUI:BCC6DB* ID_OUI_FROM_DATABASE=Nokia Corporation +OUI:BCC746* + ID_OUI_FROM_DATABASE=Hon Hai Precision IND.CO.,LTD + +OUI:BCC7DA* + ID_OUI_FROM_DATABASE=Earda Technologies co Ltd + OUI:BCC810* ID_OUI_FROM_DATABASE=Cisco SPVTG @@ -102566,6 +104531,9 @@ OUI:BCD177* OUI:BCD1D3* ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp. +OUI:BCD206* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:BCD295* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -102629,6 +104597,9 @@ OUI:BCE796* OUI:BCE92F* ID_OUI_FROM_DATABASE=HP Inc. +OUI:BCE9E2* + ID_OUI_FROM_DATABASE=Brocade Communications Systems LLC + OUI:BCEA2B* ID_OUI_FROM_DATABASE=CityCom GmbH @@ -102668,6 +104639,9 @@ OUI:BCF310* OUI:BCF45F* ID_OUI_FROM_DATABASE=zte corporation +OUI:BCF4D4* + ID_OUI_FROM_DATABASE=CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. + OUI:BCF5AC* ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) @@ -102686,6 +104660,9 @@ OUI:BCF9F2* OUI:BCFAB8* ID_OUI_FROM_DATABASE=Guangzhou Shiyuan Electronic Technology Company Limited +OUI:BCFAEB* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:BCFE8C* ID_OUI_FROM_DATABASE=Altronic, LLC @@ -102713,6 +104690,9 @@ OUI:C00380* OUI:C005C2* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. +OUI:C0060C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:C006C3* ID_OUI_FROM_DATABASE=TP-Link Corporation Limited @@ -102899,6 +104879,9 @@ OUI:C03DD9* OUI:C03E0F* ID_OUI_FROM_DATABASE=BSkyB Ltd +OUI:C03E50* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:C03EBA* ID_OUI_FROM_DATABASE=Dell Inc. @@ -103067,6 +105050,9 @@ OUI:C06C6D* OUI:C06D1A* ID_OUI_FROM_DATABASE=Tianjin Henxinhuifeng Technology Co.,Ltd. +OUI:C06DED* + ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. + OUI:C07009* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -103199,6 +105185,9 @@ OUI:C08C60* OUI:C08C71* ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company +OUI:C08D51* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + OUI:C08F20* ID_OUI_FROM_DATABASE=Shenzhen Skyworth Digital Technology CO., Ltd @@ -103298,6 +105287,9 @@ OUI:C09F05* OUI:C09F42* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:C09F51* + ID_OUI_FROM_DATABASE=SERNET (SUZHOU) TECHNOLOGIES CORPORATION + OUI:C09FE1* ID_OUI_FROM_DATABASE=zte corporation @@ -103346,12 +105338,18 @@ OUI:C0A66D* OUI:C0A8F0* ID_OUI_FROM_DATABASE=Adamson Systems Engineering +OUI:C0A938* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:C0AA68* ID_OUI_FROM_DATABASE=OSASI Technos Inc. OUI:C0AC54* ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS +OUI:C0AD97* + ID_OUI_FROM_DATABASE=TECNO MOBILE LIMITED + OUI:C0AEFD* ID_OUI_FROM_DATABASE=Shenzhen HC-WLAN Technology Co.,Ltd @@ -103412,6 +105410,9 @@ OUI:C0BFA7* OUI:C0BFC0* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:C0C170* + ID_OUI_FROM_DATABASE=Shenzhen SuperElectron Technology Co.,Ltd. + OUI:C0C1C0* ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC @@ -103559,12 +105560,18 @@ OUI:C0DCD7* OUI:C0DCDA* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:C0DD8A* + ID_OUI_FROM_DATABASE=Facebook Technologies, LLC + OUI:C0DF77* ID_OUI_FROM_DATABASE=Conrad Electronic SE OUI:C0E018* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:C0E01C* + ID_OUI_FROM_DATABASE=IoT Security Group, SL + OUI:C0E1BE* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -103592,11 +105599,17 @@ OUI:C0E7BF* OUI:C0E862* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:C0E911* + ID_OUI_FROM_DATABASE=Private + OUI:C0EAE4* ID_OUI_FROM_DATABASE=Sonicwall +OUI:C0EDE5* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + OUI:C0EE40* - ID_OUI_FROM_DATABASE=Laird Technologies + ID_OUI_FROM_DATABASE=Laird Connectivity OUI:C0EEB5* ID_OUI_FROM_DATABASE=Enice Network. @@ -103925,6 +105938,9 @@ OUI:C436DA* OUI:C43772* ID_OUI_FROM_DATABASE=Virtuozzo International GmbH +OUI:C43875* + ID_OUI_FROM_DATABASE=Sonos, Inc. + OUI:C438D3* ID_OUI_FROM_DATABASE=TAGATEC CO.,LTD @@ -104402,6 +106418,51 @@ OUI:C49F4C* OUI:C49FF3* ID_OUI_FROM_DATABASE=Mciao Technologies, Inc. +OUI:C4A10E0* + ID_OUI_FROM_DATABASE=HYOSUNG HEAVY INDUSTRIES + +OUI:C4A10E1* + ID_OUI_FROM_DATABASE=BARTEC PIXAVI AS + +OUI:C4A10E2* + ID_OUI_FROM_DATABASE=Wistron InfoComn (Kunshan) Co., Ltd. + +OUI:C4A10E3* + ID_OUI_FROM_DATABASE=Consolinno Energy GmbH + +OUI:C4A10E4* + ID_OUI_FROM_DATABASE=Harbour Cross Technology Ltd + +OUI:C4A10E5* + ID_OUI_FROM_DATABASE=O-NET Industrial Technologies (Shenzhen) Limited + +OUI:C4A10E6* + ID_OUI_FROM_DATABASE=Hainan World Electronic Science and Techology Co.,Ltd + +OUI:C4A10E7* + ID_OUI_FROM_DATABASE=Guangzhou South Satellite Navigation Instrument Co., Ltd. + +OUI:C4A10E8* + ID_OUI_FROM_DATABASE=Ayla Networks (Shenzhen) Co., Ltd. + +OUI:C4A10E9* + ID_OUI_FROM_DATABASE=XI'AN YEP TELECOM TECHNOLOGY CO.,LTD + +OUI:C4A10EA* + ID_OUI_FROM_DATABASE=Jiangsu Perceive World Technology Co.,Ltd. + +OUI:C4A10EB* + ID_OUI_FROM_DATABASE=Clinton Electronics Corporation + +OUI:C4A10EC* + ID_OUI_FROM_DATABASE=Focus-on + +OUI:C4A10ED* + ID_OUI_FROM_DATABASE=Connectlab SRL + +OUI:C4A10EE* + ID_OUI_FROM_DATABASE=Alio, Inc + OUI:C4A151* ID_OUI_FROM_DATABASE=Sichuan Tianyi Comheart Telecom Co.,LTD @@ -104486,6 +106547,9 @@ OUI:C4BED4* OUI:C4BF60* ID_OUI_FROM_DATABASE=TECNO MOBILE LIMITED +OUI:C4C063* + ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd + OUI:C4C0AE* ID_OUI_FROM_DATABASE=MIDORI ELECTRONIC CO., LTD. @@ -104570,6 +106634,12 @@ OUI:C4DD57* OUI:C4DE7B* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. +OUI:C4DEE2* + ID_OUI_FROM_DATABASE=Espressif Inc. + +OUI:C4DF39* + ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd. + OUI:C4E032* ID_OUI_FROM_DATABASE=IEEE 1904.1 Working Group @@ -104789,6 +106859,9 @@ OUI:C80E95* OUI:C81073* ID_OUI_FROM_DATABASE=CENTURY OPTICOMM CO.,LTD +OUI:C8120B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:C8138B* ID_OUI_FROM_DATABASE=Shenzhen Skyworth Digital Technology CO., Ltd @@ -104852,6 +106925,9 @@ OUI:C82158* OUI:C821DA* ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd +OUI:C82496* + ID_OUI_FROM_DATABASE=Jiangsu Yinhe Electronics Co.,Ltd. + OUI:C825E1* ID_OUI_FROM_DATABASE=Lemobile Information Technology (Beijing) Co., Ltd @@ -105149,6 +107225,9 @@ OUI:C869CD* OUI:C86C1E* ID_OUI_FROM_DATABASE=Display Systems Ltd +OUI:C86C20* + ID_OUI_FROM_DATABASE=Sichuan Tianyi Comheart Telecom Co.,LTD + OUI:C86C3D* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. @@ -105344,6 +107423,9 @@ OUI:C89D18* OUI:C89E43* ID_OUI_FROM_DATABASE=NETGEAR +OUI:C89E61* + ID_OUI_FROM_DATABASE=Lyngsoe Systems LTd + OUI:C89F1A* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -105431,6 +107513,9 @@ OUI:C8B5B7* OUI:C8B6D3* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:C8B82F* + ID_OUI_FROM_DATABASE=eero inc. + OUI:C8BA94* ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) @@ -105461,6 +107546,9 @@ OUI:C8BD69* OUI:C8BE19* ID_OUI_FROM_DATABASE=D-Link International +OUI:C8BE35* + ID_OUI_FROM_DATABASE=Extreme Networks, Inc. + OUI:C8BFFE* ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. @@ -105551,6 +107639,9 @@ OUI:C8D5FE* OUI:C8D69D* ID_OUI_FROM_DATABASE=Arab International Optronics +OUI:C8D6B7* + ID_OUI_FROM_DATABASE=Solidigm Technology + OUI:C8D719* ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC @@ -105617,6 +107708,9 @@ OUI:C8E7F0* OUI:C8EAF8* ID_OUI_FROM_DATABASE=zte corporation +OUI:C8EBEC* + ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd + OUI:C8EE08* ID_OUI_FROM_DATABASE=TANGTOP TECHNOLOGY CO.,LTD @@ -105629,6 +107723,9 @@ OUI:C8EEA6* OUI:C8EF2E* ID_OUI_FROM_DATABASE=Beijing Gefei Tech. Co., Ltd +OUI:C8F09E* + ID_OUI_FROM_DATABASE=Espressif Inc. + OUI:C8F230* ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD @@ -106034,6 +108131,9 @@ OUI:CC3D82* OUI:CC3E5F* ID_OUI_FROM_DATABASE=Hewlett Packard +OUI:CC3E79* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:CC3F1D* ID_OUI_FROM_DATABASE=HMS Industrial Networks SLU @@ -106067,6 +108167,9 @@ OUI:CC46D6* OUI:CC4703* ID_OUI_FROM_DATABASE=Intercon Systems Co., Ltd. +OUI:CC4792* + ID_OUI_FROM_DATABASE=ASIX Electronics Corporation + OUI:CC47BD* ID_OUI_FROM_DATABASE=Rhombus Systems @@ -106131,7 +108234,7 @@ OUI:CC4F5CD* ID_OUI_FROM_DATABASE=Beijing Neutron Technology CO.,LTD. OUI:CC4F5CE* - ID_OUI_FROM_DATABASE=Buttons (Beijing) Technology Limited + ID_OUI_FROM_DATABASE=Beijing Techao Weijia Technology Limited OUI:CC500A* ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD @@ -106196,6 +108299,9 @@ OUI:CC5FBF* OUI:CC60BB* ID_OUI_FROM_DATABASE=Empower RF Systems +OUI:CC60C8* + ID_OUI_FROM_DATABASE=Microsoft Corporation + OUI:CC61E5* ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company @@ -106208,6 +108314,9 @@ OUI:CC65AD* OUI:CC660A* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:CC6618* + ID_OUI_FROM_DATABASE=Adtran Inc + OUI:CC66B2* ID_OUI_FROM_DATABASE=Nokia @@ -106307,6 +108416,9 @@ OUI:CC812A* OUI:CC81DA* ID_OUI_FROM_DATABASE=Phicomm (Shanghai) Co., Ltd. +OUI:CC827F* + ID_OUI_FROM_DATABASE=Advantech Technology (CHINA) Co., Ltd. + OUI:CC82EB* ID_OUI_FROM_DATABASE=KYOCERA CORPORATION @@ -106736,9 +108848,15 @@ OUI:CCDB04* OUI:CCDB93* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:CCDBA7* + ID_OUI_FROM_DATABASE=Espressif Inc. + OUI:CCDC55* ID_OUI_FROM_DATABASE=Dragonchip Limited +OUI:CCDD58* + ID_OUI_FROM_DATABASE=Robert Bosch GmbH + OUI:CCE0C3* ID_OUI_FROM_DATABASE=EXTEN Technologies, Inc. @@ -106793,6 +108911,9 @@ OUI:CCEF48* OUI:CCF0FD* ID_OUI_FROM_DATABASE=China Mobile (Hangzhou) Information Technology Co., Ltd. +OUI:CCF305* + ID_OUI_FROM_DATABASE=SHENZHEN TIAN XING CHUANG ZHAN ELECTRONIC CO.,LTD + OUI:CCF3A5* ID_OUI_FROM_DATABASE=Chi Mei Communication Systems, Inc @@ -106998,7 +109119,7 @@ OUI:D01E1D* ID_OUI_FROM_DATABASE=SaiNXT Technologies LLP OUI:D021AC* - ID_OUI_FROM_DATABASE=Yo Labs LLC + ID_OUI_FROM_DATABASE=Yohana OUI:D021F9* ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. @@ -107525,6 +109646,9 @@ OUI:D096FB* OUI:D097FE* ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd. +OUI:D0989C* + ID_OUI_FROM_DATABASE=ConMet + OUI:D099D5* ID_OUI_FROM_DATABASE=Alcatel-Lucent @@ -107597,6 +109721,9 @@ OUI:D0A0D6* OUI:D0A311* ID_OUI_FROM_DATABASE=Neuberger Gebäudeautomation GmbH +OUI:D0A46F* + ID_OUI_FROM_DATABASE=China Dragon Technology Limited + OUI:D0A4B1* ID_OUI_FROM_DATABASE=Sonifex Ltd. @@ -107945,6 +110072,9 @@ OUI:D0FA1D* OUI:D0FCCC* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:D0FCD0* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + OUI:D0FF50* ID_OUI_FROM_DATABASE=Texas Instruments @@ -108215,6 +110345,9 @@ OUI:D440F0* OUI:D44165* ID_OUI_FROM_DATABASE=Sichuan Tianyi Comheart Telecom Co.,LTD +OUI:D4430E* + ID_OUI_FROM_DATABASE=Zhejiang Dahua Technology Co., Ltd. + OUI:D443A8* ID_OUI_FROM_DATABASE=Changzhou Haojie Electric Co., Ltd. @@ -108299,6 +110432,9 @@ OUI:D45763* OUI:D45800* ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD +OUI:D45A3F* + ID_OUI_FROM_DATABASE=Juniper Networks + OUI:D45AB2* ID_OUI_FROM_DATABASE=Galleon Systems @@ -108677,6 +110813,9 @@ OUI:D4A148* OUI:D4A33D* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:D4A3EB* + ID_OUI_FROM_DATABASE=Shenzhen iComm Semiconductor CO.,LTD + OUI:D4A425* ID_OUI_FROM_DATABASE=SMAX Technology Co., Ltd. @@ -108758,6 +110897,9 @@ OUI:D4BBE6* OUI:D4BD1E* ID_OUI_FROM_DATABASE=5VT Technologies,Taiwan LTd. +OUI:D4BD4F* + ID_OUI_FROM_DATABASE=Ruckus Wireless + OUI:D4BED9* ID_OUI_FROM_DATABASE=Dell Inc. @@ -108845,6 +110987,9 @@ OUI:D4D7A9* OUI:D4D7CF* ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd. +OUI:D4D853* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:D4D898* ID_OUI_FROM_DATABASE=Korea CNO Tech Co., Ltd @@ -108863,9 +111008,15 @@ OUI:D4DCCD* OUI:D4DF57* ID_OUI_FROM_DATABASE=Alpinion Medical Systems +OUI:D4E053* + ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company + OUI:D4E08E* ID_OUI_FROM_DATABASE=ValueHD Corporation +OUI:D4E22F* + ID_OUI_FROM_DATABASE=Roku, Inc + OUI:D4E2CB* ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. @@ -108920,6 +111071,9 @@ OUI:D4F057* OUI:D4F0B4* ID_OUI_FROM_DATABASE=Napco Security Technologies +OUI:D4F0EA* + ID_OUI_FROM_DATABASE=Beijing Xiaomi Mobile Software Co., Ltd + OUI:D4F143* ID_OUI_FROM_DATABASE=IPROAD.,Inc @@ -109019,6 +111173,9 @@ OUI:D80DE3* OUI:D80F99* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. +OUI:D81068* + ID_OUI_FROM_DATABASE=Murata Manufacturing Co., Ltd. + OUI:D8109F* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -109158,7 +111315,10 @@ OUI:D833B7* ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS OUI:D834EE* - ID_OUI_FROM_DATABASE=Stem Audio + ID_OUI_FROM_DATABASE=SHURE INCORPORATED + +OUI:D8365F* + ID_OUI_FROM_DATABASE=Intelbras OUI:D8373B* ID_OUI_FROM_DATABASE=Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd @@ -109409,6 +111569,9 @@ OUI:D8803C* OUI:D88083* ID_OUI_FROM_DATABASE=CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. +OUI:D880DC* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:D881CE* ID_OUI_FROM_DATABASE=AHN INC. @@ -109463,6 +111626,9 @@ OUI:D8860BE* OUI:D887D5* ID_OUI_FROM_DATABASE=Leadcore Technology CO.,LTD +OUI:D88863* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:D888CE* ID_OUI_FROM_DATABASE=RF Technology Pty Ltd @@ -109544,6 +111710,9 @@ OUI:D89B3B* OUI:D89C67* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. +OUI:D89C8E* + ID_OUI_FROM_DATABASE=Comcast Cable Corporation + OUI:D89D67* ID_OUI_FROM_DATABASE=Hewlett Packard @@ -109820,6 +111989,9 @@ OUI:D8E0B8* OUI:D8E0E1* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:D8E2DF* + ID_OUI_FROM_DATABASE=Microsoft Corporation + OUI:D8E3AE* ID_OUI_FROM_DATABASE=CIRTEC MEDICAL SYSTEMS @@ -110090,6 +112262,9 @@ OUI:DC3350* OUI:DC35F1* ID_OUI_FROM_DATABASE=Positivo Tecnologia S.A. +OUI:DC360C* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + OUI:DC36430* ID_OUI_FROM_DATABASE=Meier Tobler AG @@ -110330,6 +112505,9 @@ OUI:DC5392* OUI:DC543D* ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED +OUI:DC5475* + ID_OUI_FROM_DATABASE=Espressif Inc. + OUI:DC54D7* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. @@ -110411,6 +112589,9 @@ OUI:DC7144* OUI:DC7196* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:DC71DD* + ID_OUI_FROM_DATABASE=AX Technologies + OUI:DC7223* ID_OUI_FROM_DATABASE=Hui Zhou Gaoshengda Technology Co.,LTD @@ -110471,6 +112652,9 @@ OUI:DC8C37* OUI:DC8D8A* ID_OUI_FROM_DATABASE=Nokia Solutions and Networks GmbH & Co. KG +OUI:DC8E95* + ID_OUI_FROM_DATABASE=Silicon Laboratories + OUI:DC9020* ID_OUI_FROM_DATABASE=RURU TEK PRIVATE LIMITED @@ -110501,6 +112685,9 @@ OUI:DC9914* OUI:DC99FE* ID_OUI_FROM_DATABASE=Armatura LLC +OUI:DC9A7D* + ID_OUI_FROM_DATABASE=HISENSE VISUAL TECHNOLOGY CO.,LTD + OUI:DC9A8E* ID_OUI_FROM_DATABASE=Nanjing Cocomm electronics co., LTD @@ -110567,6 +112754,9 @@ OUI:DCA8CF* OUI:DCA904* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:DCA956* + ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + OUI:DCA971* ID_OUI_FROM_DATABASE=Intel Corporate @@ -110621,6 +112811,9 @@ OUI:DCBB96* OUI:DCBD7A* ID_OUI_FROM_DATABASE=Guangzhou Shiyuan Electronic Technology Company Limited +OUI:DCBE49* + ID_OUI_FROM_DATABASE=ITEL MOBILE LIMITED + OUI:DCBE7A* ID_OUI_FROM_DATABASE=Zhejiang Nurotron Biotechnology Co. @@ -110912,6 +113105,9 @@ OUI:E00084* OUI:E002A5* ID_OUI_FROM_DATABASE=ABB Robotics +OUI:E0036B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:E00370* ID_OUI_FROM_DATABASE=ShenZhen Continental Wireless Technology Co., Ltd. @@ -111044,12 +113240,18 @@ OUI:E02636* OUI:E0271A* ID_OUI_FROM_DATABASE=TTC Next-generation Home Network System WG +OUI:E0276C* + ID_OUI_FROM_DATABASE=Guangzhou Shiyuan Electronic Technology Company Limited + OUI:E02861* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD OUI:E0286D* ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH +OUI:E028B1* + ID_OUI_FROM_DATABASE=Shenzhen Skyworth Digital Technology CO., Ltd + OUI:E02967* ID_OUI_FROM_DATABASE=HMD Global Oy @@ -111152,12 +113354,18 @@ OUI:E0469A* OUI:E046E5* ID_OUI_FROM_DATABASE=Gosuncn Technology Group Co., Ltd. +OUI:E046EE* + ID_OUI_FROM_DATABASE=NETGEAR + OUI:E048AF* ID_OUI_FROM_DATABASE=Premietech Limited OUI:E048D3* ID_OUI_FROM_DATABASE=MOBIWIRE MOBILES (NINGBO) CO.,LTD +OUI:E048D8* + ID_OUI_FROM_DATABASE=Guangzhi Wulian Technology(Guangzhou) Co., Ltd + OUI:E049ED* ID_OUI_FROM_DATABASE=Audeze LLC @@ -111302,12 +113510,18 @@ OUI:E06995* OUI:E069BA* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:E06A05* + ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd + OUI:E06C4E* ID_OUI_FROM_DATABASE=Shenzhen TINNO Mobile Technology Corp. OUI:E06CA6* ID_OUI_FROM_DATABASE=Creotech Instruments S.A. +OUI:E06CC5* + ID_OUI_FROM_DATABASE=Huawei Device Co., Ltd. + OUI:E06CF6* ID_OUI_FROM_DATABASE=ESSENCORE limited @@ -111347,6 +113561,9 @@ OUI:E078A3* OUI:E0795E* ID_OUI_FROM_DATABASE=Wuxi Xiaohu Technology Co.,Ltd. +OUI:E0798D* + ID_OUI_FROM_DATABASE=Silicon Laboratories + OUI:E079C4* ID_OUI_FROM_DATABASE=iRay Technology Company Limited @@ -111368,6 +113585,9 @@ OUI:E07F53* OUI:E07F88* ID_OUI_FROM_DATABASE=EVIDENCE Network SIA +OUI:E0806B* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + OUI:E08177* ID_OUI_FROM_DATABASE=GreenBytes, Inc. @@ -111434,6 +113654,9 @@ OUI:E09861* OUI:E09971* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:E09C8D* + ID_OUI_FROM_DATABASE=Seakeeper, Inc. + OUI:E09D13* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -111716,6 +113939,9 @@ OUI:E0D4E8* OUI:E0D55E* ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. +OUI:E0D738* + ID_OUI_FROM_DATABASE=WireStar Networks + OUI:E0D7BA* ID_OUI_FROM_DATABASE=Texas Instruments @@ -111833,6 +114059,9 @@ OUI:E0F5CA* OUI:E0F62D* ID_OUI_FROM_DATABASE=Juniper Networks +OUI:E0F678* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + OUI:E0F6B5* ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd @@ -112379,6 +114608,9 @@ OUI:E48F34* OUI:E48F65* ID_OUI_FROM_DATABASE=Yelatma Instrument Making Enterprise, JSC +OUI:E4902A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:E49069* ID_OUI_FROM_DATABASE=Rockwell Automation @@ -112544,6 +114776,9 @@ OUI:E4B318* OUI:E4B503* ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd. +OUI:E4B633* + ID_OUI_FROM_DATABASE=Wuxi Stars Microsystem Technology Co., Ltd + OUI:E4B97A* ID_OUI_FROM_DATABASE=Dell Inc. @@ -112994,6 +115229,9 @@ OUI:E839DF* OUI:E83A12* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:E83A4B* + ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd. + OUI:E83A97* ID_OUI_FROM_DATABASE=Toshiba Corporation @@ -113042,6 +115280,9 @@ OUI:E84D74* OUI:E84DD0* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:E84DEC* + ID_OUI_FROM_DATABASE=Xerox Corporation + OUI:E84E06* ID_OUI_FROM_DATABASE=EDUP INTERNATIONAL (HK) CO., LTD @@ -113306,6 +115547,9 @@ OUI:E88152* OUI:E88175* ID_OUI_FROM_DATABASE=zte corporation +OUI:E881AB* + ID_OUI_FROM_DATABASE=Beijing Sankuai Online Technology Co.,Ltd + OUI:E8825B* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. @@ -113468,6 +115712,9 @@ OUI:E8B2AC* OUI:E8B2FE* ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. +OUI:E8B3EF* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + OUI:E8B4700* ID_OUI_FROM_DATABASE=DongGuan Ramaxel Memory Technology @@ -113633,6 +115880,9 @@ OUI:E8D765* OUI:E8D819* ID_OUI_FROM_DATABASE=AzureWave Technology Inc. +OUI:E8D87E* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + OUI:E8D8D1* ID_OUI_FROM_DATABASE=HP Inc. @@ -113651,6 +115901,9 @@ OUI:E8DAAA* OUI:E8DB84* ID_OUI_FROM_DATABASE=Espressif Inc. +OUI:E8DC6C* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:E8DE00* ID_OUI_FROM_DATABASE=ChongQing GuanFang Technology Co.,LTD @@ -113723,6 +115976,9 @@ OUI:E8EB1B* OUI:E8EB34* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:E8EBD3* + ID_OUI_FROM_DATABASE=Mellanox Technologies, Inc. + OUI:E8ECA3* ID_OUI_FROM_DATABASE=Dongguan Liesheng Electronic Co.Ltd @@ -113765,6 +116021,9 @@ OUI:E8F654* OUI:E8F724* ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise +OUI:E8F791* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + OUI:E8F928* ID_OUI_FROM_DATABASE=RFTECH SRL @@ -113777,6 +116036,9 @@ OUI:E8FA23* OUI:E8FAF7* ID_OUI_FROM_DATABASE=Guangdong Uniteddata Holding Group Co., Ltd. +OUI:E8FB1C* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + OUI:E8FBE9* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -113885,6 +116147,9 @@ OUI:EC1D8B* OUI:EC1F72* ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) +OUI:EC2125* + ID_OUI_FROM_DATABASE=Toshiba Corp. + OUI:EC219F* ID_OUI_FROM_DATABASE=VidaBox LLC @@ -113945,6 +116210,9 @@ OUI:EC2E98* OUI:EC3091* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:EC30B3* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + OUI:EC316D* ID_OUI_FROM_DATABASE=Hansgrohe @@ -114002,6 +116270,9 @@ OUI:EC4269* OUI:EC42B4* ID_OUI_FROM_DATABASE=ADC Corporation +OUI:EC42CC* + ID_OUI_FROM_DATABASE=Apple, Inc. + OUI:EC42F0* ID_OUI_FROM_DATABASE=ADL Embedded Solutions, Inc. @@ -114053,6 +116324,9 @@ OUI:EC52DC* OUI:EC542E* ID_OUI_FROM_DATABASE=Shanghai XiMei Electronic Technology Co. Ltd +OUI:EC551C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:EC55F9* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. @@ -114086,9 +116360,15 @@ OUI:EC5C84* OUI:EC5F23* ID_OUI_FROM_DATABASE=Qinghai Kimascend Electronics Technology Co. Ltd. +OUI:EC6073* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + OUI:EC60E0* ID_OUI_FROM_DATABASE=AVI-ON LABS +OUI:EC6260* + ID_OUI_FROM_DATABASE=Espressif Inc. + OUI:EC6264* ID_OUI_FROM_DATABASE=Global411 Internet Services, LLC @@ -114182,6 +116462,9 @@ OUI:EC8009* OUI:EC8193* ID_OUI_FROM_DATABASE=Logitech, Inc +OUI:EC819C* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:EC8263* ID_OUI_FROM_DATABASE=zte corporation @@ -114986,6 +117269,9 @@ OUI:F04CD5* OUI:F04DA2* ID_OUI_FROM_DATABASE=Dell Inc. +OUI:F04DD4* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + OUI:F04F7C* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. @@ -115076,6 +117362,9 @@ OUI:F06865* OUI:F06BCA* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:F06C5D* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + OUI:F06C73* ID_OUI_FROM_DATABASE=Nokia @@ -115178,6 +117467,9 @@ OUI:F085C1* OUI:F08620* ID_OUI_FROM_DATABASE=Arcadyan Corporation +OUI:F0877F* + ID_OUI_FROM_DATABASE=Magnetar Technology Shenzhen Co., LTD. + OUI:F08A28* ID_OUI_FROM_DATABASE=JIANGSU HENGSION ELECTRONIC S and T CO.,LTD @@ -115400,6 +117692,9 @@ OUI:F0B5D1* OUI:F0B61E* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:F0B661* + ID_OUI_FROM_DATABASE=eero inc. + OUI:F0B6EB* ID_OUI_FROM_DATABASE=Poslab Technology Co., Ltd. @@ -115454,6 +117749,9 @@ OUI:F0C850* OUI:F0C88C* ID_OUI_FROM_DATABASE=LeddarTech Inc. +OUI:F0C8B5* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:F0C9D1* ID_OUI_FROM_DATABASE=GD Midea Air-Conditioning Equipment Co.,Ltd. @@ -115484,6 +117782,9 @@ OUI:F0D3A7* OUI:F0D3E7* ID_OUI_FROM_DATABASE=Sensometrix SA +OUI:F0D415* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:F0D4E2* ID_OUI_FROM_DATABASE=Dell Inc. @@ -116135,9 +118436,15 @@ OUI:F46B8C* OUI:F46BEF* ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS +OUI:F46C68* + ID_OUI_FROM_DATABASE=Wistron Neweb Corporation + OUI:F46D04* ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. +OUI:F46D2F* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + OUI:F46DE2* ID_OUI_FROM_DATABASE=zte corporation @@ -116252,6 +118559,9 @@ OUI:F483E1* OUI:F4844C* ID_OUI_FROM_DATABASE=Texas Instruments +OUI:F4848D* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + OUI:F485C6* ID_OUI_FROM_DATABASE=FDT Technologies @@ -116462,6 +118772,9 @@ OUI:F4B301* OUI:F4B381* ID_OUI_FROM_DATABASE=WindowMaster A/S +OUI:F4B3B1* + ID_OUI_FROM_DATABASE=Silicon Laboratories + OUI:F4B520* ID_OUI_FROM_DATABASE=Biostar Microtech international corp. @@ -116558,6 +118871,9 @@ OUI:F4C7AA* OUI:F4C7C8* ID_OUI_FROM_DATABASE=Kelvin Inc. +OUI:F4C88A* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:F4CA24* ID_OUI_FROM_DATABASE=FreeBit Co., Ltd. @@ -116652,7 +118968,7 @@ OUI:F4E142* ID_OUI_FROM_DATABASE=Delta Elektronika BV OUI:F4E204* - ID_OUI_FROM_DATABASE=Traqueur + ID_OUI_FROM_DATABASE=COYOTE SYSTEM OUI:F4E2C6* ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. @@ -117161,6 +119477,9 @@ OUI:F84DFC* OUI:F84E17* ID_OUI_FROM_DATABASE=Sony Corporation +OUI:F84E58* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:F84E73* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -117194,6 +119513,9 @@ OUI:F854AF* OUI:F854B8* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. +OUI:F85548* + ID_OUI_FROM_DATABASE=Texas Instruments + OUI:F855CD* ID_OUI_FROM_DATABASE=Visteon Corporation @@ -117227,6 +119549,9 @@ OUI:F85C4D* OUI:F85C7D* ID_OUI_FROM_DATABASE=Shenzhen Honesty Electronics Co.,Ltd. +OUI:F85E0B* + ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd. + OUI:F85E3C* ID_OUI_FROM_DATABASE=SHENZHEN ZHIBOTONG ELECTRONICS CO.,LTD @@ -117569,6 +119894,9 @@ OUI:F8AA8A* OUI:F8AB05* ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS +OUI:F8AB82* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + OUI:F8ABE5* ID_OUI_FROM_DATABASE=shenzhen worldelite electronics co., LTD @@ -117578,6 +119906,9 @@ OUI:F8AC65* OUI:F8AC6D* ID_OUI_FROM_DATABASE=Deltenna Ltd +OUI:F8AD24* + ID_OUI_FROM_DATABASE=Realme Chongqing Mobile Telecommunications Corp.,Ltd. + OUI:F8ADCB* ID_OUI_FROM_DATABASE=HMD Global Oy @@ -117662,6 +119993,9 @@ OUI:F8B797* OUI:F8B7E2* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:F8B8B4* + ID_OUI_FROM_DATABASE=Shenzhen Skyworth Digital Technology CO., Ltd + OUI:F8B95A* ID_OUI_FROM_DATABASE=LG Innotek @@ -117737,6 +120071,9 @@ OUI:F8CAB8* OUI:F8CC6E* ID_OUI_FROM_DATABASE=DEPO Electronics Ltd +OUI:F8CDC8* + ID_OUI_FROM_DATABASE=Sichuan Tianyi Comheart Telecom Co.,LTD + OUI:F8CE72* ID_OUI_FROM_DATABASE=Wistron Corporation @@ -117815,6 +120152,9 @@ OUI:F8E43B* OUI:F8E44E* ID_OUI_FROM_DATABASE=MCOT INC. +OUI:F8E4A4* + ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD + OUI:F8E4E3* ID_OUI_FROM_DATABASE=Intel Corporate @@ -117851,6 +120191,9 @@ OUI:F8E903* OUI:F8E94E* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:F8E94F* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:F8E968* ID_OUI_FROM_DATABASE=Egker Kft. @@ -117974,6 +120317,9 @@ OUI:FC0FE6* OUI:FC0FE7* ID_OUI_FROM_DATABASE=Microchip Technology Inc. +OUI:FC101A* + ID_OUI_FROM_DATABASE=Palo Alto Networks + OUI:FC10BD* ID_OUI_FROM_DATABASE=Control Sistematizado S.A. @@ -118361,6 +120707,9 @@ OUI:FC8399* OUI:FC83C6* ID_OUI_FROM_DATABASE=N-Radio Technologies Co., Ltd. +OUI:FC8417* + ID_OUI_FROM_DATABASE=Honor Device Co., Ltd. + OUI:FC8596* ID_OUI_FROM_DATABASE=Axonne Inc. @@ -118451,6 +120800,9 @@ OUI:FC9FAE* OUI:FC9FE1* ID_OUI_FROM_DATABASE=CONWIN.Tech. Ltd +OUI:FCA05A* + ID_OUI_FROM_DATABASE=Oray.com co., LTD. + OUI:FCA13E* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -118598,6 +120950,9 @@ OUI:FCB6D8* OUI:FCB7F0* ID_OUI_FROM_DATABASE=Idaho National Laboratory +OUI:FCB97E* + ID_OUI_FROM_DATABASE=GE Appliances + OUI:FCBBA1* ID_OUI_FROM_DATABASE=Shenzhen Minicreate Technology Co.,Ltd diff --git a/hwdb.d/20-acpi-vendor.hwdb b/hwdb.d/20-acpi-vendor.hwdb index 87eba251c..7fdb463e1 100644 --- a/hwdb.d/20-acpi-vendor.hwdb +++ b/hwdb.d/20-acpi-vendor.hwdb @@ -51,6 +51,9 @@ acpi:ATML*: acpi:AUTH*: ID_VENDOR_FROM_DATABASE=AuthenTec +acpi:AWDZ*: + ID_VENDOR_FROM_DATABASE=Shanghai Aiwei Electronic Technology Co., Ltd. + acpi:BABA*: ID_VENDOR_FROM_DATABASE=Alibaba Co., Ltd. @@ -219,6 +222,9 @@ acpi:MSHW*: acpi:MXIM*: ID_VENDOR_FROM_DATABASE=Maxim Integrated +acpi:NOLO*: + ID_VENDOR_FROM_DATABASE=NOLO VR + acpi:NVDA*: ID_VENDOR_FROM_DATABASE=Nvidia @@ -273,6 +279,9 @@ acpi:SECC*: acpi:SHRP*: ID_VENDOR_FROM_DATABASE=Sharp Corporation +acpi:SILC*: + ID_VENDOR_FROM_DATABASE=Silicom Ltd. Connectivity Solutions + acpi:SNSL*: ID_VENDOR_FROM_DATABASE=Sensel, Inc. @@ -912,6 +921,9 @@ acpi:ATV*: acpi:ATX*: ID_VENDOR_FROM_DATABASE=Athenix Corporation +acpi:AUD*: + ID_VENDOR_FROM_DATABASE=AudioControl + acpi:AUG*: ID_VENDOR_FROM_DATABASE=August Home, Inc. @@ -6036,6 +6048,9 @@ acpi:SBS*: acpi:SBT*: ID_VENDOR_FROM_DATABASE=Senseboard Technologies AB +acpi:SCA*: + ID_VENDOR_FROM_DATABASE=Schneider Consumer Group + acpi:SCB*: ID_VENDOR_FROM_DATABASE=SeeCubic B.V. @@ -6156,6 +6171,9 @@ acpi:SES*: acpi:SET*: ID_VENDOR_FROM_DATABASE=SendTek Corporation +acpi:SFL*: + ID_VENDOR_FROM_DATABASE=Shiftall Inc. + acpi:SFM*: ID_VENDOR_FROM_DATABASE=TORNADO Company diff --git a/hwdb.d/20-acpi-vendor.hwdb.patch b/hwdb.d/20-acpi-vendor.hwdb.patch index 38f3eee8a..d1781e419 100644 --- a/hwdb.d/20-acpi-vendor.hwdb.patch +++ b/hwdb.d/20-acpi-vendor.hwdb.patch @@ -1,5 +1,5 @@ ---- 20-acpi-vendor.hwdb.base 2021-12-23 19:33:43.195441335 +0900 -+++ 20-acpi-vendor.hwdb 2021-12-23 19:33:43.215441358 +0900 +--- 20-acpi-vendor.hwdb.base 2022-03-29 12:18:39.832815359 +0200 ++++ 20-acpi-vendor.hwdb 2022-03-29 12:18:39.838815428 +0200 @@ -3,6 +3,8 @@ # Data imported from: # https://uefi.org/uefi-pnp-export @@ -19,7 +19,7 @@ acpi:AMDI*: ID_VENDOR_FROM_DATABASE=AMD -@@ -325,6 +324,9 @@ +@@ -334,6 +333,9 @@ acpi:AAA*: ID_VENDOR_FROM_DATABASE=Avolites Ltd @@ -29,7 +29,7 @@ acpi:AAE*: ID_VENDOR_FROM_DATABASE=Anatek Electronics Inc. -@@ -352,6 +354,9 @@ +@@ -361,6 +363,9 @@ acpi:ABO*: ID_VENDOR_FROM_DATABASE=D-Link Systems Inc @@ -39,7 +39,7 @@ acpi:ABS*: ID_VENDOR_FROM_DATABASE=Abaco Systems, Inc. -@@ -397,7 +402,7 @@ +@@ -406,7 +411,7 @@ acpi:ACO*: ID_VENDOR_FROM_DATABASE=Allion Computer Inc. @@ -48,7 +48,7 @@ ID_VENDOR_FROM_DATABASE=Aspen Tech Inc acpi:ACR*: -@@ -673,6 +678,9 @@ +@@ -682,6 +687,9 @@ acpi:AMT*: ID_VENDOR_FROM_DATABASE=AMT International Industry @@ -58,7 +58,7 @@ acpi:AMX*: ID_VENDOR_FROM_DATABASE=AMX LLC -@@ -721,6 +729,9 @@ +@@ -730,6 +738,9 @@ acpi:AOA*: ID_VENDOR_FROM_DATABASE=AOpen Inc. @@ -68,7 +68,7 @@ acpi:AOE*: ID_VENDOR_FROM_DATABASE=Advanced Optics Electronics, Inc. -@@ -730,6 +741,9 @@ +@@ -739,6 +750,9 @@ acpi:AOT*: ID_VENDOR_FROM_DATABASE=Alcatel @@ -78,7 +78,7 @@ acpi:APC*: ID_VENDOR_FROM_DATABASE=American Power Conversion -@@ -905,7 +919,7 @@ +@@ -917,7 +931,7 @@ ID_VENDOR_FROM_DATABASE=ALPS ALPINE CO., LTD. acpi:AUO*: @@ -87,7 +87,7 @@ acpi:AUR*: ID_VENDOR_FROM_DATABASE=Aureal Semiconductor -@@ -985,6 +999,9 @@ +@@ -997,6 +1011,9 @@ acpi:AXE*: ID_VENDOR_FROM_DATABASE=Axell Corporation @@ -97,7 +97,7 @@ acpi:AXI*: ID_VENDOR_FROM_DATABASE=American Magnetics -@@ -1135,6 +1152,9 @@ +@@ -1147,6 +1164,9 @@ acpi:BML*: ID_VENDOR_FROM_DATABASE=BIOMED Lab @@ -107,7 +107,7 @@ acpi:BMS*: ID_VENDOR_FROM_DATABASE=BIOMEDISYS -@@ -1147,6 +1167,9 @@ +@@ -1159,6 +1179,9 @@ acpi:BNO*: ID_VENDOR_FROM_DATABASE=Bang & Olufsen @@ -117,7 +117,7 @@ acpi:BNS*: ID_VENDOR_FROM_DATABASE=Boulder Nonlinear Systems -@@ -1390,6 +1413,9 @@ +@@ -1402,6 +1425,9 @@ acpi:CHA*: ID_VENDOR_FROM_DATABASE=Chase Research PLC @@ -127,7 +127,7 @@ acpi:CHD*: ID_VENDOR_FROM_DATABASE=ChangHong Electric Co.,Ltd -@@ -1552,6 +1578,9 @@ +@@ -1564,6 +1590,9 @@ acpi:COD*: ID_VENDOR_FROM_DATABASE=CODAN Pty. Ltd. @@ -137,7 +137,7 @@ acpi:COI*: ID_VENDOR_FROM_DATABASE=Codec Inc. -@@ -1961,7 +1990,7 @@ +@@ -1973,7 +2002,7 @@ ID_VENDOR_FROM_DATABASE=Dragon Information Technology acpi:DJE*: @@ -146,7 +146,7 @@ acpi:DJP*: ID_VENDOR_FROM_DATABASE=Maygay Machines, Ltd -@@ -2299,6 +2328,9 @@ +@@ -2311,6 +2340,9 @@ acpi:EIN*: ID_VENDOR_FROM_DATABASE=Elegant Invention @@ -156,7 +156,7 @@ acpi:EKA*: ID_VENDOR_FROM_DATABASE=MagTek Inc. -@@ -2563,6 +2595,9 @@ +@@ -2575,6 +2607,9 @@ acpi:FCG*: ID_VENDOR_FROM_DATABASE=First International Computer Ltd @@ -166,7 +166,7 @@ acpi:FCS*: ID_VENDOR_FROM_DATABASE=Focus Enhancements, Inc. -@@ -2939,7 +2974,7 @@ +@@ -2951,7 +2986,7 @@ ID_VENDOR_FROM_DATABASE=General Standards Corporation acpi:GSM*: @@ -175,7 +175,7 @@ acpi:GSN*: ID_VENDOR_FROM_DATABASE=Grandstream Networks, Inc. -@@ -3040,6 +3075,9 @@ +@@ -3052,6 +3087,9 @@ acpi:HEC*: ID_VENDOR_FROM_DATABASE=Hisense Electric Co., Ltd. @@ -185,7 +185,7 @@ acpi:HEL*: ID_VENDOR_FROM_DATABASE=Hitachi Micro Systems Europe Ltd -@@ -3172,6 +3210,9 @@ +@@ -3184,6 +3222,9 @@ acpi:HSD*: ID_VENDOR_FROM_DATABASE=HannStar Display Corp @@ -195,7 +195,7 @@ acpi:HSM*: ID_VENDOR_FROM_DATABASE=AT&T Microelectronics -@@ -3298,6 +3339,9 @@ +@@ -3310,6 +3351,9 @@ acpi:ICI*: ID_VENDOR_FROM_DATABASE=Infotek Communication Inc @@ -205,7 +205,7 @@ acpi:ICM*: ID_VENDOR_FROM_DATABASE=Intracom SA -@@ -3394,6 +3438,9 @@ +@@ -3406,6 +3450,9 @@ acpi:IKE*: ID_VENDOR_FROM_DATABASE=Ikegami Tsushinki Co. Ltd. @@ -215,7 +215,7 @@ acpi:IKS*: ID_VENDOR_FROM_DATABASE=Ikos Systems Inc -@@ -3439,6 +3486,9 @@ +@@ -3451,6 +3498,9 @@ acpi:IMT*: ID_VENDOR_FROM_DATABASE=Inmax Technology Corporation @@ -225,7 +225,7 @@ acpi:INA*: ID_VENDOR_FROM_DATABASE=Inventec Corporation -@@ -3955,6 +4005,9 @@ +@@ -3967,6 +4017,9 @@ acpi:LAN*: ID_VENDOR_FROM_DATABASE=Sodeman Lancom Inc @@ -235,7 +235,7 @@ acpi:LAS*: ID_VENDOR_FROM_DATABASE=LASAT Comm. A/S -@@ -4003,6 +4056,9 @@ +@@ -4015,6 +4068,9 @@ acpi:LED*: ID_VENDOR_FROM_DATABASE=Long Engineering Design Inc @@ -245,7 +245,7 @@ acpi:LEG*: ID_VENDOR_FROM_DATABASE=Legerity, Inc -@@ -4018,6 +4074,9 @@ +@@ -4030,6 +4086,9 @@ acpi:LGC*: ID_VENDOR_FROM_DATABASE=Logic Ltd @@ -255,7 +255,7 @@ acpi:LGI*: ID_VENDOR_FROM_DATABASE=Logitech Inc -@@ -4075,6 +4134,9 @@ +@@ -4087,6 +4146,9 @@ acpi:LND*: ID_VENDOR_FROM_DATABASE=Land Computer Company Ltd @@ -265,7 +265,7 @@ acpi:LNK*: ID_VENDOR_FROM_DATABASE=Link Tech Inc -@@ -4109,7 +4171,7 @@ +@@ -4121,7 +4183,7 @@ ID_VENDOR_FROM_DATABASE=Design Technology acpi:LPL*: @@ -274,7 +274,7 @@ acpi:LSC*: ID_VENDOR_FROM_DATABASE=LifeSize Communications -@@ -4285,6 +4347,9 @@ +@@ -4297,6 +4359,9 @@ acpi:MCX*: ID_VENDOR_FROM_DATABASE=Millson Custom Solutions Inc. @@ -284,7 +284,7 @@ acpi:MDA*: ID_VENDOR_FROM_DATABASE=Media4 Inc -@@ -4525,6 +4590,9 @@ +@@ -4537,6 +4602,9 @@ acpi:MOM*: ID_VENDOR_FROM_DATABASE=Momentum Data Systems @@ -294,7 +294,7 @@ acpi:MOS*: ID_VENDOR_FROM_DATABASE=Moses Corporation -@@ -4759,6 +4827,9 @@ +@@ -4771,6 +4839,9 @@ acpi:NAL*: ID_VENDOR_FROM_DATABASE=Network Alchemy @@ -304,7 +304,7 @@ acpi:NAT*: ID_VENDOR_FROM_DATABASE=NaturalPoint Inc. -@@ -5281,6 +5352,9 @@ +@@ -5293,6 +5364,9 @@ acpi:PCX*: ID_VENDOR_FROM_DATABASE=PC Xperten @@ -314,7 +314,7 @@ acpi:PDM*: ID_VENDOR_FROM_DATABASE=Psion Dacom Plc. -@@ -5344,9 +5418,6 @@ +@@ -5356,9 +5430,6 @@ acpi:PHE*: ID_VENDOR_FROM_DATABASE=Philips Medical Systems Boeblingen GmbH @@ -324,7 +324,7 @@ acpi:PHL*: ID_VENDOR_FROM_DATABASE=Philips Consumer Electronics Company -@@ -5437,9 +5508,6 @@ +@@ -5449,9 +5520,6 @@ acpi:PNL*: ID_VENDOR_FROM_DATABASE=Panelview, Inc. @@ -334,7 +334,7 @@ acpi:PNR*: ID_VENDOR_FROM_DATABASE=Planar Systems, Inc. -@@ -5575,15 +5643,9 @@ +@@ -5587,15 +5655,9 @@ acpi:PTS*: ID_VENDOR_FROM_DATABASE=Plain Tree Systems Inc @@ -350,7 +350,7 @@ acpi:PVG*: ID_VENDOR_FROM_DATABASE=Proview Global Co., Ltd -@@ -5899,9 +5961,6 @@ +@@ -5911,9 +5973,6 @@ acpi:RTI*: ID_VENDOR_FROM_DATABASE=Rancho Tech Inc @@ -360,7 +360,7 @@ acpi:RTL*: ID_VENDOR_FROM_DATABASE=Realtek Semiconductor Company Ltd -@@ -6070,9 +6129,6 @@ +@@ -6085,9 +6144,6 @@ acpi:SEE*: ID_VENDOR_FROM_DATABASE=SeeColor Corporation @@ -370,7 +370,7 @@ acpi:SEI*: ID_VENDOR_FROM_DATABASE=Seitz & Associates Inc -@@ -6541,6 +6597,9 @@ +@@ -6559,6 +6615,9 @@ acpi:SVD*: ID_VENDOR_FROM_DATABASE=SVD Computer @@ -380,7 +380,7 @@ acpi:SVI*: ID_VENDOR_FROM_DATABASE=Sun Microsystems -@@ -6625,6 +6684,9 @@ +@@ -6643,6 +6702,9 @@ acpi:SZM*: ID_VENDOR_FROM_DATABASE=Shenzhen MTC Co., Ltd @@ -390,7 +390,7 @@ acpi:TAA*: ID_VENDOR_FROM_DATABASE=Tandberg -@@ -6715,6 +6777,9 @@ +@@ -6733,6 +6795,9 @@ acpi:TDG*: ID_VENDOR_FROM_DATABASE=Six15 Technologies @@ -400,7 +400,7 @@ acpi:TDM*: ID_VENDOR_FROM_DATABASE=Tandem Computer Europe Inc -@@ -6757,6 +6822,9 @@ +@@ -6775,6 +6840,9 @@ acpi:TEV*: ID_VENDOR_FROM_DATABASE=Televés, S.A. @@ -410,7 +410,7 @@ acpi:TEZ*: ID_VENDOR_FROM_DATABASE=Tech Source Inc. -@@ -6880,9 +6948,6 @@ +@@ -6898,9 +6966,6 @@ acpi:TNC*: ID_VENDOR_FROM_DATABASE=TNC Industrial Company Ltd @@ -420,7 +420,7 @@ acpi:TNM*: ID_VENDOR_FROM_DATABASE=TECNIMAGEN SA -@@ -7192,14 +7257,14 @@ +@@ -7210,14 +7275,14 @@ acpi:UNC*: ID_VENDOR_FROM_DATABASE=Unisys Corporation @@ -441,7 +441,7 @@ acpi:UNI*: ID_VENDOR_FROM_DATABASE=Uniform Industry Corp. -@@ -7234,6 +7299,9 @@ +@@ -7252,6 +7317,9 @@ acpi:USA*: ID_VENDOR_FROM_DATABASE=Utimaco Safeware AG @@ -451,7 +451,7 @@ acpi:USD*: ID_VENDOR_FROM_DATABASE=U.S. Digital Corporation -@@ -7489,9 +7557,6 @@ +@@ -7507,9 +7575,6 @@ acpi:WAL*: ID_VENDOR_FROM_DATABASE=Wave Access @@ -461,7 +461,7 @@ acpi:WAV*: ID_VENDOR_FROM_DATABASE=Wavephore -@@ -7616,7 +7681,7 @@ +@@ -7634,7 +7699,7 @@ ID_VENDOR_FROM_DATABASE=WyreStorm Technologies LLC acpi:WYS*: @@ -470,7 +470,7 @@ acpi:WYT*: ID_VENDOR_FROM_DATABASE=Wooyoung Image & Information Co.,Ltd. -@@ -7630,9 +7695,6 @@ +@@ -7648,9 +7713,6 @@ acpi:XDM*: ID_VENDOR_FROM_DATABASE=XDM Ltd. @@ -480,7 +480,7 @@ acpi:XES*: ID_VENDOR_FROM_DATABASE=Extreme Engineering Solutions, Inc. -@@ -7663,9 +7725,6 @@ +@@ -7681,9 +7743,6 @@ acpi:XNT*: ID_VENDOR_FROM_DATABASE=XN Technologies, Inc. @@ -490,7 +490,7 @@ acpi:XQU*: ID_VENDOR_FROM_DATABASE=SHANGHAI SVA-DAV ELECTRONICS CO., LTD -@@ -7732,6 +7791,9 @@ +@@ -7750,6 +7809,9 @@ acpi:ZBX*: ID_VENDOR_FROM_DATABASE=Zebax Technologies diff --git a/hwdb.d/20-dmi-id.hwdb b/hwdb.d/20-dmi-id.hwdb index a614473bd..c7bf6cfab 100644 --- a/hwdb.d/20-dmi-id.hwdb +++ b/hwdb.d/20-dmi-id.hwdb @@ -4,3 +4,7 @@ dmi:bvnLENOVO* ID_SYSFS_ATTRIBUTE_MODEL=product_version ID_VENDOR_FROM_DATABASE=Lenovo + +# Microsoft Surface 1's chassis type +dmi:bvnMicrosoft Corporation*:pvrSurface with Windows 8 Pro* + ID_CHASSIS=tablet diff --git a/hwdb.d/20-pci-classes.hwdb b/hwdb.d/20-pci-classes.hwdb index 3dca78b05..52603ebad 100644 --- a/hwdb.d/20-pci-classes.hwdb +++ b/hwdb.d/20-pci-classes.hwdb @@ -612,7 +612,7 @@ pci:v*d*sv*sd*bc12sc00* ID_PCI_SUBCLASS_FROM_DATABASE=Processing accelerators pci:v*d*sv*sd*bc12sc01* - ID_PCI_SUBCLASS_FROM_DATABASE=AI Inference Accelerator + ID_PCI_SUBCLASS_FROM_DATABASE=SNIA Smart Data Accelerator Interface (SDXI) controller pci:v*d*sv*sd*bc13* ID_PCI_CLASS_FROM_DATABASE=Non-Essential Instrumentation diff --git a/hwdb.d/20-pci-vendor-model.hwdb b/hwdb.d/20-pci-vendor-model.hwdb index 745f5545c..dba7d96d3 100644 --- a/hwdb.d/20-pci-vendor-model.hwdb +++ b/hwdb.d/20-pci-vendor-model.hwdb @@ -218,6 +218,15 @@ pci:v00000731d00009200* pci:v00000731d0000920A* ID_MODEL_FROM_DATABASE=JH920 +pci:v00000731d0000920Asv00000731sd0000920A* + ID_MODEL_FROM_DATABASE=JH920 + +pci:v00000731d0000920Asv00000731sd0000920B* + ID_MODEL_FROM_DATABASE=JH920 (-I) + +pci:v00000731d0000920Asv00000731sd0000920C* + ID_MODEL_FROM_DATABASE=JH920 (-M) + pci:v00000731d0000920B* ID_MODEL_FROM_DATABASE=JH920-I @@ -227,21 +236,42 @@ pci:v00000731d0000920C* pci:v00000731d00009210* ID_MODEL_FROM_DATABASE=JM9210 +pci:v00000731d00009210sv00000731sd00009210* + ID_MODEL_FROM_DATABASE=JM9210 + +pci:v00000731d00009210sv00000731sd00009211* + ID_MODEL_FROM_DATABASE=JM9210 (-I) + pci:v00000731d00009211* ID_MODEL_FROM_DATABASE=JM9210-I pci:v00000731d00009230* ID_MODEL_FROM_DATABASE=JM9230 +pci:v00000731d00009230sv00000731sd00009230* + ID_MODEL_FROM_DATABASE=JM9230 + +pci:v00000731d00009230sv00000731sd00009231* + ID_MODEL_FROM_DATABASE=JM9230 (-I) + pci:v00000731d00009231* ID_MODEL_FROM_DATABASE=JM9231-I pci:v00000731d00009250* ID_MODEL_FROM_DATABASE=JM9250 +pci:v00000731d00009250sv00000731sd00009250* + ID_MODEL_FROM_DATABASE=JM9250 + pci:v00000731d0000930A* ID_MODEL_FROM_DATABASE=JH930-I +pci:v00000731d0000930Asv00000731sd0000930A* + ID_MODEL_FROM_DATABASE=JH930-I + +pci:v00000731d0000930Asv00000731sd0000930B* + ID_MODEL_FROM_DATABASE=JH930-I (JH930-M) + pci:v00000731d0000930B* ID_MODEL_FROM_DATABASE=JH930-M @@ -2297,9 +2327,15 @@ pci:v00001000d000000E6sv00001028sd0000200D* pci:v00001000d000000E6sv00001028sd0000200E* ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS38xx (HBA350i MX) +pci:v00001000d000000E6sv00001028sd00002170* + ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS38xx (HBA350i MM) + pci:v00001000d000000E6sv00001028sd00002175* ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS38xx (HBA350i Adapter) +pci:v00001000d000000E6sv00001028sd00002197* + ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS38xx (HBA350i MM LP) + pci:v00001000d000000E6sv00001D49sd00000205* ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS38xx (ThinkSystem 440-16i SAS/SATA PCIe Gen4 12Gb Internal HBA) @@ -2316,10 +2352,10 @@ pci:v00001000d000000E6sv00001D49sd00000209* ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS38xx (ThinkSystem 440-8e SAS/SATA PCIe Gen4 12Gb HBA) pci:v00001000d000000E6sv00008086sd00004050* - ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS38xx (Storage Controller RS3P4QF160F) + ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS38xx (Storage Controller RS3P4QF160J) pci:v00001000d000000E6sv00008086sd00004070* - ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS38xx (Storage Controller RS3P4GF016F) + ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Secure SAS38xx (Storage Controller RS3P4GF016J) pci:v00001000d000000E7* ID_MODEL_FROM_DATABASE=Fusion-MPT 12GSAS/PCIe Unsupported SAS38xx @@ -2615,6 +2651,9 @@ pci:v00001000d000010E2* pci:v00001000d000010E2sv00001000sd00004000* ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Secure SAS39xx (MegaRAID 9560-16i) +pci:v00001000d000010E2sv00001000sd00004002* + ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Secure SAS39xx (MegaRAID 9561-16i) + pci:v00001000d000010E2sv00001000sd00004010* ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Secure SAS39xx (MegaRAID 9560-8i) @@ -2705,6 +2744,9 @@ pci:v00001000d000010E6sv00001028sd00002174* pci:v00001000d000010E6sv00001028sd00002177* ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Secure SAS38xx (PERC H350 Adapter) +pci:v00001000d000010E6sv00001028sd00002199* + ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Secure SAS38xx (PERC H350 Mini LP) + pci:v00001000d000010E6sv00001D49sd00000505* ID_MODEL_FROM_DATABASE=MegaRAID 12GSAS/PCIe Secure SAS38xx (ThinkSystem RAID 540-8i PCIe Gen4 12Gb Adapter) @@ -2768,6 +2810,9 @@ pci:v00001000d0000C012* pci:v00001000d0000C012sv00001D49sd00000003* ID_MODEL_FROM_DATABASE=PEX880xx PCIe Gen 4 Switch (ThinkSystem 1611-8P PCIe Gen4 NVMe Switch Adapter) +pci:v00001000d0000C030* + ID_MODEL_FROM_DATABASE=PEX890xx PCIe Gen 5 Switch + pci:v00001001* ID_VENDOR_FROM_DATABASE=Kolter Electronic @@ -2994,7 +3039,7 @@ pci:v00001002d0000164D* ID_MODEL_FROM_DATABASE=Rembrandt pci:v00001002d00001681* - ID_MODEL_FROM_DATABASE=Rembrandt + ID_MODEL_FROM_DATABASE=Rembrandt [Radeon 680M] pci:v00001002d00001714* ID_MODEL_FROM_DATABASE=BeaverCreek HDMI Audio [Radeon HD 6500D and 6400G-6600G series] @@ -5889,13 +5934,16 @@ pci:v00001002d00006611sv00001B0Asd000090D3* ID_MODEL_FROM_DATABASE=Oland [Radeon HD 8570 / R5 430 OEM / R7 240/340 / Radeon 520 OEM] (Radeon R7 240 OEM) pci:v00001002d00006613* - ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340] + ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340 / Radeon 520] pci:v00001002d00006613sv0000148Csd00007340* - ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340] (Radeon R7 340) + ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340 / Radeon 520] (Radeon R7 340) pci:v00001002d00006613sv00001682sd00007240* - ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340] (R7 240 2048 MB) + ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340 / Radeon 520] (R7 240 2048 MB) + +pci:v00001002d00006613sv00001DCFsd00003000* + ID_MODEL_FROM_DATABASE=Oland PRO [Radeon R7 240/340 / Radeon 520] pci:v00001002d00006631* ID_MODEL_FROM_DATABASE=Oland @@ -10976,18 +11024,27 @@ pci:v00001002d000073A3* pci:v00001002d000073A4* ID_MODEL_FROM_DATABASE=Navi 21 USB +pci:v00001002d000073A5* + ID_MODEL_FROM_DATABASE=Navi 21 [Radeon RX 6950 XT] + pci:v00001002d000073AB* ID_MODEL_FROM_DATABASE=Navi 21 Pro-XLA [Radeon Pro W6800X/Radeon Pro W6800X Duo] pci:v00001002d000073AF* ID_MODEL_FROM_DATABASE=Navi 21 [Radeon RX 6900 XT] +pci:v00001002d000073AFsv0000148Csd00002414* + ID_MODEL_FROM_DATABASE=Navi 21 [Radeon RX 6900 XT] (Navi 21 XTXH [PowerColor Red Devil RX 6900 XT Ultimate]) + pci:v00001002d000073BF* ID_MODEL_FROM_DATABASE=Navi 21 [Radeon RX 6800/6800 XT / 6900 XT] pci:v00001002d000073BFsv00001002sd00000E3A* ID_MODEL_FROM_DATABASE=Navi 21 [Radeon RX 6800/6800 XT / 6900 XT] (Radeon RX 6900 XT) +pci:v00001002d000073BFsv0000148Csd00002408* + ID_MODEL_FROM_DATABASE=Navi 21 [Radeon RX 6800/6800 XT / 6900 XT] (Red Devil AMD Radeon RX 6900 XT) + pci:v00001002d000073BFsv00001EAEsd00006701* ID_MODEL_FROM_DATABASE=Navi 21 [Radeon RX 6800/6800 XT / 6900 XT] (XFX Speedster MERC 319 AMD Radeon RX 6800 XT Black) @@ -10998,7 +11055,7 @@ pci:v00001002d000073C4* ID_MODEL_FROM_DATABASE=Navi 22 USB pci:v00001002d000073DF* - ID_MODEL_FROM_DATABASE=Navi 22 [Radeon RX 6700/6700 XT / 6800M] + ID_MODEL_FROM_DATABASE=Navi 22 [Radeon RX 6700/6700 XT/6750 XT / 6800M] pci:v00001002d000073E0* ID_MODEL_FROM_DATABASE=Navi 23 @@ -11012,6 +11069,9 @@ pci:v00001002d000073E3* pci:v00001002d000073E4* ID_MODEL_FROM_DATABASE=Navi 23 USB +pci:v00001002d000073EF* + ID_MODEL_FROM_DATABASE=Navi 23 [Radeon RX 6650 XT] + pci:v00001002d000073FF* ID_MODEL_FROM_DATABASE=Navi 23 [Radeon RX 6600/6600 XT/6600M] @@ -11027,6 +11087,12 @@ pci:v00001002d0000740C* pci:v00001002d0000740F* ID_MODEL_FROM_DATABASE=Aldebaran +pci:v00001002d0000743F* + ID_MODEL_FROM_DATABASE=Navi 24 [Radeon RX 6400 / 6500 XT] + +pci:v00001002d0000743Fsv00001DA2sd0000E457* + ID_MODEL_FROM_DATABASE=Navi 24 [Radeon RX 6400 / 6500 XT] (PULSE AMD Radeon RX 6500 XT) + pci:v00001002d00007833* ID_MODEL_FROM_DATABASE=RS350 Host Bridge @@ -12204,7 +12270,7 @@ pci:v00001002d0000AB20* ID_MODEL_FROM_DATABASE=Vega 20 HDMI Audio [Radeon VII] pci:v00001002d0000AB28* - ID_MODEL_FROM_DATABASE=Navi 21 HDMI Audio [Radeon RX 6800/6800 XT / 6900 XT] + ID_MODEL_FROM_DATABASE=Navi 21/23 HDMI/DP Audio Controller pci:v00001002d0000AB38* ID_MODEL_FROM_DATABASE=Navi 10 HDMI Audio @@ -14577,28 +14643,28 @@ pci:v00001022d000015E1sv0000EA50sd0000CE19* ID_MODEL_FROM_DATABASE=Raven USB 3.1 (mCOM10-L1900) pci:v00001022d000015E2* - ID_MODEL_FROM_DATABASE=Raven/Raven2/FireFlight/Renoir Audio Processor + ID_MODEL_FROM_DATABASE=ACP/ACP3X/ACP6x Audio Coprocessor pci:v00001022d000015E2sv000017AAsd00005124* - ID_MODEL_FROM_DATABASE=Raven/Raven2/FireFlight/Renoir Audio Processor (ThinkPad E595) + ID_MODEL_FROM_DATABASE=ACP/ACP3X/ACP6x Audio Coprocessor (ThinkPad E595) pci:v00001022d000015E2sv0000EA50sd0000CE19* - ID_MODEL_FROM_DATABASE=Raven/Raven2/FireFlight/Renoir Audio Processor (mCOM10-L1900) + ID_MODEL_FROM_DATABASE=ACP/ACP3X/ACP6x Audio Coprocessor (mCOM10-L1900) pci:v00001022d000015E3* - ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) HD Audio Controller + ID_MODEL_FROM_DATABASE=Family 17h/19h HD Audio Controller pci:v00001022d000015E3sv0000103Csd00008615* - ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) HD Audio Controller (Pavilion Laptop 15-cw1xxx) + ID_MODEL_FROM_DATABASE=Family 17h/19h HD Audio Controller (Pavilion Laptop 15-cw1xxx) pci:v00001022d000015E3sv00001043sd000086C7* - ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) HD Audio Controller (PRIME B450M-A Motherboard) + ID_MODEL_FROM_DATABASE=Family 17h/19h HD Audio Controller (PRIME B450M-A Motherboard) pci:v00001022d000015E3sv000017AAsd00005124* - ID_MODEL_FROM_DATABASE=Family 17h (Models 10h-1fh) HD Audio Controller (ThinkPad E595) + ID_MODEL_FROM_DATABASE=Family 17h/19h HD Audio Controller (ThinkPad E595) pci:v00001022d000015E4* - ID_MODEL_FROM_DATABASE=Raven/Raven2/Renoir Sensor Fusion Hub + ID_MODEL_FROM_DATABASE=Sensor Fusion Hub pci:v00001022d000015E5* ID_MODEL_FROM_DATABASE=Raven2 USB 3.1 @@ -15122,6 +15188,21 @@ pci:v00001022d000043C8* pci:v00001022d000043D5* ID_MODEL_FROM_DATABASE=400 Series Chipset USB 3.1 XHCI Controller +pci:v00001022d000043E9* + ID_MODEL_FROM_DATABASE=500 Series Chipset Switch Upstream Port + +pci:v00001022d000043EB* + ID_MODEL_FROM_DATABASE=500 Series Chipset SATA Controller + +pci:v00001022d000043EBsv00001B21sd00001062* + ID_MODEL_FROM_DATABASE=500 Series Chipset SATA Controller (ASM1062 Serial ATA Controller) + +pci:v00001022d000043EE* + ID_MODEL_FROM_DATABASE=500 Series Chipset USB 3.1 XHCI Controller + +pci:v00001022d000043EEsv00001B21sd00001142* + ID_MODEL_FROM_DATABASE=500 Series Chipset USB 3.1 XHCI Controller (ASM1042A USB 3.0 Host Controller) + pci:v00001022d000057A3* ID_MODEL_FROM_DATABASE=Matisse PCIe GPP Bridge @@ -34004,6 +34085,9 @@ pci:v000010DEd000010F0* pci:v000010DEd000010F1* ID_MODEL_FROM_DATABASE=GP106 High Definition Audio Controller +pci:v000010DEd000010F1sv00001043sd000085B6* + ID_MODEL_FROM_DATABASE=GP106 High Definition Audio Controller (DUAL-GTX1060-O6G [GeForce GTX 1060 6GB Dual]) + pci:v000010DEd000010F7* ID_MODEL_FROM_DATABASE=TU102 High Definition Audio Controller @@ -36092,6 +36176,9 @@ pci:v000010DEd00001C02* pci:v000010DEd00001C03* ID_MODEL_FROM_DATABASE=GP106 [GeForce GTX 1060 6GB] +pci:v000010DEd00001C03sv00001043sd000085B6* + ID_MODEL_FROM_DATABASE=GP106 [GeForce GTX 1060 6GB] (DUAL-GTX1060-O6G [GeForce GTX 1060 6GB Dual]) + pci:v000010DEd00001C04* ID_MODEL_FROM_DATABASE=GP106 [GeForce GTX 1060 5GB] @@ -36488,6 +36575,9 @@ pci:v000010DEd00001F02* pci:v000010DEd00001F02sv00001043sd00008673* ID_MODEL_FROM_DATABASE=TU106 [GeForce RTX 2070] (TURBO RTX 2070) +pci:v000010DEd00001F03* + ID_MODEL_FROM_DATABASE=TU106 [GeForce RTX 2060 12GB] + pci:v000010DEd00001F04* ID_MODEL_FROM_DATABASE=TU106 @@ -36587,6 +36677,12 @@ pci:v000010DEd00001F9C* pci:v000010DEd00001F9D* ID_MODEL_FROM_DATABASE=TU117M [GeForce GTX 1650 Mobile / Max-Q] +pci:v000010DEd00001F9F* + ID_MODEL_FROM_DATABASE=TU117M [GeForce MX550] + +pci:v000010DEd00001FA0* + ID_MODEL_FROM_DATABASE=TU117M [GeForce MX550] + pci:v000010DEd00001FAE* ID_MODEL_FROM_DATABASE=TU117GL @@ -36599,6 +36695,12 @@ pci:v000010DEd00001FB1* pci:v000010DEd00001FB2* ID_MODEL_FROM_DATABASE=TU117GLM [Quadro T400 Mobile] +pci:v000010DEd00001FB6* + ID_MODEL_FROM_DATABASE=TU117GLM [T600 Laptop GPU] + +pci:v000010DEd00001FB7* + ID_MODEL_FROM_DATABASE=TU117GLM [T550 Laptop GPU] + pci:v000010DEd00001FB8* ID_MODEL_FROM_DATABASE=TU117GLM [Quadro T2000 Mobile / Max-Q] @@ -36611,6 +36713,9 @@ pci:v000010DEd00001FBA* pci:v000010DEd00001FBB* ID_MODEL_FROM_DATABASE=TU117GLM [Quadro T500 Mobile] +pci:v000010DEd00001FBC* + ID_MODEL_FROM_DATABASE=TU117GLM [T1200 Laptop GPU] + pci:v000010DEd00001FBF* ID_MODEL_FROM_DATABASE=TU117GL @@ -36620,9 +36725,18 @@ pci:v000010DEd00001FD9* pci:v000010DEd00001FDD* ID_MODEL_FROM_DATABASE=TU117BM [GeForce GTX 1650 Mobile Refresh] +pci:v000010DEd00001FF0* + ID_MODEL_FROM_DATABASE=TU117GL [T1000 8GB] + +pci:v000010DEd00001FF2* + ID_MODEL_FROM_DATABASE=TU117GL [T400 4GB] + pci:v000010DEd00001FF9* ID_MODEL_FROM_DATABASE=TU117GLM [Quadro T1000 Mobile] +pci:v000010DEd00002082* + ID_MODEL_FROM_DATABASE=GA100 [CMP 170HX] + pci:v000010DEd000020B0* ID_MODEL_FROM_DATABASE=GA100 [A100 SXM4 40GB] @@ -36632,6 +36746,9 @@ pci:v000010DEd000020B1* pci:v000010DEd000020B2* ID_MODEL_FROM_DATABASE=GA100 [A100 SXM4 80GB] +pci:v000010DEd000020B3* + ID_MODEL_FROM_DATABASE=GA100 [PG506-242/243] + pci:v000010DEd000020B5* ID_MODEL_FROM_DATABASE=GA100 [A100 PCIe 80GB] @@ -36653,9 +36770,15 @@ pci:v000010DEd000020BF* pci:v000010DEd000020C2* ID_MODEL_FROM_DATABASE=GA100 [CMP 170HX] +pci:v000010DEd000020F0* + ID_MODEL_FROM_DATABASE=GA100 [A100-PG506-207] + pci:v000010DEd000020F1* ID_MODEL_FROM_DATABASE=GA100 [A100 PCIe 40GB] +pci:v000010DEd000020F2* + ID_MODEL_FROM_DATABASE=GA100 [A100-PG506-217] + pci:v000010DEd00002182* ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1660 Ti] @@ -36722,6 +36845,9 @@ pci:v000010DEd00002206sv00001462sd00003892* pci:v000010DEd00002208* ID_MODEL_FROM_DATABASE=GA102 [GeForce RTX 3080 Ti] +pci:v000010DEd0000220A* + ID_MODEL_FROM_DATABASE=GA102 [GeForce RTX 3080 12GB] + pci:v000010DEd0000220D* ID_MODEL_FROM_DATABASE=GA102 [CMP 90HX] @@ -36743,6 +36869,9 @@ pci:v000010DEd00002231* pci:v000010DEd00002232* ID_MODEL_FROM_DATABASE=GA102GL [RTX A4500] +pci:v000010DEd00002233* + ID_MODEL_FROM_DATABASE=GA102GL [RTX A5500] + pci:v000010DEd00002235* ID_MODEL_FROM_DATABASE=GA102GL [A40] @@ -36752,6 +36881,9 @@ pci:v000010DEd00002236* pci:v000010DEd00002237* ID_MODEL_FROM_DATABASE=GA102GL [A10G] +pci:v000010DEd00002238* + ID_MODEL_FROM_DATABASE=GA102GL [A10M] + pci:v000010DEd0000223F* ID_MODEL_FROM_DATABASE=GA102GL @@ -36767,9 +36899,18 @@ pci:v000010DEd00002302* pci:v000010DEd00002321* ID_MODEL_FROM_DATABASE=GA103 +pci:v000010DEd00002414* + ID_MODEL_FROM_DATABASE=GA103 [GeForce RTX 3060 Ti] + pci:v000010DEd00002420* ID_MODEL_FROM_DATABASE=GA103M [GeForce RTX 3080 Ti Mobile] +pci:v000010DEd00002438* + ID_MODEL_FROM_DATABASE=GA103GLM [RTX A5500 Laptop GPU] + +pci:v000010DEd00002460* + ID_MODEL_FROM_DATABASE=GA103M [GeForce RTX 3080 Ti Laptop GPU] + pci:v000010DEd00002482* ID_MODEL_FROM_DATABASE=GA104 [GeForce RTX 3070 Ti] @@ -36833,6 +36974,15 @@ pci:v000010DEd000024B7* pci:v000010DEd000024B8* ID_MODEL_FROM_DATABASE=GA104GLM [RTX A3000 Mobile] +pci:v000010DEd000024B9* + ID_MODEL_FROM_DATABASE=GA104GLM [RTX A3000 12GB Laptop GPU] + +pci:v000010DEd000024BA* + ID_MODEL_FROM_DATABASE=GA104GLM [RTX A4500 Laptop GPU] + +pci:v000010DEd000024BB* + ID_MODEL_FROM_DATABASE=GA104GLM [RTX A3000 Laptop GPU] + pci:v000010DEd000024BF* ID_MODEL_FROM_DATABASE=GA104 [GeForce RTX 3070 Engineering Sample] @@ -36842,6 +36992,12 @@ pci:v000010DEd000024DC* pci:v000010DEd000024DD* ID_MODEL_FROM_DATABASE=GA104M [GeForce RTX 3070 Mobile / Max-Q] +pci:v000010DEd000024E0* + ID_MODEL_FROM_DATABASE=GA104M [Geforce RTX 3070 Ti Laptop GPU] + +pci:v000010DEd000024FA* + ID_MODEL_FROM_DATABASE=GA104 [RTX A4500 Embedded GPU ] + pci:v000010DEd00002501* ID_MODEL_FROM_DATABASE=GA106 [GeForce RTX 3060] @@ -36854,6 +37010,9 @@ pci:v000010DEd00002504* pci:v000010DEd00002505* ID_MODEL_FROM_DATABASE=GA106 +pci:v000010DEd00002507* + ID_MODEL_FROM_DATABASE=GA106 [Geforce RTX 3050] + pci:v000010DEd00002520* ID_MODEL_FROM_DATABASE=GA106M [GeForce RTX 3060 Mobile / Max-Q] @@ -36863,12 +37022,18 @@ pci:v000010DEd00002523* pci:v000010DEd0000252F* ID_MODEL_FROM_DATABASE=GA106 [GeForce RTX 3060 Engineering Sample] +pci:v000010DEd00002531* + ID_MODEL_FROM_DATABASE=GA106 [RTX A2000] + pci:v000010DEd00002560* ID_MODEL_FROM_DATABASE=GA106M [GeForce RTX 3060 Mobile / Max-Q] pci:v000010DEd00002563* ID_MODEL_FROM_DATABASE=GA106M [GeForce RTX 3050 Ti Mobile / Max-Q] +pci:v000010DEd00002571* + ID_MODEL_FROM_DATABASE=GA106 [RTX A2000 12GB] + pci:v000010DEd00002583* ID_MODEL_FROM_DATABASE=GA107 [GeForce RTX 3050] @@ -36884,15 +37049,33 @@ pci:v000010DEd000025A4* pci:v000010DEd000025A5* ID_MODEL_FROM_DATABASE=GA107M [GeForce RTX 3050 Mobile] +pci:v000010DEd000025A6* + ID_MODEL_FROM_DATABASE=GA107M [GeForce MX570] + +pci:v000010DEd000025A7* + ID_MODEL_FROM_DATABASE=GA107M [GeForce MX570] + +pci:v000010DEd000025A9* + ID_MODEL_FROM_DATABASE=GA107M [GeForce RTX 2050] + pci:v000010DEd000025AF* ID_MODEL_FROM_DATABASE=GA107 [GeForce RTX 3050 Engineering Sample] pci:v000010DEd000025B5* ID_MODEL_FROM_DATABASE=GA107GLM [RTX A4 Mobile] +pci:v000010DEd000025B6* + ID_MODEL_FROM_DATABASE=GA107GL [A2 / A16] + pci:v000010DEd000025B8* ID_MODEL_FROM_DATABASE=GA107GLM [RTX A2000 Mobile] +pci:v000010DEd000025B9* + ID_MODEL_FROM_DATABASE=GA107GLM [RTX A1000 Laptop GPU] + +pci:v000010DEd000025BA* + ID_MODEL_FROM_DATABASE=GA107GLM [RTX A2000 8GB Laptop GPU] + pci:v000010DEd000025E0* ID_MODEL_FROM_DATABASE=GA107BM [GeForce RTX 3050 Ti Mobile] @@ -36902,6 +37085,12 @@ pci:v000010DEd000025E2* pci:v000010DEd000025E5* ID_MODEL_FROM_DATABASE=GA107BM [GeForce RTX 3050 Mobile] +pci:v000010DEd000025F9* + ID_MODEL_FROM_DATABASE=GA107 [RTX A1000 Embedded GPU ] + +pci:v000010DEd000025FA* + ID_MODEL_FROM_DATABASE=GA107 [RTX A2000 Embedded GPU] + pci:v000010DF* ID_VENDOR_FROM_DATABASE=Emulex Corporation @@ -37586,6 +37775,9 @@ pci:v000010ECd0000525Asv000017AAsd0000224F* pci:v000010ECd00005260* ID_MODEL_FROM_DATABASE=RTS5260 PCI Express Card Reader +pci:v000010ECd00005261* + ID_MODEL_FROM_DATABASE=RTS5261 PCI Express Card Reader + pci:v000010ECd00005286* ID_MODEL_FROM_DATABASE=RTS5286 PCI Express Card Reader @@ -37889,6 +38081,9 @@ pci:v000010ECd00008168sv0000103Csd0000825B* pci:v000010ECd00008168sv0000103Csd00008615* ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Pavilion Laptop 15-cw1xxx) +pci:v000010ECd00008168sv0000103Csd00008882* + ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (HP ProDesk 405 G8 Desktop Mini PC) + pci:v000010ECd00008168sv00001043sd000011F5* ID_MODEL_FROM_DATABASE=RTL8111/8168/8411 PCI Express Gigabit Ethernet Controller (Notebook motherboard (one of many models)) @@ -40556,6 +40751,9 @@ pci:v00001106d00009140* pci:v00001106d00009201* ID_MODEL_FROM_DATABASE=USB3.0 Controller +pci:v00001106d00009380* + ID_MODEL_FROM_DATABASE=Ncore Coprocessor + pci:v00001106d00009530* ID_MODEL_FROM_DATABASE=VX800/820/900 Series Secure Digital Memory Card Controller @@ -49715,6 +49913,9 @@ pci:v000012D8d00000508* pci:v000012D8d00002304* ID_MODEL_FROM_DATABASE=PI7C9X2G304 EL/SL PCIe2 3-Port/4-Lane Packet Switch +pci:v000012D8d00002308* + ID_MODEL_FROM_DATABASE=PI7C9X2G308GP 8-lane PCI Express 2.0 Switch with 3 PCI Express ports + pci:v000012D8d00002404* ID_MODEL_FROM_DATABASE=PI7C9X2G404 EL/SL PCIe2 4-Port/4-Lane Packet Switch @@ -51683,6 +51884,12 @@ pci:v000013A3d00000037* pci:v000013A3d00000037sv000013A3sd00000036* ID_MODEL_FROM_DATABASE=8204 Acceleration Processor (DX1740 Acceleration Card) +pci:v000013A3d00009240* + ID_MODEL_FROM_DATABASE=XR9240 Compression and Security Coprocessor [Panther II] + +pci:v000013A3d00009240sv000013A3sd00009200* + ID_MODEL_FROM_DATABASE=XR9240 Compression and Security Coprocessor [Panther II] (DX2040 Compression and Security Acceleration Card [Panther II]) + pci:v000013A4* ID_VENDOR_FROM_DATABASE=Rascom Inc @@ -56477,9 +56684,18 @@ pci:v000014C2* pci:v000014C3* ID_VENDOR_FROM_DATABASE=MEDIATEK Corp. +pci:v000014C3d00000608* + ID_MODEL_FROM_DATABASE=RZ608 Wi-Fi 6E 80MHz + +pci:v000014C3d00000616* + ID_MODEL_FROM_DATABASE=MT7922 802.11ax PCI Express Wireless Network Adapter + pci:v000014C3d00007612* ID_MODEL_FROM_DATABASE=MT7612E 802.11acbgn PCI Express Wireless Network Adapter +pci:v000014C3d00007615* + ID_MODEL_FROM_DATABASE=MT7615E 802.11ac PCI Express Wireless Network Adapter + pci:v000014C3d00007630* ID_MODEL_FROM_DATABASE=MT7630e 802.11bgn Wireless Network Adapter @@ -56489,6 +56705,9 @@ pci:v000014C3d00007662* pci:v000014C3d00007915* ID_MODEL_FROM_DATABASE=MT7915E 802.11ax PCI Express Wireless Network Adapter +pci:v000014C3d00007961* + ID_MODEL_FROM_DATABASE=MT7921 802.11ax PCI Express Wireless Network Adapter + pci:v000014C4* ID_VENDOR_FROM_DATABASE=IWASAKI Information Systems Co Ltd @@ -57503,6 +57722,12 @@ pci:v000014E4d00001688sv00001259sd00002708* pci:v000014E4d0000168A* ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet +pci:v000014E4d0000168Asv00001014sd00000493* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet (PCIe2 LP 4-Port (10Gb+1GbE) SR+RJ45 Adapter (FC EN0T; CCIN 2CC3)) + +pci:v000014E4d0000168Asv00001014sd00000494* + ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet (PCIe2 LP 4-Port (10Gb+1GbE) SR+RJ45 Adapter (FC EN0T; CCIN 2CC3)) + pci:v000014E4d0000168Asv00001028sd00001F5C* ID_MODEL_FROM_DATABASE=NetXtreme II BCM57800 1/10 Gigabit Ethernet (BCM57800 10-Gigabit Ethernet) @@ -61079,6 +61304,9 @@ pci:v000015ADd000007C0* pci:v000015ADd000007E0* ID_MODEL_FROM_DATABASE=SATA AHCI controller +pci:v000015ADd000007F0* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller + pci:v000015ADd00000801* ID_MODEL_FROM_DATABASE=Virtual Machine Interface @@ -61178,6 +61406,12 @@ pci:v000015B3d0000021E* pci:v000015B3d0000021F* ID_MODEL_FROM_DATABASE=CX8 Family [ConnectX-8 Secure Flash Recovery] +pci:v000015B3d00000220* + ID_MODEL_FROM_DATABASE=BF4 Family Flash Recovery [BlueField-4 SoC Flash Recovery] + +pci:v000015B3d00000221* + ID_MODEL_FROM_DATABASE=BF4 Family Secure Flash Recovery [BlueField-4 Secure Flash Recovery] + pci:v000015B3d0000024E* ID_MODEL_FROM_DATABASE=MT53100 [Spectrum-2, Flash recovery mode] @@ -61211,6 +61445,9 @@ pci:v000015B3d00000257* pci:v000015B3d00000258* ID_MODEL_FROM_DATABASE=Quantum-2 RMA +pci:v000015B3d00000259* + ID_MODEL_FROM_DATABASE=Abir Chiplet + pci:v000015B3d00000262* ID_MODEL_FROM_DATABASE=MT27710 [ConnectX-4 Lx Programmable] EN @@ -61232,6 +61469,9 @@ pci:v000015B3d00000274* pci:v000015B3d00000275* ID_MODEL_FROM_DATABASE=Spectrum-4C RMA +pci:v000015B3d00000277* + ID_MODEL_FROM_DATABASE=Spectrum-4TOR RMA + pci:v000015B3d00000281* ID_MODEL_FROM_DATABASE=NPS-600 Flash Recovery @@ -61727,6 +61967,15 @@ pci:v000015B3d0000A2DB* pci:v000015B3d0000A2DC* ID_MODEL_FROM_DATABASE=MT43244 BlueField-3 integrated ConnectX-7 network controller +pci:v000015B3d0000A2DD* + ID_MODEL_FROM_DATABASE=BF4 Family Crypto enabled [BlueField-4 SoC Crypto enabled] + +pci:v000015B3d0000A2DE* + ID_MODEL_FROM_DATABASE=BF4 Family Crypto disabled [BlueField-4 SoC Crypto disabled] + +pci:v000015B3d0000A2DF* + ID_MODEL_FROM_DATABASE=BF4 Family integrated network controller [BlueField-4 integrated network controller] + pci:v000015B3d0000C2D2* ID_MODEL_FROM_DATABASE=MT416842 BlueField SoC management interfac @@ -61739,6 +61988,9 @@ pci:v000015B3d0000C2D4* pci:v000015B3d0000C2D5* ID_MODEL_FROM_DATABASE=MT43244 BlueField-3 SoC Management Interface +pci:v000015B3d0000C2D6* + ID_MODEL_FROM_DATABASE=BF4 Family Management Interface [BlueField-4 SoC Management Interface] + pci:v000015B3d0000C738* ID_MODEL_FROM_DATABASE=MT51136 @@ -62019,10 +62271,10 @@ pci:v000015CE* ID_VENDOR_FROM_DATABASE=Genrad Inc pci:v000015CF* - ID_VENDOR_FROM_DATABASE=Hilscher GmbH + ID_VENDOR_FROM_DATABASE=Hilscher Gesellschaft für Systemautomation mbH pci:v000015CFd00000000* - ID_MODEL_FROM_DATABASE=CIFX 50E-DP(M/S) + ID_MODEL_FROM_DATABASE=CIFX PCI/PCIe pci:v000015D1* ID_VENDOR_FROM_DATABASE=Infineon Technologies AG @@ -64556,6 +64808,48 @@ pci:v0000177Dd0000A037* pci:v0000177Dd0000A040* ID_MODEL_FROM_DATABASE=THUNDERX CPT Cryptographic Accelerator +pci:v0000177Dd0000A059* + ID_MODEL_FROM_DATABASE=Octeon TX2 CGX (MAC) + +pci:v0000177Dd0000A060* + ID_MODEL_FROM_DATABASE=Octeon 10 RPM (MAC) + +pci:v0000177Dd0000A061* + ID_MODEL_FROM_DATABASE=Octeon Tx2 Loopback Interface (LBK) + +pci:v0000177Dd0000A063* + ID_MODEL_FROM_DATABASE=Octeon Tx2 RVU Physical Function + +pci:v0000177Dd0000A064* + ID_MODEL_FROM_DATABASE=Octeon Tx2 RVU Virtual Function + +pci:v0000177Dd0000A065* + ID_MODEL_FROM_DATABASE=Octeon Tx2 RVU Admin Function + +pci:v0000177Dd0000A09E* + ID_MODEL_FROM_DATABASE=Octeon 10 PTP controller + +pci:v0000177Dd0000A0F2* + ID_MODEL_FROM_DATABASE=Octeon 10 CPT Cryptographic Accelerator, Physical function + +pci:v0000177Dd0000A0F3* + ID_MODEL_FROM_DATABASE=Octeon 10 CPT Cryptographic Accelerator, Virtual function + +pci:v0000177Dd0000A0F6* + ID_MODEL_FROM_DATABASE=Octeon Tx2 SDP Physical Function + +pci:v0000177Dd0000A0F7* + ID_MODEL_FROM_DATABASE=Octeon Tx2 SDP Virtual Function + +pci:v0000177Dd0000A0F8* + ID_MODEL_FROM_DATABASE=Octeon Tx2 Loopback Interface Virtual Function (LBKVF) + +pci:v0000177Dd0000A0FD* + ID_MODEL_FROM_DATABASE=Octeon Tx2 CPT Cryptographic Accelerator, Physical function + +pci:v0000177Dd0000A0FE* + ID_MODEL_FROM_DATABASE=Octeon Tx2 CPT Cryptographic Accelerator, Virtual function + pci:v0000177Dd0000A100* ID_MODEL_FROM_DATABASE=THUNDERX CN88XX 48 core SoC @@ -64871,6 +65165,12 @@ pci:v000017CBd00001000* pci:v000017CBd00001101* ID_MODEL_FROM_DATABASE=QCA6390 Wireless Network Adapter [AX500-DBS (2x2)] +pci:v000017CBd00001103* + ID_MODEL_FROM_DATABASE=Atheros QCNFA765 + +pci:v000017CBd00001104* + ID_MODEL_FROM_DATABASE=QCN6024/9024/9074 Wireless Network Adapter + pci:v000017CC* ID_VENDOR_FROM_DATABASE=NetChip Technology, Inc @@ -67367,6 +67667,12 @@ pci:v00001974d00000018* pci:v00001974d00000019* ID_MODEL_FROM_DATABASE=FlexCard PCIe3 +pci:v00001974d0000001A* + ID_MODEL_FROM_DATABASE=FlexCard PXIe Ethernet + +pci:v00001974d0000001B* + ID_MODEL_FROM_DATABASE=FlexCard PCIe Ethernet + pci:v00001976* ID_VENDOR_FROM_DATABASE=TRENDnet @@ -68339,6 +68645,21 @@ pci:v00001AA8d00000009* pci:v00001AA8d0000000A* ID_MODEL_FROM_DATABASE=RAIDCore Controller +pci:v00001AA9* + ID_VENDOR_FROM_DATABASE=Schweitzer Engineering Laboratories + +pci:v00001AA9d0000000D* + ID_MODEL_FROM_DATABASE=SEL-3390S8 Serial Adapter + +pci:v00001AA9d0000000E* + ID_MODEL_FROM_DATABASE=SEL-3390E4 Ethernet Adapter + +pci:v00001AA9d00000014* + ID_MODEL_FROM_DATABASE=SEL-3390T Time and Ethernet Adapter + +pci:v00001AA9d00000018* + ID_MODEL_FROM_DATABASE=SEL-3390E4 Ethernet Adapter + pci:v00001AAE* ID_VENDOR_FROM_DATABASE=Global Velocity, Inc. @@ -68483,6 +68804,9 @@ pci:v00001AE8d00000A58* pci:v00001AE8d00000A5A* ID_MODEL_FROM_DATABASE=microEnable 5 AD8-CL +pci:v00001AE8d00000A64* + ID_MODEL_FROM_DATABASE=imaWorx CXP-12 Quad + pci:v00001AE8d00000B52* ID_MODEL_FROM_DATABASE=mE5 Abacus 4G Base @@ -68732,6 +69056,9 @@ pci:v00001B21d00001080sv00001849sd00001080* pci:v00001B21d00001142* ID_MODEL_FROM_DATABASE=ASM1042A USB 3.0 Host Controller +pci:v00001B21d00001166* + ID_MODEL_FROM_DATABASE=ASM1166 Serial ATA Controller + pci:v00001B21d00001182* ID_MODEL_FROM_DATABASE=ASM1182e 2-Port PCIe x1 Gen2 Packet Switch @@ -68744,12 +69071,18 @@ pci:v00001B21d00001184* pci:v00001B21d00001184sv00001849sd00001184* ID_MODEL_FROM_DATABASE=ASM1184e 4-Port PCIe x1 Gen2 Packet Switch +pci:v00001B21d00001187* + ID_MODEL_FROM_DATABASE=ASM1187e 7-Port PCIe x1 Gen2 Packet Switch + pci:v00001B21d00001242* ID_MODEL_FROM_DATABASE=ASM1142 USB 3.1 Host Controller pci:v00001B21d00001343* ID_MODEL_FROM_DATABASE=ASM1143 USB 3.1 Host Controller +pci:v00001B21d00001812* + ID_MODEL_FROM_DATABASE=ASM1812 6-Port PCIe x4 Gen2 Packet Switch + pci:v00001B21d00002142* ID_MODEL_FROM_DATABASE=ASM2142 USB 3.1 Host Controller @@ -68966,6 +69299,12 @@ pci:v00001B4Bd00002241sv00001028sd00002112* pci:v00001B4Bd00002241sv00001028sd00002113* ID_MODEL_FROM_DATABASE=88NR2241 Non-Volatile memory controller (BOSS-N1 Modular) +pci:v00001B4Bd00002241sv00001028sd00002151* + ID_MODEL_FROM_DATABASE=88NR2241 Non-Volatile memory controller (BOSS-N1 Modular ET) + +pci:v00001B4Bd00002241sv00001028sd00002196* + ID_MODEL_FROM_DATABASE=88NR2241 Non-Volatile memory controller (ROR-N100) + pci:v00001B4Bd00002241sv00001D49sd00000306* ID_MODEL_FROM_DATABASE=88NR2241 Non-Volatile memory controller (ThinkSystem M.2 NVMe 2-Bay RAID Enablement Kit) @@ -68993,6 +69332,9 @@ pci:v00001B4Bd00009130* pci:v00001B4Bd00009130sv00001043sd00008438* ID_MODEL_FROM_DATABASE=88SE9128 PCIe SATA 6 Gb/s RAID controller with HyperDuo (P8P67 Deluxe Motherboard) +pci:v00001B4Bd00009170* + ID_MODEL_FROM_DATABASE=88SE9170 PCIe 2.0 x1 2-port SATA 6 Gb/s Controller + pci:v00001B4Bd00009172* ID_MODEL_FROM_DATABASE=88SE9172 SATA 6Gb/s Controller @@ -69323,6 +69665,24 @@ pci:v00001BB1d00000100sv00001BB1sd00000151* pci:v00001BB1d00000100sv00001BB1sd00000152* ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5520 TCG) +pci:v00001BB1d00000100sv00001BB1sd00000153* + ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5050H) + +pci:v00001BB1d00000100sv00001BB1sd00000154* + ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5050H TCG) + +pci:v00001BB1d00000100sv00001BB1sd00000155* + ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5050M) + +pci:v00001BB1d00000100sv00001BB1sd00000156* + ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5050M TCG) + +pci:v00001BB1d00000100sv00001BB1sd00000157* + ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5050M 7mm) + +pci:v00001BB1d00000100sv00001BB1sd00000158* + ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro 5050M TCG 7mm) + pci:v00001BB1d00000100sv00001BB1sd000001A1* ID_MODEL_FROM_DATABASE=Nytro Flash Storage (Nytro XP7102) @@ -69332,6 +69692,9 @@ pci:v00001BB1d00005012* pci:v00001BB1d00005016* ID_MODEL_FROM_DATABASE=FireCuda 520 SSD +pci:v00001BB1d00005018* + ID_MODEL_FROM_DATABASE=FireCuda 530 SSD + pci:v00001BB3* ID_VENDOR_FROM_DATABASE=Bluecherry @@ -69779,12 +70142,54 @@ pci:v00001C5Cd00002839sv00001028sd0000214A* pci:v00001C5Cd00002839sv00001C5Csd00000100* ID_MODEL_FROM_DATABASE=PE8000 Series NVMe Solid State Drive +pci:v00001C5Cd00002849* + ID_MODEL_FROM_DATABASE=PE81x0 U.2/3 NVMe Solid State Drive + pci:v00001C5F* ID_VENDOR_FROM_DATABASE=Beijing Memblaze Technology Co. Ltd. pci:v00001C5Fd0000000D* ID_MODEL_FROM_DATABASE=PBlaze5 520/526 +pci:v00001C5Fd0000000E* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 + +pci:v00001C5Fd0000000Esv00001C5Fsd00000B20* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 1920G AIC) + +pci:v00001C5Fd0000000Esv00001C5Fsd00000B21* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 1920G 2.5" U.2) + +pci:v00001C5Fd0000000Esv00001C5Fsd00000B30* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 3840G AIC) + +pci:v00001C5Fd0000000Esv00001C5Fsd00000B31* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 3840G 2.5" U.2) + +pci:v00001C5Fd0000000Esv00001C5Fsd00000B40* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 7680G AIC) + +pci:v00001C5Fd0000000Esv00001C5Fsd00000B41* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 7680G 2.5" U.2) + +pci:v00001C5Fd0000000Esv00001C5Fsd00004B20* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 1600G AIC) + +pci:v00001C5Fd0000000Esv00001C5Fsd00004B21* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 1600G 2.5" U.2) + +pci:v00001C5Fd0000000Esv00001C5Fsd00004B30* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 3200G AIC) + +pci:v00001C5Fd0000000Esv00001C5Fsd00004B31* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 3200G 2.5" U.2) + +pci:v00001C5Fd0000000Esv00001C5Fsd00004B40* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 6400G AIC) + +pci:v00001C5Fd0000000Esv00001C5Fsd00004B41* + ID_MODEL_FROM_DATABASE=PBlaze6 6530 (NVMe SSD PBlaze6 6530 6400G 2.5" U.2) + pci:v00001C5Fd0000003D* ID_MODEL_FROM_DATABASE=PBlaze5 920/926 @@ -69908,6 +70313,9 @@ pci:v00001CB8* pci:v00001CC1* ID_VENDOR_FROM_DATABASE=ADATA Technology Co., Ltd. +pci:v00001CC1d00005766* + ID_MODEL_FROM_DATABASE=ADATA XPG GAMMIXS1 1L Media + pci:v00001CC1d00008201* ID_MODEL_FROM_DATABASE=XPG SX8200 Pro PCIe Gen3x4 M.2 2280 Solid State Drive @@ -69947,6 +70355,9 @@ pci:v00001CC4d00001203sv00001CC4sd0000A214* pci:v00001CC4d000017AB* ID_MODEL_FROM_DATABASE=NVMe 256G SSD device +pci:v00001CC4d00006303* + ID_MODEL_FROM_DATABASE=AM630 PCIe 4.0 x4 NVMe SSD Controller + pci:v00001CC5* ID_VENDOR_FROM_DATABASE=Embedded Intelligence, Inc. @@ -70523,6 +70934,9 @@ pci:v00001D6Cd0000101D* pci:v00001D6Cd0000101E* ID_MODEL_FROM_DATABASE=AR-ARKA-FX1 [Arkville 64B DPDK Data Mover for Agilex R-Tile] +pci:v00001D6Cd0000101F* + ID_MODEL_FROM_DATABASE=AR-TK242 [2x100GbE Packet Capture Device] + pci:v00001D6Cd00004200* ID_MODEL_FROM_DATABASE=A5PL-E1-10GETI [10 GbE Ethernet Traffic Instrument] @@ -71141,6 +71555,12 @@ pci:v00001DE5d00003000* pci:v00001DED* ID_VENDOR_FROM_DATABASE=Alibaba (China) Co., Ltd. +pci:v00001DEDd0000107F* + ID_MODEL_FROM_DATABASE=Elastic RDMA Adapter + +pci:v00001DEDd00005007* + ID_MODEL_FROM_DATABASE=Elastic RDMA Adapter + pci:v00001DEDd00008000* ID_MODEL_FROM_DATABASE=M1 Root Port @@ -71409,7 +71829,7 @@ pci:v00001E0Fd00000007sv00001028sd00002110* ID_MODEL_FROM_DATABASE=NVMe SSD Controller Cx6 (Dell Ent NVMe FIPS CM6 MU 6.4TB) pci:v00001E0Fd00000007sv00001E0Fsd00000001* - ID_MODEL_FROM_DATABASE=NVMe SSD Controller Cx6 (Generic NVMe CM6 RI 3.84TB) + ID_MODEL_FROM_DATABASE=NVMe SSD Controller Cx6 (Generic NVMe CM6) pci:v00001E0Fd00000009* ID_MODEL_FROM_DATABASE=NVMe SSD @@ -71417,6 +71837,39 @@ pci:v00001E0Fd00000009* pci:v00001E0Fd00000009sv00001E0Fsd00000001* ID_MODEL_FROM_DATABASE=NVMe SSD (Toshiba RC500 NVMe SSD 500GB) +pci:v00001E0Fd00000011* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller CD7 + +pci:v00001E0Fd00000011sv00001028sd00002189* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller CD7 (DC NVMe SED CD7 RI 960GB) + +pci:v00001E0Fd00000011sv00001028sd0000218A* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller CD7 (DC NVMe CD7 RI 960GB) + +pci:v00001E0Fd00000011sv00001028sd0000218B* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller CD7 (DC NVMe SED CD7 RI 1.92TB) + +pci:v00001E0Fd00000011sv00001028sd0000218C* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller CD7 (DC NVMe CD7 RI 1.92TB) + +pci:v00001E0Fd00000011sv00001028sd0000218D* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller CD7 (DC NVMe SED CD7 RI 3.84TB) + +pci:v00001E0Fd00000011sv00001028sd0000218E* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller CD7 (DC NVMe CD7 RI 3.84TB) + +pci:v00001E0Fd00000011sv00001028sd0000218F* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller CD7 (DC NVMe SED CD7 RI 7.68TB) + +pci:v00001E0Fd00000011sv00001028sd00002190* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller CD7 (DC NVMe CD7 RI 7.68TB) + +pci:v00001E0Fd00000011sv00001028sd00002191* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller CD7 (DC NVMe SED CD7 RI 15.36TB) + +pci:v00001E0Fd00000011sv00001028sd00002192* + ID_MODEL_FROM_DATABASE=NVMe SSD Controller CD7 (DC NVMe CD7 RI 15.36TB) + pci:v00001E17* ID_VENDOR_FROM_DATABASE=Arnold & Richter Cine Technik GmbH & Co. Betriebs KG @@ -71490,7 +71943,7 @@ pci:v00001E39* ID_VENDOR_FROM_DATABASE=MEDION AG pci:v00001E3B* - ID_VENDOR_FROM_DATABASE=Shenzhen DAPU Microelectronics Co., Ltd + ID_VENDOR_FROM_DATABASE=DapuStor Corporation pci:v00001E3Bd00000600* ID_MODEL_FROM_DATABASE=NVMe SSD Controller DPU600 @@ -71678,6 +72131,9 @@ pci:v00001E3D* pci:v00001E49* ID_VENDOR_FROM_DATABASE=Yangtze Memory Technologies Co.,Ltd +pci:v00001E49d00000041* + ID_MODEL_FROM_DATABASE=ZHITAI TiPro7000 + pci:v00001E49d00001013* ID_MODEL_FROM_DATABASE=PC210 @@ -71726,6 +72182,15 @@ pci:v00001E59* pci:v00001E59d00000001* ID_MODEL_FROM_DATABASE=MinION Mk1C +pci:v00001E5D* + ID_VENDOR_FROM_DATABASE=ASR Microelectronics + +pci:v00001E5Dd00007000* + ID_MODEL_FROM_DATABASE=AI controller A7000 + +pci:v00001E5Dd00007010* + ID_MODEL_FROM_DATABASE=AI controller A7010 + pci:v00001E60* ID_VENDOR_FROM_DATABASE=Hailo Technologies Ltd. @@ -71834,6 +72299,15 @@ pci:v00001EABd0000300A* pci:v00001EABd0000300B* ID_MODEL_FROM_DATABASE=NVMe SSD Controller 300B +pci:v00001EAC* + ID_VENDOR_FROM_DATABASE=Quectel Wireless Solutions Co., Ltd. + +pci:v00001EACd00001001* + ID_MODEL_FROM_DATABASE=EM120R-GL LTE Modem + +pci:v00001EACd00001002* + ID_MODEL_FROM_DATABASE=EM160R-GL LTE Modem + pci:v00001EAE* ID_VENDOR_FROM_DATABASE=XFX Limited @@ -71843,6 +72317,12 @@ pci:v00001EB1* pci:v00001EB1d00001001* ID_MODEL_FROM_DATABASE=Video Accelerator +pci:v00001EB4* + ID_VENDOR_FROM_DATABASE=Quantum Nebula Microelectronics Technology Co.,Ltd. + +pci:v00001EB4d00003401* + ID_MODEL_FROM_DATABASE=SSD Contoller + pci:v00001EBD* ID_VENDOR_FROM_DATABASE=EMERGETECH Company Ltd. @@ -71858,6 +72338,15 @@ pci:v00001ED2d00000000* pci:v00001ED3* ID_VENDOR_FROM_DATABASE=Yeston +pci:v00001ED5* + ID_VENDOR_FROM_DATABASE=Moore Threads Technology Co.,Ltd + +pci:v00001ED5d00000100* + ID_MODEL_FROM_DATABASE=MTT S10 + +pci:v00001ED5d00000101* + ID_MODEL_FROM_DATABASE=MTT S30 + pci:v00001ED8* ID_VENDOR_FROM_DATABASE=Digiteq Automotive @@ -71909,6 +72398,27 @@ pci:v00001F03d00005236* pci:v00001F03d00005636* ID_MODEL_FROM_DATABASE=IG5636-Based NVMe SSD +pci:v00001F2F* + ID_VENDOR_FROM_DATABASE=China Mobile (Hangzhou) Information Technology Co.Ltd. + +pci:v00001F2Fd00001513* + ID_MODEL_FROM_DATABASE=DERA MENG NVMe Controller + +pci:v00001F2Fd00001513sv00001F2Fsd00006113* + ID_MODEL_FROM_DATABASE=DERA MENG NVMe Controller (KM660 U.2 1.6TB NVMe SSD) + +pci:v00001F2Fd00001513sv00001F2Fsd00006114* + ID_MODEL_FROM_DATABASE=DERA MENG NVMe Controller (KM560 U.2 1.92TB NVMe SSD) + +pci:v00001F2Fd00001513sv00001F2Fsd00006115* + ID_MODEL_FROM_DATABASE=DERA MENG NVMe Controller (KM660 U.2 3.2TB NVMe SSD) + +pci:v00001F2Fd00001513sv00001F2Fsd00006116* + ID_MODEL_FROM_DATABASE=DERA MENG NVMe Controller (KM560 U.2 3.84TB NVMe SSD) + +pci:v00001F2Fd00001513sv00001F2Fsd00006118* + ID_MODEL_FROM_DATABASE=DERA MENG NVMe Controller (KM560 U.2 7.68TB NVMe SSD) + pci:v00001FAB* ID_VENDOR_FROM_DATABASE=Unifabrix Ltd. @@ -74114,6 +74624,24 @@ pci:v00006688d00001600* pci:v00006688d00001800* ID_MODEL_FROM_DATABASE=CooVOX TDM BRI Module +pci:v00006766* + ID_VENDOR_FROM_DATABASE=Glenfly Tech Co., Ltd. + +pci:v00006766d00003D00* + ID_MODEL_FROM_DATABASE=Arise-GT-10C0 + +pci:v00006766d00003D02* + ID_MODEL_FROM_DATABASE=Arise 1020 + +pci:v00006766d00003D40* + ID_MODEL_FROM_DATABASE=Arise-GT-10C0 High Definition Audio Controller + +pci:v00006766d00003D41* + ID_MODEL_FROM_DATABASE=Arise 1020 High Definition Audio Controller + +pci:v00006899* + ID_VENDOR_FROM_DATABASE=ZT Systems + pci:v00006900* ID_VENDOR_FROM_DATABASE=Red Hat, Inc. @@ -74147,6 +74675,15 @@ pci:v00007401d0000E100* pci:v00007470* ID_VENDOR_FROM_DATABASE=TP-LINK Technologies Co., Ltd. +pci:v00007526* + ID_VENDOR_FROM_DATABASE=HongQin (Beijing) Technology Co., Ltd. + +pci:v00007526d00000082* + ID_MODEL_FROM_DATABASE=HQ SSD 1TB + +pci:v00007526d00000083* + ID_MODEL_FROM_DATABASE=HQ SSD 2TB M.2 NVMe + pci:v00007604* ID_VENDOR_FROM_DATABASE=O.N. Electronic Co Ltd. @@ -76160,6 +76697,24 @@ pci:v00008086d00000B60sv00001028sd00002103* pci:v00008086d00000B60sv00001028sd00002104* ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Sentinel Rock Controller] (NVMe RI U.2 7.68TB (P5500)) +pci:v00008086d00000B60sv00001028sd0000219A* + ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Sentinel Rock Controller] (NVMe P5316 RI 15.36TB) + +pci:v00008086d00000B60sv00001028sd0000219B* + ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Sentinel Rock Controller] (NVMe P5316 RI 30.72TB) + +pci:v00008086d00000B60sv00001028sd0000219C* + ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Sentinel Rock Controller] (NVMe SED P5316 RI 15.36) + +pci:v00008086d00000B60sv00001028sd0000219D* + ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Sentinel Rock Controller] (NVMe SED P5316 RI 30.72) + +pci:v00008086d00000B60sv00001028sd0000219E* + ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Sentinel Rock Controller] (NVMe FIPS P5316 RI 15.36TB) + +pci:v00008086d00000B60sv00001028sd0000219F* + ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Sentinel Rock Controller] (NVMe FIPS P5316 RI 30.72) + pci:v00008086d00000B60sv00008086sd00008008* ID_MODEL_FROM_DATABASE=NVMe DC SSD [3DNAND, Sentinel Rock Controller] (NVMe Datacenter SSD [3DNAND] SE 2.5" U.2 (P5510)) @@ -76484,12 +77039,33 @@ pci:v00008086d00000D9F* pci:v00008086d00000DD2* ID_MODEL_FROM_DATABASE=Ethernet Network Adapter I710 +pci:v00008086d00000DD2sv00001137sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Network Adapter I710 (I710T4LG 4x1 GbE RJ45 PCIe NIC) + +pci:v00008086d00000DD2sv00001137sd000002E3* + ID_MODEL_FROM_DATABASE=Ethernet Network Adapter I710 (I710T4LG 4x1 GbE RJ45 PCIe NIC) + +pci:v00008086d00000DD2sv00008086sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Network Adapter I710 (-T4L) + pci:v00008086d00000DD2sv00008086sd0000000D* ID_MODEL_FROM_DATABASE=Ethernet Network Adapter I710 (-T4L) pci:v00008086d00000DD2sv00008086sd00000010* ID_MODEL_FROM_DATABASE=Ethernet Network Adapter I710 (-T4L for OCP 3.0) +pci:v00008086d00000DD2sv00008086sd0000401A* + ID_MODEL_FROM_DATABASE=Ethernet Network Adapter I710 (-T4L) + +pci:v00008086d00000DD2sv00008086sd0000401B* + ID_MODEL_FROM_DATABASE=Ethernet Network Adapter I710 (-T4L for OCP 3.0) + +pci:v00008086d00000DDA* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 for 10GbE SFP+ + +pci:v00008086d00000DDAsv00001BD4sd00000076* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 for 10GbE SFP+ + pci:v00008086d00000E00* ID_MODEL_FROM_DATABASE=Xeon E7 v2/Xeon E5 v2/Core i7 DMI2 @@ -80507,6 +81083,9 @@ pci:v00008086d00001592sv00008086sd0000000D* pci:v00008086d00001592sv00008086sd0000000E* ID_MODEL_FROM_DATABASE=Ethernet Controller E810-C for QSFP (Ethernet Network Adapter E810-2C-Q2) +pci:v00008086d00001592sv00008086sd00000010* + ID_MODEL_FROM_DATABASE=Ethernet Controller E810-C for QSFP (Ethernet 100G 2P E810-C-stg Adapter) + pci:v00008086d00001593* ID_MODEL_FROM_DATABASE=Ethernet Controller E810-C for SFP @@ -80543,9 +81122,18 @@ pci:v00008086d00001593sv00008086sd0000000D* pci:v00008086d00001593sv00008086sd0000000E* ID_MODEL_FROM_DATABASE=Ethernet Controller E810-C for SFP (Ethernet Network Adapter E810-XXV-4T) +pci:v00008086d00001593sv00008086sd0000000F* + ID_MODEL_FROM_DATABASE=Ethernet Controller E810-C for SFP (Ethernet 25G 4P E810-XXV-stg Adapter) + +pci:v00008086d00001593sv00008086sd00000010* + ID_MODEL_FROM_DATABASE=Ethernet Controller E810-C for SFP (Ethernet 25G 4P E810-XXV-st Adapter) + pci:v00008086d00001599* ID_MODEL_FROM_DATABASE=Ethernet Controller E810-XXV for backplane +pci:v00008086d00001599sv00008086sd00000001* + ID_MODEL_FROM_DATABASE=Ethernet Controller E810-XXV for backplane (Ethernet 25G 2P E810-XXV-k Mezz) + pci:v00008086d0000159A* ID_MODEL_FROM_DATABASE=Ethernet Controller E810-XXV for QSFP @@ -91634,6 +92222,9 @@ pci:v00008086d00003433* pci:v00008086d00003438* ID_MODEL_FROM_DATABASE=7500/5520/5500/X58 I/O Hub Throttle Registers +pci:v00008086d0000347E* + ID_MODEL_FROM_DATABASE=Ice Lake Xeon Non-Transparent Bridge + pci:v00008086d00003482* ID_MODEL_FROM_DATABASE=Ice Lake-LP LPC Controller @@ -94064,12 +94655,18 @@ pci:v00008086d0000444E* pci:v00008086d0000460D* ID_MODEL_FROM_DATABASE=12th Gen Core Processor PCI Express x16 Controller #1 +pci:v00008086d0000461D* + ID_MODEL_FROM_DATABASE=Alder Lake Innovation Platform Framework Processor Participant + pci:v00008086d0000461E* ID_MODEL_FROM_DATABASE=Alder Lake-P Thunderbolt 4 USB Controller pci:v00008086d0000461F* ID_MODEL_FROM_DATABASE=Alder Lake-P Thunderbolt 4 PCI Express Root Port #3 +pci:v00008086d00004626* + ID_MODEL_FROM_DATABASE=Alder Lake-P Integrated Graphics Controller + pci:v00008086d00004629* ID_MODEL_FROM_DATABASE=12th Gen Core Processor Host Bridge/DRAM Registers @@ -94094,6 +94691,9 @@ pci:v00008086d0000464D* pci:v00008086d0000464F* ID_MODEL_FROM_DATABASE=12th Gen Core Processor Gaussian & Neural Accelerator +pci:v00008086d00004660* + ID_MODEL_FROM_DATABASE=12th Gen Core Processor Host Bridge/DRAM Registers + pci:v00008086d0000466D* ID_MODEL_FROM_DATABASE=Alder Lake-P Thunderbolt 4 NHI #1 @@ -94118,6 +94718,9 @@ pci:v00008086d000046A1* pci:v00008086d000046A3* ID_MODEL_FROM_DATABASE=Alder Lake-P GT1 [UHD Graphics] +pci:v00008086d000046A6* + ID_MODEL_FROM_DATABASE=Alder Lake-P Integrated Graphics Controller + pci:v00008086d000046C0* ID_MODEL_FROM_DATABASE=AlderLake-M GT1 @@ -94152,10 +94755,13 @@ pci:v00008086d00004C9A* ID_MODEL_FROM_DATABASE=RocketLake-S [UHD Graphics] pci:v00008086d00004DA3* - ID_MODEL_FROM_DATABASE=JaserLake SMBus + ID_MODEL_FROM_DATABASE=Jasper Lake SMBus pci:v00008086d00004DA4* - ID_MODEL_FROM_DATABASE=JaserLake SPI (flash) Controller + ID_MODEL_FROM_DATABASE=Jasper Lake SPI Controller + +pci:v00008086d00004DC8* + ID_MODEL_FROM_DATABASE=Jasper Lake HD Audio pci:v00008086d00004DE0* ID_MODEL_FROM_DATABASE=Management Engine Interface @@ -94337,12 +94943,21 @@ pci:v00008086d0000504C* pci:v00008086d00005181* ID_MODEL_FROM_DATABASE=Alder Lake PCH-P LPC/eSPI Controller +pci:v00008086d00005182* + ID_MODEL_FROM_DATABASE=Alder Lake PCH eSPI Controller + pci:v00008086d000051A3* ID_MODEL_FROM_DATABASE=Alder Lake PCH-P SMBus Host Controller pci:v00008086d000051A4* ID_MODEL_FROM_DATABASE=Alder Lake-P PCH SPI Controller +pci:v00008086d000051A8* + ID_MODEL_FROM_DATABASE=Alder Lake PCH UART #0 + +pci:v00008086d000051A9* + ID_MODEL_FROM_DATABASE=Alder Lake PCH UART #1 + pci:v00008086d000051BF* ID_MODEL_FROM_DATABASE=Alder Lake PCH-P PCI Express Root Port #9 @@ -95486,6 +96101,45 @@ pci:v00008086d00007800sv00008086sd00000000* pci:v00008086d00007800sv00008086sd00000100* ID_MODEL_FROM_DATABASE=82740 (i740) AGP Graphics Accelerator (Intel740 Graphics Accelerator) +pci:v00008086d00007A84* + ID_MODEL_FROM_DATABASE=Z690 Chipset LPC/eSPI Controller + +pci:v00008086d00007AA3* + ID_MODEL_FROM_DATABASE=Alder Lake-S PCH SMBus Controller + +pci:v00008086d00007AA4* + ID_MODEL_FROM_DATABASE=Alder Lake-S PCH SPI Controller + +pci:v00008086d00007AA7* + ID_MODEL_FROM_DATABASE=Alder Lake-S PCH Shared SRAM + +pci:v00008086d00007AB4* + ID_MODEL_FROM_DATABASE=Alder Lake-S PCH PCI Express Root Port #13 + +pci:v00008086d00007ABD* + ID_MODEL_FROM_DATABASE=Alder Lake-S PCH PCI Express Root Port #6 + +pci:v00008086d00007ACC* + ID_MODEL_FROM_DATABASE=Alder Lake-S PCH I2C Controller #0 + +pci:v00008086d00007AD0* + ID_MODEL_FROM_DATABASE=Alder Lake-S HD Audio Controller + +pci:v00008086d00007AE0* + ID_MODEL_FROM_DATABASE=Alder Lake-S PCH USB 3.2 Gen 2x2 XHCI Controller + +pci:v00008086d00007AE2* + ID_MODEL_FROM_DATABASE=Alder Lake-S PCH SATA Controller [AHCI Mode] + +pci:v00008086d00007AE8* + ID_MODEL_FROM_DATABASE=Alder Lake-S PCH HECI Controller #1 + +pci:v00008086d00007AF0* + ID_MODEL_FROM_DATABASE=Alder Lake-S PCH CNVi WiFi + +pci:v00008086d00007AF0sv00008086sd00000094* + ID_MODEL_FROM_DATABASE=Alder Lake-S PCH CNVi WiFi (Wi-Fi 6 AX201 160MHz) + pci:v00008086d00008002* ID_MODEL_FROM_DATABASE=Trusted Execution Technology Registers @@ -96593,6 +97247,9 @@ pci:v00008086d00009B63* pci:v00008086d00009B64* ID_MODEL_FROM_DATABASE=10th Gen Core Processor Host Bridge/DRAM Registers +pci:v00008086d00009BA8* + ID_MODEL_FROM_DATABASE=CometLake-S GT1 [UHD Graphics 610] + pci:v00008086d00009BC4* ID_MODEL_FROM_DATABASE=CometLake-H GT2 [UHD Graphics] @@ -97193,6 +97850,9 @@ pci:v00008086d00009D3Dsv0000103Csd00008079* pci:v00008086d00009D3Dsv000017AAsd00002247* ID_MODEL_FROM_DATABASE=Sunrise Point-LP Active Management Technology - SOL (ThinkPad T570) +pci:v00008086d00009D3E* + ID_MODEL_FROM_DATABASE=iTouch Controller + pci:v00008086d00009D43* ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller @@ -97355,6 +98015,9 @@ pci:v00008086d00009DA8* pci:v00008086d00009DAA* ID_MODEL_FROM_DATABASE=Cannon Point-LP Serial IO SPI Controller +pci:v00008086d00009DAB* + ID_MODEL_FROM_DATABASE=Cannon Point-LP Serial IO SPI Controller + pci:v00008086d00009DB0* ID_MODEL_FROM_DATABASE=Cannon Point-LP PCI Express Root Port #9 @@ -97385,6 +98048,9 @@ pci:v00008086d00009DBE* pci:v00008086d00009DBF* ID_MODEL_FROM_DATABASE=Cannon Point PCI Express Root Port #8 +pci:v00008086d00009DC4* + ID_MODEL_FROM_DATABASE=Cannon Point-LP SD Host Controller + pci:v00008086d00009DC5* ID_MODEL_FROM_DATABASE=Cannon Point-LP Serial IO I2C Host Controller @@ -98475,7 +99141,7 @@ pci:v00008086d0000A39A* ID_MODEL_FROM_DATABASE=Comet Lake PCI Express Root Port 11 pci:v00008086d0000A3A1* - ID_MODEL_FROM_DATABASE=Memory controller + ID_MODEL_FROM_DATABASE=Cannon Lake PCH Power Management Controller pci:v00008086d0000A3A3* ID_MODEL_FROM_DATABASE=Comet Lake PCH-V SMBus Host Controller @@ -99923,6 +100589,12 @@ pci:v00009005d0000028Fsv00001BD4sd00000071* pci:v00009005d0000028Fsv00001BD4sd00000072* ID_MODEL_FROM_DATABASE=Smart Storage PQI SAS (RS0800M5E16i) +pci:v00009005d0000028Fsv00001CC4sd00000101* + ID_MODEL_FROM_DATABASE=Smart Storage PQI SAS (Ramaxel FBGF-RAD PM8204) + +pci:v00009005d0000028Fsv00001CC4sd00000201* + ID_MODEL_FROM_DATABASE=Smart Storage PQI SAS (Ramaxel FBGF-RAD PM8222) + pci:v00009005d0000028Fsv00001D49sd00000220* ID_MODEL_FROM_DATABASE=Smart Storage PQI SAS (ThinkSystem 4350-8i SAS/SATA 12Gb HBA) @@ -100754,6 +101426,12 @@ pci:v0000CDDDd00000200* pci:v0000CEBA* ID_VENDOR_FROM_DATABASE=KEBA AG +pci:v0000CF86* + ID_VENDOR_FROM_DATABASE=Spectrum-4TOR + +pci:v0000CF86d00000276* + ID_MODEL_FROM_DATABASE=Spectrum-4TOR in Flash Recovery Mode + pci:v0000D161* ID_VENDOR_FROM_DATABASE=Digium, Inc. diff --git a/hwdb.d/20-usb-vendor-model.hwdb b/hwdb.d/20-usb-vendor-model.hwdb index f40a3947c..4bcb23881 100644 --- a/hwdb.d/20-usb-vendor-model.hwdb +++ b/hwdb.d/20-usb-vendor-model.hwdb @@ -8078,6 +8078,12 @@ usb:v046Dp0846* usb:v046Dp084B* ID_MODEL_FROM_DATABASE=ConferenceCam Connect Video +usb:v046Dp084C* + ID_MODEL_FROM_DATABASE=ConferenceCam Connect Audio + +usb:v046Dp084E* + ID_MODEL_FROM_DATABASE=ConferenceCam Connect + usb:v046Dp0850* ID_MODEL_FROM_DATABASE=QuickCam Web @@ -8235,7 +8241,7 @@ usb:v046Dp08D9* ID_MODEL_FROM_DATABASE=QuickCam IM/Connect usb:v046Dp08DA* - ID_MODEL_FROM_DATABASE=QuickCam Messanger + ID_MODEL_FROM_DATABASE=QuickCam Messenger usb:v046Dp08DD* ID_MODEL_FROM_DATABASE=QuickCam for Notebooks @@ -8652,7 +8658,7 @@ usb:v046DpC06C* ID_MODEL_FROM_DATABASE=Optical Mouse usb:v046DpC077* - ID_MODEL_FROM_DATABASE=M105 Optical Mouse + ID_MODEL_FROM_DATABASE=Mouse usb:v046DpC07C* ID_MODEL_FROM_DATABASE=M-R0017 [G700s Rechargeable Gaming Mouse] @@ -8676,7 +8682,7 @@ usb:v046DpC08B* ID_MODEL_FROM_DATABASE=G502 SE HERO Gaming Mouse usb:v046DpC092* - ID_MODEL_FROM_DATABASE=G203 LIGHTSYNC Gaming Mouse + ID_MODEL_FROM_DATABASE=G102/G203 LIGHTSYNC Gaming Mouse usb:v046DpC101* ID_MODEL_FROM_DATABASE=UltraX Media Remote @@ -9122,6 +9128,9 @@ usb:v046DpC534* usb:v046DpC537* ID_MODEL_FROM_DATABASE=Cordless Mouse Receiver +usb:v046DpC539* + ID_MODEL_FROM_DATABASE=Cordless Mouse Receiver + usb:v046DpC53A* ID_MODEL_FROM_DATABASE=PowerPlay Wireless Charging System @@ -58655,6 +58664,9 @@ usb:v1A86p5523* usb:v1A86p5584* ID_MODEL_FROM_DATABASE=CH341 in parallel mode, usb to printer port converter +usb:v1A86p7522* + ID_MODEL_FROM_DATABASE=CH340 serial converter + usb:v1A86p7523* ID_MODEL_FROM_DATABASE=CH340 serial converter @@ -58665,7 +58677,7 @@ usb:v1A86p7584* ID_MODEL_FROM_DATABASE=CH340S usb:v1A86pE008* - ID_MODEL_FROM_DATABASE=HID-based serial adapater + ID_MODEL_FROM_DATABASE=HID-based serial adapter usb:v1A89* ID_VENDOR_FROM_DATABASE=Dynalith Systems Co., Ltd. @@ -60954,7 +60966,7 @@ usb:v1D50p6054* ID_MODEL_FROM_DATABASE=Satlab/AAUSAT3 BlueBox usb:v1D50p6055* - ID_MODEL_FROM_DATABASE=RADiuS ER900TRS-02 transciever with SMA Connector + ID_MODEL_FROM_DATABASE=RADiuS ER900TRS-02 transceiver with SMA Connector usb:v1D50p6056* ID_MODEL_FROM_DATABASE=The Glitch @@ -61365,16 +61377,16 @@ usb:v1D50p60EE* ID_MODEL_FROM_DATABASE=Duet 3 motion control electronics usb:v1D50p60F0* - ID_MODEL_FROM_DATABASE=UDAD-T1 data aquisition device (boot) + ID_MODEL_FROM_DATABASE=UDAD-T1 data acquisition device (boot) usb:v1D50p60F1* - ID_MODEL_FROM_DATABASE=UDAD-T1 data aquisition device + ID_MODEL_FROM_DATABASE=UDAD-T1 data acquisition device usb:v1D50p60F2* - ID_MODEL_FROM_DATABASE=UDAD-T2 data aquisition device (boot) + ID_MODEL_FROM_DATABASE=UDAD-T2 data acquisition device (boot) usb:v1D50p60F3* - ID_MODEL_FROM_DATABASE=UDAD-T2 data aquisition device + ID_MODEL_FROM_DATABASE=UDAD-T2 data acquisition device usb:v1D50p60F4* ID_MODEL_FROM_DATABASE=Uniti ARC motor controller @@ -61421,6 +61433,9 @@ usb:v1D50p6122* usb:v1D50p614C* ID_MODEL_FROM_DATABASE=dwtk In-Circuit Emulator +usb:v1D50p614D* + ID_MODEL_FROM_DATABASE=Generic Display + usb:v1D50p8085* ID_MODEL_FROM_DATABASE=Box0 (box0-v5) @@ -61473,7 +61488,7 @@ usb:v1D57pAF03* ID_MODEL_FROM_DATABASE=Wireless Receiver usb:v1D57pFA20* - ID_MODEL_FROM_DATABASE=2.4GHz Wireless Reciever (Mini Keyboard & Mouse) + ID_MODEL_FROM_DATABASE=2.4GHz Wireless Receiver (Mini Keyboard & Mouse) usb:v1D5B* ID_VENDOR_FROM_DATABASE=Smartronix, Inc. @@ -62397,7 +62412,22 @@ usb:v1FBD* ID_VENDOR_FROM_DATABASE=Delphin Technology AG usb:v1FBDp0001* - ID_MODEL_FROM_DATABASE=Expert Key - Data aquisition system + ID_MODEL_FROM_DATABASE=Expert Key - Data acquisition system + +usb:v1FBDp0004* + ID_MODEL_FROM_DATABASE=MetiOS Device (RNDIS) + +usb:v1FBDp0005* + ID_MODEL_FROM_DATABASE=Loggito + +usb:v1FBDp0006* + ID_MODEL_FROM_DATABASE=LoggitoLab 8 AI-RTD + +usb:v1FBDp0007* + ID_MODEL_FROM_DATABASE=LoggitoLab 8 TC + +usb:v1FBDp0008* + ID_MODEL_FROM_DATABASE=LoggitoLab 4 AI-RTD 4 TC usb:v1FC9* ID_VENDOR_FROM_DATABASE=NXP Semiconductors @@ -67871,6 +67901,30 @@ usb:v3195pF280* usb:v3195pF281* ID_MODEL_FROM_DATABASE=MSO-28 +usb:v3197* + ID_VENDOR_FROM_DATABASE=Katusha + +usb:v3197p1001* + ID_MODEL_FROM_DATABASE=M151 + +usb:v3197p1002* + ID_MODEL_FROM_DATABASE=M250 + +usb:v3197p1003* + ID_MODEL_FROM_DATABASE=P130 + +usb:v3197p1004* + ID_MODEL_FROM_DATABASE=M130 + +usb:v3197p1101* + ID_MODEL_FROM_DATABASE=P247 + +usb:v3197p1102* + ID_MODEL_FROM_DATABASE=M247 + +usb:v3197p1103* + ID_MODEL_FROM_DATABASE=M348 + usb:v31C9* ID_VENDOR_FROM_DATABASE=BeiJing LanXum Computer Technology Co., Ltd. @@ -69653,9 +69707,24 @@ usb:v8086p07D3* usb:v8086p07DC* ID_MODEL_FROM_DATABASE=Bluetooth 4.0* Smart Ready (low energy) +usb:v8086p0A66* + ID_MODEL_FROM_DATABASE=RealSense 3D Camera (Front F200) + +usb:v8086p0AA5* + ID_MODEL_FROM_DATABASE=RealSense SR300 + +usb:v8086p0AD2* + ID_MODEL_FROM_DATABASE=RealSense D410 + +usb:v8086p0AD3* + ID_MODEL_FROM_DATABASE=RealSense D415 + usb:v8086p0B07* ID_MODEL_FROM_DATABASE=RealSense D435 +usb:v8086p0B64* + ID_MODEL_FROM_DATABASE=RealSense L515 + usb:v8086p0DAD* ID_MODEL_FROM_DATABASE=Cherry MiniatureCard Keyboard @@ -69731,6 +69800,9 @@ usb:v8086p9500* usb:v8086p9890* ID_MODEL_FROM_DATABASE=82930 Test Board +usb:v8086pA36D* + ID_MODEL_FROM_DATABASE=Host Controller + usb:v8086pBEEF* ID_MODEL_FROM_DATABASE=SCM Miniature Card Reader/Writer @@ -69815,9 +69887,6 @@ usb:v8087p8008* usb:v8087p800A* ID_MODEL_FROM_DATABASE=Hub -usb:v8087p8087* - ID_MODEL_FROM_DATABASE=07da Centrino Advanced-N 6235 - usb:v80EE* ID_VENDOR_FROM_DATABASE=VirtualBox diff --git a/hwdb.d/60-autosuspend-fingerprint-reader.hwdb b/hwdb.d/60-autosuspend-fingerprint-reader.hwdb index 91a079374..b44e3e328 100644 --- a/hwdb.d/60-autosuspend-fingerprint-reader.hwdb +++ b/hwdb.d/60-autosuspend-fingerprint-reader.hwdb @@ -148,6 +148,7 @@ usb:v04F3p0C58* # Supported by libfprint driver elanmoc usb:v04F3p0C7D* usb:v04F3p0C7E* +usb:v04F3p0C82* ID_AUTOSUSPEND=1 ID_PERSIST=0 @@ -185,7 +186,6 @@ usb:v06CBp00DF* usb:v06CBp00F9* usb:v06CBp00FC* usb:v06CBp00C2* -usb:v06CBp00C9* usb:v06CBp0100* usb:v06CBp00F0* usb:v06CBp0103* @@ -272,15 +272,19 @@ usb:v06CBp008A* usb:v06CBp009A* usb:v06CBp009B* usb:v06CBp00A2* +usb:v06CBp00A8* usb:v06CBp00B7* usb:v06CBp00BB* usb:v06CBp00BE* usb:v06CBp00C4* usb:v06CBp00CB* +usb:v06CBp00C9* usb:v06CBp00D8* usb:v06CBp00DA* +usb:v06CBp00DC* usb:v06CBp00E7* usb:v06CBp00E9* +usb:v06CBp00FD* usb:v0A5Cp5801* usb:v0A5Cp5805* usb:v0A5Cp5834* @@ -292,6 +296,7 @@ usb:v0A5Cp5844* usb:v0A5Cp5845* usb:v0BDAp5812* usb:v10A5p0007* +usb:v10A5p9200* usb:v1188p9545* usb:v138Ap0007* usb:v138Ap003A* @@ -309,6 +314,7 @@ usb:v1491p0088* usb:v16D1p1027* usb:v1C7Ap0300* usb:v1C7Ap0575* +usb:v1C7Ap0576* usb:v27C6p5042* usb:v27C6p5110* usb:v27C6p5117* @@ -328,7 +334,9 @@ usb:v27C6p55A2* usb:v27C6p55A4* usb:v27C6p55B4* usb:v27C6p5740* +usb:v27C6p5E0A* usb:v2808p9338* +usb:v298Dp2020* usb:v298Dp2033* usb:v3538p0930* ID_AUTOSUSPEND=1 diff --git a/hwdb.d/60-evdev.hwdb b/hwdb.d/60-evdev.hwdb index f896dde35..9fcb4a3dd 100644 --- a/hwdb.d/60-evdev.hwdb +++ b/hwdb.d/60-evdev.hwdb @@ -591,6 +591,24 @@ evdev:name:MSFT0001:02 04F3:304B Touchpad:dmi:*svnLENOVO:*pvrLenovoLegionY9000X2 EVDEV_ABS_35=::31 EVDEV_ABS_36=::30 +######################################### +# Microsoft +######################################### + +# Surface Laptop 2 (13") +evdev:name:Microsoft Surface 045E:0933 Touchpad:dmi:*svnMicrosoftCorporation:*pnSurfaceLaptop2** + EVDEV_ABS_00=::38 + EVDEV_ABS_01=::38 + EVDEV_ABS_35=::38 + EVDEV_ABS_36=::38 + +# Surface Laptop 3 (15") +evdev:name:Microsoft Surface 045E:09AF Touchpad:dmi:*svnMicrosoftCorporation:*pnSurfaceLaptop3** + EVDEV_ABS_00=::39 + EVDEV_ABS_01=::37 + EVDEV_ABS_35=::39 + EVDEV_ABS_36=::37 + ######################################### # NEWYES ######################################### diff --git a/hwdb.d/60-input-id.hwdb b/hwdb.d/60-input-id.hwdb index c4101cc2a..2d5681dea 100644 --- a/hwdb.d/60-input-id.hwdb +++ b/hwdb.d/60-input-id.hwdb @@ -70,4 +70,13 @@ id-input:modalias:input:b0005v046DpB00De0700* # Logitech MX Keys id-input:modalias:input:b0003v046Dp408Ae0111* - ID_INPUT_MOUSE=0 + ID_INPUT_MOUSE=0 + +# Logitech Craft Keyboard +id-input:modalias:input:b0003v046Dp4066e0111* + ID_INPUT_MOUSE=0 + +# CH Products Pro Pedals +id-input:modalias:input:b0003v068Ep00F2e0100* + ID_INPUT_ACCELEROMETER=0 + ID_INPUT_JOYSTICK=1 diff --git a/hwdb.d/60-keyboard.hwdb b/hwdb.d/60-keyboard.hwdb index b614a22c8..c196a1fd6 100644 --- a/hwdb.d/60-keyboard.hwdb +++ b/hwdb.d/60-keyboard.hwdb @@ -164,6 +164,12 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnPredator*PH*315-52:* KEYBOARD_KEY_ef=kbdillumup # Fn+F10 KEYBOARD_KEY_f0=kbdillumdown # Fn+F9 +# Travelmate B311-31, B311R-31, B311RN-31 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnTravelMateB311-31*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnTravelMateB311R-31*:pvr* +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnTravelMateB311RN-31*:pvr* + KEYBOARD_KEY_8a=f20 # Microphone mute + # Travelmate C300 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnTravelMate*C3[01]0*:* KEYBOARD_KEY_67=f24 # FIXME: rotate screen @@ -231,6 +237,14 @@ evdev:name:Asus Laptop extra buttons:dmi:bvn*:bvr*:bd*:svnASUS*:pn*:* evdev:input:b0003v0B05p1869* KEYBOARD_KEY_ff31007c=f20 # Remap micmute to f20 +# Asus TF103C misses the home button in its PNP0C40 GPIO resources +# causing the volume-button mappings to be off by one, correct this +evdev:name:gpio-keys:phys:gpio-keys/input0:ev:3:dmi:*:svnASUSTeKCOMPUTERINC.:pnTF103C*:* + KEYBOARD_KEY_1=volumeup + +evdev:name:gpio-keys:phys:gpio-keys/input0:ev:100003:dmi:*:svnASUSTeKCOMPUTERINC.:pnTF103C*:* + KEYBOARD_KEY_0=volumedown + ########################################################### # BenQ ########################################################### @@ -588,6 +602,18 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*G60*Notebook*PC:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*2570p*:* KEYBOARD_KEY_f8=wlan # Wireless HW switch button +# Elitebook 2760p +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*2760p*:* + KEYBOARD_KEY_89=battery # Fn+F8 + KEYBOARD_KEY_f8=unknown # rfkill is also reported by HP Wireless hotkeys + KEYBOARD_KEY_86=volumeup + KEYBOARD_KEY_87=volumedown + KEYBOARD_KEY_92=brightnessdown + KEYBOARD_KEY_97=brightnessup + KEYBOARD_KEY_d8=!f23 # touchpad off + KEYBOARD_KEY_d9=!f22 # touchpad on + KEYBOARD_KEY_b3=unknown # FIXME: Auto brightness + # TX2 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[tT][xX]2*:* KEYBOARD_KEY_c2=media @@ -625,6 +651,8 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPLicrice:* # HP EliteBook evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHPEliteBook*:* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPEliteBook*:* +# HP Elite x360 +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPElite*x360*:* # HP Elite Dragonfly evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPEliteDragonfly*:* # HP ProBook 440 G2 @@ -711,8 +739,8 @@ evdev:name:gpio-keys:phys:gpio-keys/input0:ev:3:dmi:*:svnHewlett-Packard:pnHPStr evdev:name:gpio-keys:phys:gpio-keys/input0:ev:23:dmi:*:svnHewlett-Packard:pnHPStream7Tablet:* KEYBOARD_KEY_0=unknown -# HP Omen 15 -evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP:pnOMENLaptop15*:pvr* +# HP Omen +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP:pnOMEN*:pvr* KEYBOARD_KEY_a1=!calc ########################################################## @@ -1337,6 +1365,17 @@ evdev:name:MSI Laptop hotkeys:dmi:bvn*:bvr*:bd*:svn*:pnM[iI][cC][rR][oO]-S[tT][a KEYBOARD_KEY_0213=f22 KEYBOARD_KEY_0214=f23 +########################################## +# NEC +########################################## + +# VersaPro VG-S +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnNEC:pnPC-VK22TGSGS:pvr* + KEYBOARD_KEY_a8=f21 # Fn+Space touchpad toggle + KEYBOARD_KEY_67=brightnessdown # Fn+F7 brightness down + KEYBOARD_KEY_65=brightnessup # Fn+F8 brightness up + KEYBOARD_KEY_71=battery # Fn+F4 ECO + ########################################################### # Olimex ########################################################### @@ -1883,6 +1922,20 @@ evdev:input:b0003v1038p0310* KEYBOARD_KEY_7002f=f11 KEYBOARD_KEY_70046=f6 +########################################################### +# CZC +########################################################### + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnCZC:pnODEON*TPC-10:* + KEYBOARD_KEY_01=leftmeta # Home button held for 2s + KEYBOARD_KEY_db=prog1 # Home button + KEYBOARD_KEY_dd=rfkill # Second button labeled Wi-Fi + +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnViewSonic:pnVPAD10:* + KEYBOARD_KEY_01=leftmeta # Home button held for 2s + KEYBOARD_KEY_db=prog1 # Home button + KEYBOARD_KEY_dd=back # Second button labeled Back + ########################################################### # Other ########################################################### diff --git a/hwdb.d/60-sensor.hwdb b/hwdb.d/60-sensor.hwdb index ab7b1adac..ea278913b 100644 --- a/hwdb.d/60-sensor.hwdb +++ b/hwdb.d/60-sensor.hwdb @@ -426,6 +426,10 @@ sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd03/20/201 sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.11:bd05/25/2017:*svnDefaultstring:pnDefaultstring:pvrDefaultstring:rvnAMICorporation:rnDefaultstring:rvrDefaultstring:cvnDefaultstring:ct3:cvrDefaultstring:* ACCEL_LOCATION=base +# GPD Pocket 3 +sensor:modalias:acpi:MXC6655*:dmi:*:svnGPD:pnG1621-02:* + ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1 + ######################################### # Hometech ######################################## @@ -747,6 +751,10 @@ sensor:modalias:acpi:SMO8500*:dmi:*:svnPEAQ:pnPEAQPMMC1010MD99187:* sensor:modalias:acpi:KIOX000A*:dmi:*:svnPIPO:pnW2S:* ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1 +# Pipo W2Pro +sensor:modalias:acpi:KIOX0008*:dmi:*svnPIPO:pnW2pro:* + ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1 + # Pipo W4 sensor:modalias:acpi:SMO8500*:dmi:*:bvrV8L_WIN32_CHIPHD_*_DX:*:rvnAMICorporation:rnAptioCRB:* ACCEL_MOUNT_MATRIX=0, -1, 0; -1, 0, 0; 0, 0, 1 diff --git a/hwdb.d/70-av-production.hwdb b/hwdb.d/70-av-production.hwdb new file mode 100644 index 000000000..a13d6981e --- /dev/null +++ b/hwdb.d/70-av-production.hwdb @@ -0,0 +1,147 @@ +# This file is part of systemd. +# +# Database for AV production controllers that should be accessible to the seat owner. +# +# This covers DJ tables, music-oriented key pads, and streaming-oriented key pads +# such as Elgato Stream Deck +# +# To add local entries, copy this file to +# /etc/udev/hwdb.d/ +# and add your rules there. To load the new rules execute (as root): +# systemd-hwdb update +# udevadm trigger + +################ +# Ableton +################ +# Push 2 +usb:v2982p1967* + ID_AV_PRODUCTION_CONTROLLER=1 + +################ +# Eks +################ +# Otus +usb:v1157p0300* + ID_AV_PRODUCTION_CONTROLLER=1 + +################ +# Elgato +################ +# Stream Deck Original (gen 1) +usb:v0FD9p0060* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Stream Deck Mini +usb:v0FD9p0063* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Stream Deck XL +usb:v0FD9p006C* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Stream Deck Original (gen 2) +usb:v0FD9p006D* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Stream Deck MK.2 +usb:v0FD9p0080* + ID_AV_PRODUCTION_CONTROLLER=1 + +############################# +# Hercules (Guillemot Corp) +############################# +# DJ Console MP3e2 +usb:v06F8pB105* + ID_AV_PRODUCTION_CONTROLLER=1 + +# DJ Console MP3 LE / Glow +usb:v06F8pB120* + ID_AV_PRODUCTION_CONTROLLER=1 + +# DJ Console Mk2 +usb:v06F8pB100* + ID_AV_PRODUCTION_CONTROLLER=1 + +# DJ Console Mk4 +usb:v06F8pB107* + ID_AV_PRODUCTION_CONTROLLER=1 + +##################### +# Native Instruments +##################### + +# Maschine 2 +usb:v17CCp1140* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Maschine 2 Mikro +usb:v17CCp1110* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Maschine 2 Studio +usb:v17CCp1300* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Maschine Jam +usb:v17CCp1500* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Maschine 3 +usb:v17CCp1600* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol D2 +usb:v17CCp1400* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol F1 +usb:v17CCp1120* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol S2 Mk2 +usb:v17CCp1320* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol S2 Mk3 +usb:v17CCp1710* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol S3 +usb:v17CCp1900* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol S4 Mk2 +usb:v17CCp1310* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol S4 Mk3 +usb:v17CCp1720* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol S5 +usb:v17CCp1420* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol S8 +usb:v17CCp1370* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol X1 Mk2 +usb:v17CCp1220* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol Z1 +usb:v17CCp1210* + ID_AV_PRODUCTION_CONTROLLER=1 + +# Traktor Kontrol Z2 +usb:v17CCp1130* + ID_AV_PRODUCTION_CONTROLLER=1 + +#################### +# Pioneer +#################### +# CDJ 2000 NXS 2 +usb:v2B73p0005* + ID_AV_PRODUCTION_CONTROLLER=1 diff --git a/hwdb.d/70-mouse.hwdb b/hwdb.d/70-mouse.hwdb index 630231245..723d145e3 100644 --- a/hwdb.d/70-mouse.hwdb +++ b/hwdb.d/70-mouse.hwdb @@ -166,6 +166,14 @@ mouse:bluetooth:v05acp030d:name:*:* mouse:usb:v04f2p0963:name:Chicony 2.4G Multimedia Wireless Kit:* MOUSE_DPI=1000@142 +########################################## +# CST +########################################## + +# CST Laser Trackball +mouse:usb:v5332p1400:name:Clearly Superior Technologies. CST Laser Trackball:* + MOUSE_DPI=400@1000 *800@1000 1600@1000 + ########################################## # Dell ########################################## @@ -198,6 +206,17 @@ mouse:usb:v0461p4d46:name:USB Optical Mouse:* mouse:usb:v056ep010d:name:ELECOM TrackBall Mouse HUGE TrackBall:* MOUSE_DPI=500@125 *1000@125 1500@125 +# Elecom DEFT Pro TrackBall (M-DPT1MR) +mouse:usb:v056ep0131:name:ELECOM TrackBall Mouse DEFT Pro TrackBall Mouse:* + MOUSE_DPI=*500 1000 1500 + MOUSE_WHEEL_CLICK_ANGLE=10 + +# Elecom Relacon (M-RT1DR) +mouse:usb:v056ep0155:name:ELECOM ELECOM Relacon:* + ID_INPUT_TRACKBALL=1 + MOUSE_DPI=*500 1000 1500 + MOUSE_WHEEL_CLICK_ANGLE=30 + ########################################## # Fujitsu Siemens ########################################## diff --git a/hwdb.d/70-pda.hwdb b/hwdb.d/70-pda.hwdb new file mode 100644 index 000000000..3fdb4efcd --- /dev/null +++ b/hwdb.d/70-pda.hwdb @@ -0,0 +1,50 @@ +# This file is part of systemd. +# +# Database for handhelds (PDAs, calculators, etc.) that should be accessible +# the seat owner. +# +# Permitted keys: +# Specify if a device is a handheld +# ID_PDA=1|0 + +########################################################### +# HP Inc. +########################################################### +# HP x9G+ Calculator +usb:v03F0p0121* + ID_PDA=1 + +# HP Prime +usb:v03F0p1541* + ID_PDA=1 + +########################################################### +# Texas Instruments +########################################################### +# SilverLink +usb:v0451pE001* + ID_PDA=1 + +# TI-84 Plus DirectLink +usb:v0451pE003* + ID_PDA=1 + +# TI-89 Titanium DirectLink +usb:v0451pE004* + ID_PDA=1 + +# TI-84 Plus Silver Edition DirectLink +usb:v0451pE008* + ID_PDA=1 + +# TI-Nspire DirectLink +usb:v0451pE012* + ID_PDA=1 + +# TI-Nspire Lab Cradle +usb:v0451pE01C* + ID_PDA=1 + +# TI-Nspire CX II DirectLink +usb:v0451pE022* + ID_PDA=1 diff --git a/hwdb.d/acpi_id_registry.html b/hwdb.d/acpi_id_registry.html index 5c37d1ae6..b2a684452 100644 --- a/hwdb.d/acpi_id_registry.html +++ b/hwdb.d/acpi_id_registry.html @@ -110,6 +110,9 @@ Purism SPCPURI06/10/2021 Lontium Semiconductor CorporationLTSC07/21/2021 Wacom TechnologyWACF09/21/2021 + Shanghai Aiwei Electronic Technology Co., Ltd.AWDZ12/31/2021 + Silicom Ltd. Connectivity SolutionsSILC03/28/2022 + NOLO VRNOLO03/28/2022 diff --git a/hwdb.d/ids_parser.py b/hwdb.d/ids_parser.py index 0ce79cd97..811c12559 100755 --- a/hwdb.d/ids_parser.py +++ b/hwdb.d/ids_parser.py @@ -6,7 +6,7 @@ import sys from pyparsing import (Word, White, Literal, Regex, LineEnd, SkipTo, ZeroOrMore, OneOrMore, Combine, Optional, Suppress, - Group, + Group, ParserElement, stringEnd, pythonStyleComment) EOL = LineEnd().suppress() @@ -20,6 +20,8 @@ COMMENTLINE = pythonStyleComment + EOL EMPTYLINE = LineEnd() text_eol = lambda name: Regex(r'[^\n]+')(name) + EOL +ParserElement.set_default_whitespace_chars(' \n') + def klass_grammar(): klass_line = Literal('C ').suppress() + NUM2('klass') + text_eol('text') subclass_line = TAB + NUM2('subclass') + text_eol('text') @@ -35,8 +37,12 @@ def klass_grammar(): def usb_ids_grammar(): vendor_line = NUM4('vendor') + text_eol('text') device_line = TAB + NUM4('device') + text_eol('text') + interface_line = TAB + TAB + NUM4('interface') + NUM4('interface2') + text_eol('text') + device = (device_line + + ZeroOrMore(Group(interface_line) + ^ COMMENTLINE.suppress())) vendor = (vendor_line('VENDOR') + - ZeroOrMore(Group(device_line)('VENDOR_DEV*') ^ COMMENTLINE.suppress())) + ZeroOrMore(Group(device)('VENDOR_DEV*') ^ COMMENTLINE.suppress())) klass = klass_grammar() diff --git a/hwdb.d/ma-large.txt b/hwdb.d/ma-large.txt index 82cbdee33..eb99ea7fb 100644 --- a/hwdb.d/ma-large.txt +++ b/hwdb.d/ma-large.txt @@ -10088,12 +10088,6 @@ AC9A22 (base 16) NXP Semiconductors Hefei Anhui 230088 CN -80-6A-B0 (hex) Shenzhen TINNO Mobile Technology Corp. -806AB0 (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F.,H-3 Building,OCT Eastern lndustrial Park - Nanshan District, Shenzhen GUANGDONG 518053 - CN - 48-AD-08 (hex) HUAWEI TECHNOLOGIES CO.,LTD 48AD08 (base 16) HUAWEI TECHNOLOGIES CO.,LTD No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park @@ -18563,12 +18557,6 @@ D4C766 (base 16) Acentic GmbH Sungnam-Si Kyunggi-Do 463-870 KR -00-26-04 (hex) Audio Processing Technology Ltd -002604 (base 16) Audio Processing Technology Ltd - Whiterock Business Park - Belfast BT12 7FP - GB - 00-26-59 (hex) Nintendo Co., Ltd. 002659 (base 16) Nintendo Co., Ltd. 11-1 HOKOTATE-CHO KAMITOBA, MINAMI-KU @@ -31049,12 +31037,6 @@ D4C766 (base 16) Acentic GmbH GOLDEN CO 80401 US -00-00-BD (hex) Mitsubishi Cable Industries, Ltd. / Ryosei Systems -0000BD (base 16) Mitsubishi Cable Industries, Ltd. / Ryosei Systems - 8, NISHINO-CHO, HIGASHI-MUKOJIMA - AMAGASAKI HYOGO 660-0856 - JP - 00-00-2E (hex) SOCIETE EVIRA 00002E (base 16) SOCIETE EVIRA ZONE PORTUAIRE DE BREGAILLON @@ -34475,12 +34457,6 @@ DCCD74 (base 16) Japan E.M.Solutions Co., Ltd. Kato 673-1447 JP -00-12-93 (hex) ABB Power Protection (CH) -001293 (base 16) ABB Power Protection (CH) - 1501 Roanoke Blvd. - Salem VA 24153 - US - A0-3B-01 (hex) Kyung In Electronics A03B01 (base 16) Kyung In Electronics #1411, Byucksan Digital Valley 2, 184, Gasan Digital2-ro, Geumcheon-gu @@ -34619,12 +34595,6 @@ A0A3F0 (base 16) D-Link International Shenzhen 518000 CN -E0-6C-4E (hex) Shenzhen TINNO Mobile Technology Corp. -E06C4E (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - F0-25-8E (hex) HUAWEI TECHNOLOGIES CO.,LTD F0258E (base 16) HUAWEI TECHNOLOGIES CO.,LTD No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park @@ -35279,12 +35249,6 @@ C87B23 (base 16) Bose Corporation Moulineaux 92370 FR -30-8E-7A (hex) Shenzhen iComm Semiconductor CO.,LTD -308E7A (base 16) Shenzhen iComm Semiconductor CO.,LTD - Room 504A,Block B,Digital Building,Gargen City,No.1079,Nanhai Road,Nanshan District,Shenzhen. - Shenzhen 518067 - CN - 9C-1C-37 (hex) AltoBeam (China) Inc. 9C1C37 (base 16) AltoBeam (China) Inc. B808, Tsinghua Tongfang Hi-Tech Plaza, Haidian @@ -37232,12 +37196,6 @@ A41752 (base 16) Hifocus Electronics India Private Limited Chennai Tamil Nadu 600002 IN -2C-DD-5F (hex) Shenzhen iComm Semiconductor CO.,LTD -2CDD5F (base 16) Shenzhen iComm Semiconductor CO.,LTD - Room 504A,Block B,Digital Building,Gargen City,No.1079,Nanhai Road,Nanshan District,Shenzhen. - Shenzhen 518067 - CN - 40-FE-95 (hex) New H3C Technologies Co., Ltd 40FE95 (base 16) New H3C Technologies Co., Ltd 466 Changhe Road, Binjiang District @@ -37496,12 +37454,6 @@ AC567B (base 16) Sunnovo International Limited Beijing Beijing 100083 CN -A0-B4-BF (hex) InfiNet LLC -A0B4BF (base 16) InfiNet LLC - office 11, 24 S.Deryabinoy st. - Yekaterinburg Sverdlovsk region 620102 - RU - 9C-C1-2D (hex) GD Midea Air-Conditioning Equipment Co.,Ltd. 9CC12D (base 16) GD Midea Air-Conditioning Equipment Co.,Ltd. Midea Global Innovation Center,Beijiao Town,Shunde @@ -37526,36 +37478,6 @@ DC8084 (base 16) Apple, Inc. Ernakulam KL 686662 IN -B0-5C-16 (hex) Fiberhome Telecommunication Technologies Co.,LTD -B05C16 (base 16) Fiberhome Telecommunication Technologies Co.,LTD - No.5 DongXin Road - Wuhan Hubei 430074 - CN - -08-E0-21 (hex) Honor Device Co., Ltd. -08E021 (base 16) Honor Device Co., Ltd. - Suite 3401, Unit A, Building 6, Shum Yip Sky Park, No. 8089, Hongli West Road, Xiangmihu Street, Futian District - Shenzhen Guangdong 518040 - CN - -A8-53-7D (hex) Mist Systems, Inc. -A8537D (base 16) Mist Systems, Inc. - 1601 South De Anza Blvd, Suite 248 - Cupertino CA 95014 - US - -30-34-22 (hex) eero inc. -303422 (base 16) eero inc. - 660 3rd Street - San Francisco CA 94107 - US - -E8-D3-22 (hex) Cisco Systems, Inc -E8D322 (base 16) Cisco Systems, Inc - 80 West Tasman Drive - San Jose CA 94568 - US - E4-DA-DF (hex) Taicang T&W Electronics E4DADF (base 16) Taicang T&W Electronics 89# Jiang Nan RD @@ -37586,6 +37508,669 @@ C85895 (base 16) Motorola Mobility LLC, a Lenovo Company Chicago IL 60654 US +A8-53-7D (hex) Mist Systems, Inc. +A8537D (base 16) Mist Systems, Inc. + 1601 South De Anza Blvd, Suite 248 + Cupertino CA 95014 + US + +30-34-22 (hex) eero inc. +303422 (base 16) eero inc. + 660 3rd Street + San Francisco CA 94107 + US + +08-E0-21 (hex) Honor Device Co., Ltd. +08E021 (base 16) Honor Device Co., Ltd. + Suite 3401, Unit A, Building 6, Shum Yip Sky Park, No. 8089, Hongli West Road, Xiangmihu Street, Futian District + Shenzhen Guangdong 518040 + CN + +B0-5C-16 (hex) Fiberhome Telecommunication Technologies Co.,LTD +B05C16 (base 16) Fiberhome Telecommunication Technologies Co.,LTD + No.5 DongXin Road + Wuhan Hubei 430074 + CN + +E8-D3-22 (hex) Cisco Systems, Inc +E8D322 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +30-2B-DC (hex) Top-Unum Electronics Co., LTD +302BDC (base 16) Top-Unum Electronics Co., LTD + No. 58, Ln. 137, Jianshan Rd., Yingge Dist., + New Taipei City 239, Taiwan 239 + CN + +8C-15-53 (hex) Beijing Memblaze Technology Co Ltd +8C1553 (base 16) Beijing Memblaze Technology Co Ltd + Building B2,Dongsheng Park, 66 Xixiaokou Road, Haidian + Beijing Beijing 100192 + CN + +D4-BD-4F (hex) Ruckus Wireless +D4BD4F (base 16) Ruckus Wireless + 350 West Java Drive + Sunnyvale CA 94089 + US + +5C-C9-C0 (hex) Renesas Electronics (Penang) Sdn. Bhd. +5CC9C0 (base 16) Renesas Electronics (Penang) Sdn. Bhd. + Phase 3, Bayan Lepas FIZ + Bayan Lepas Penang 11900 + MY + +6C-B1-58 (hex) TP-LINK TECHNOLOGIES CO.,LTD. +6CB158 (base 16) TP-LINK TECHNOLOGIES CO.,LTD. + Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan + Shenzhen Guangdong 518057 + CN + +E8-81-AB (hex) Beijing Sankuai Online Technology Co.,Ltd +E881AB (base 16) Beijing Sankuai Online Technology Co.,Ltd + BC Building, China Electronic Science Taiji Information Technology Industry Base, Yard 7, Rongda Road, Chaoyang District + Beijing 100102 + CN + +1C-47-F6 (hex) Zhidao Network Technology(Shenzhen) Co.,Ltd +1C47F6 (base 16) Zhidao Network Technology(Shenzhen) Co.,Ltd + B3, 11 / F, Exiang Technology Building, No. 31, Zhongsi Road, Gaoxin, Maling Community, Yuehai Street, Nanshan District + Shenzhen 518000 + CN + +A0-B4-BF (hex) InfiNet LLC +A0B4BF (base 16) InfiNet LLC + Office 425, 69/75 Vavilova str. + Moscow\ 117335 + RU + +E8-EB-D3 (hex) Mellanox Technologies, Inc. +E8EBD3 (base 16) Mellanox Technologies, Inc. + 350 Oakmead Parkway, Suite 100 + Sunnyvale CA 94085 + US + +C0-06-0C (hex) HUAWEI TECHNOLOGIES CO.,LTD +C0060C (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +B0-A4-F0 (hex) HUAWEI TECHNOLOGIES CO.,LTD +B0A4F0 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +24-75-3A (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +24753A (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + NO.18 HAIBIN ROAD, + DONG GUAN GUANG DONG 523860 + CN + +90-F7-B2 (hex) New H3C Technologies Co., Ltd +90F7B2 (base 16) New H3C Technologies Co., Ltd + 466 Changhe Road, Binjiang District + Hangzhou Zhejiang 310052 + CN + +04-E3-1A (hex) Sagemcom Broadband SAS +04E31A (base 16) Sagemcom Broadband SAS + 250, route de l'Empereur + Rueil Malmaison Cedex hauts de seine 92848 + FR + +6C-99-9D (hex) Amazon Technologies Inc. +6C999D (base 16) Amazon Technologies Inc. + P.O Box 8102 + Reno NV 89507 + US + +7C-6A-60 (hex) China Mobile Group Device Co.,Ltd. +7C6A60 (base 16) China Mobile Group Device Co.,Ltd. + 32 Xuanwumen West Street,Xicheng District + Beijing 100053 + CN + +68-18-D9 (hex) Hill AFB - CAPRE Group +6818D9 (base 16) Hill AFB - CAPRE Group + 7278 4th Street + Hill AFB UT 84056 + US + +18-BC-57 (hex) ADVA Optical Networking Ltd. +18BC57 (base 16) ADVA Optical Networking Ltd. + ADVAntage House + York YO30 4RY + GB + +9C-A2-F4 (hex) TP-Link Corporation Limited +9CA2F4 (base 16) TP-Link Corporation Limited + Room 901,9/F.New East Ocean Centre, 9 Science Museum Road + Tsim Sha Tsui Kowloon 999077 + HK + +1C-61-B4 (hex) TP-Link Corporation Limited +1C61B4 (base 16) TP-Link Corporation Limited + Room 901,9/F.New East Ocean Centre, 9 Science Museum Road + Tsim Sha Tsui Kowloon 999077 + HK + +B4-69-5F (hex) TCT mobile ltd +B4695F (base 16) TCT mobile ltd + No.86 hechang 7th road, zhongkai, Hi-Tech District + Hui Zhou Guang Dong 516006 + CN + +D8-E2-DF (hex) Microsoft Corporation +D8E2DF (base 16) Microsoft Corporation + One Microsoft Way + REDMOND WA 98052 + US + +6C-93-08 (hex) IEEE Registration Authority +6C9308 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +38-8F-30 (hex) Samsung Electronics Co.,Ltd +388F30 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +24-06-F2 (hex) Sichuan Tianyi Comheart Telecom Co.,LTD +2406F2 (base 16) Sichuan Tianyi Comheart Telecom Co.,LTD + No.198,First Section,Snow Mountain Avenue, Jinyuan Town, Dayi County + Chengdu Sichuan 611330 + CN + +84-C6-92 (hex) Texas Instruments +84C692 (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +6C-B2-FD (hex) Texas Instruments +6CB2FD (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +6C-0F-61 (hex) Hypervolt Ltd +6C0F61 (base 16) Hypervolt Ltd + 25 Churchill Place + London E14 5EY + GB + +A0-44-66 (hex) Intellics +A04466 (base 16) Intellics + 697, Pangyo-ro, Bundang-gu + Seongnam-si Gyeonggi-do 13511 + KR + +CC-66-18 (hex) Adtran Inc +CC6618 (base 16) Adtran Inc + 901 Explorer Blvd. + Huntsville AL 35806-2807 + US + +C0-C1-70 (hex) Shenzhen SuperElectron Technology Co.,Ltd. +C0C170 (base 16) Shenzhen SuperElectron Technology Co.,Ltd. + 1213-1214, haosheng business center, dongbin road, nanshan street, nanshan district, shenzhen city + Shenzhen Guangdong 518000 + CN + +50-42-89 (hex) zte corporation +504289 (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +30-8E-7A (hex) Shenzhen iComm Semiconductor CO.,LTD +308E7A (base 16) Shenzhen iComm Semiconductor CO.,LTD + Room 601,Block B ,Digital Building,Garden City + Shenzhen No.1079 Nanhai Road,Nanshan District 518067 + CN + +2C-DD-5F (hex) Shenzhen iComm Semiconductor CO.,LTD +2CDD5F (base 16) Shenzhen iComm Semiconductor CO.,LTD + Room 601,Block B ,Digital Building,Garden City + Shenzhen No.1079 Nanhai Road,Nanshan District 518067 + CN + +C0-6D-ED (hex) Hangzhou Hikvision Digital Technology Co.,Ltd. +C06DED (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd. + No.555 Qianmo Road + Hangzhou Zhejiang 310052 + CN + +44-6D-7F (hex) Amazon Technologies Inc. +446D7F (base 16) Amazon Technologies Inc. + P.O Box 8102 + Reno 89507 + US + +E0-27-6C (hex) Guangzhou Shiyuan Electronic Technology Company Limited +E0276C (base 16) Guangzhou Shiyuan Electronic Technology Company Limited + No.6, 4th Yunpu Road, Yunpu industry District + Guangzhou Guangdong 510530 + CN + +90-93-5A (hex) ARRIS Group, Inc. +90935A (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +AC-8F-A9 (hex) Nokia Solutions and Networks GmbH & Co. KG +AC8FA9 (base 16) Nokia Solutions and Networks GmbH & Co. KG + Werinherstrasse 91 + München Bavaria D-81541 + DE + +4C-73-4F (hex) Juniper Networks +4C734F (base 16) Juniper Networks + 1133 Innovation Way + Sunnyvale CA 94089 + US + +24-EB-ED (hex) HUAWEI TECHNOLOGIES CO.,LTD +24EBED (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +AC-51-AB (hex) HUAWEI TECHNOLOGIES CO.,LTD +AC51AB (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +48-CD-D3 (hex) HUAWEI TECHNOLOGIES CO.,LTD +48CDD3 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +F8-AD-24 (hex) Realme Chongqing Mobile Telecommunications Corp.,Ltd. +F8AD24 (base 16) Realme Chongqing Mobile Telecommunications Corp.,Ltd. + No.178 Yulong Avenue, Yufengshan, Yubei District, Chongqing. + Chongqing China 401120 + CN + +A8-C9-8A (hex) New H3C Technologies Co., Ltd +A8C98A (base 16) New H3C Technologies Co., Ltd + 466 Changhe Road, Binjiang District + Hangzhou Zhejiang 310052 + CN + +44-29-1E (hex) AltoBeam (China) Inc. +44291E (base 16) AltoBeam (China) Inc. + B808, Tsinghua Tongfang Hi-Tech Plaza, Haidian + Beijing Beijing 100083 + CN + +DC-8E-95 (hex) Silicon Laboratories +DC8E95 (base 16) Silicon Laboratories + 400 West Cesar Chavez Street + Austin TX 78701 + US + +7C-EF-40 (hex) Nextorage Corporation +7CEF40 (base 16) Nextorage Corporation + Kawasaki-eki-mae Tower Riverk 9F, 12-1, Ekimaehoncho, Kawasaki-ku + Kawasaki City Kanagawa 210-0007 + JP + +28-BE-43 (hex) vivo Mobile Communication Co., Ltd. +28BE43 (base 16) vivo Mobile Communication Co., Ltd. + No.1, vivo Road, Chang'an + Dongguan Guangdong 523860 + CN + +2C-FC-8B (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +2CFC8B (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + NO.18 HAIBIN ROAD, + DONG GUAN GUANG DONG 523860 + CN + +28-74-F5 (hex) Nokia Solutions and Networks GmbH & Co. KG +2874F5 (base 16) Nokia Solutions and Networks GmbH & Co. KG + Werinherstrasse 91 + München Bavaria D-81541 + DE + +B0-1F-8C (hex) Aruba, a Hewlett Packard Enterprise Company +B01F8C (base 16) Aruba, a Hewlett Packard Enterprise Company + 3333 Scott Blvd + Santa Clara CA 95054 + US + +C0-E0-1C (hex) IoT Security Group, SL +C0E01C (base 16) IoT Security Group, SL + Calle Pez Dorado, 27, local 2 + Torremolinos Malaga 29620 + ES + +00-26-04 (hex) WorldCast Systems +002604 (base 16) WorldCast Systems + 20 Avenue Neil Armstrong + Mérignac 33700 + FR + +00-CB-7A (hex) Technicolor CH USA Inc. +00CB7A (base 16) Technicolor CH USA Inc. + 5030 Sugarloaf Parkway Bldg 6 + Lawrenceville GA 30044 + US + +F8-AB-82 (hex) Xiaomi Communications Co Ltd +F8AB82 (base 16) Xiaomi Communications Co Ltd + #019, 9th Floor, Building 6, 33 Xi'erqi Middle Road + Beijing Haidian District 100085 + CN + +EC-30-B3 (hex) Xiaomi Communications Co Ltd +EC30B3 (base 16) Xiaomi Communications Co Ltd + #019, 9th Floor, Building 6, 33 Xi'erqi Middle Road + Beijing Haidian District 100085 + CN + +1C-AF-4A (hex) Samsung Electronics Co.,Ltd +1CAF4A (base 16) Samsung Electronics Co.,Ltd + 129, Samsung-ro, Youngtongl-Gu + Suwon Gyeonggi-Do 16677 + KR + +C8-12-0B (hex) Samsung Electronics Co.,Ltd +C8120B (base 16) Samsung Electronics Co.,Ltd + 129, Samsung-ro, Youngtongl-Gu + Suwon Gyeonggi-Do 16677 + KR + +90-2C-FB (hex) CanTops Co,.Ltd. +902CFB (base 16) CanTops Co,.Ltd. + A-1002 Digital Empire, 16, Deogyong-daero 1556beon-gil + Yeongtong-gu Suwon-si, Gyonggi-do 1660 + KR + +A8-A2-37 (hex) Arcadyan Corporation +A8A237 (base 16) Arcadyan Corporation + No.8, Sec.2, Guangfu Rd. + Hsinchu City Hsinchu 30071 + TW + +00-00-BD (hex) RYOSEI, Ltd. +0000BD (base 16) RYOSEI, Ltd. + 16-4, kitahatsushima-cho + Amagasaki-shi Hyogo 660-0834 + JP + +AC-CC-FC (hex) Amazon Technologies Inc. +ACCCFC (base 16) Amazon Technologies Inc. + P.O Box 8102 + Reno NV 89507 + US + +08-E6-3B (hex) zte corporation +08E63B (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +88-C1-74 (hex) zte corporation +88C174 (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +C8-9E-61 (hex) Lyngsoe Systems LTd +C89E61 (base 16) Lyngsoe Systems LTd + 101 Simona Dr., Unit 2 + Bolton Ontario L7E 4E8 + CA + +9C-57-BC (hex) eero inc. +9C57BC (base 16) eero inc. + 660 3rd Street + San Francisco CA 94107 + US + +2C-82-17 (hex) Apple, Inc. +2C8217 (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +14-2D-4D (hex) Apple, Inc. +142D4D (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +EC-42-CC (hex) Apple, Inc. +EC42CC (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +B8-21-1C (hex) Apple, Inc. +B8211C (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +D4-5A-3F (hex) Juniper Networks +D45A3F (base 16) Juniper Networks + 1133 Innovation Way + Sunnyvale CA 94089 + US + +E8-DC-6C (hex) Cisco Systems, Inc +E8DC6C (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +48-B4-C3 (hex) Aruba, a Hewlett Packard Enterprise Company +48B4C3 (base 16) Aruba, a Hewlett Packard Enterprise Company + 3333 Scott Blvd + Santa Clara CA 95054 + US + +B0-3F-64 (hex) Apple, Inc. +B03F64 (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +68-A7-B4 (hex) Honor Device Co., Ltd. +68A7B4 (base 16) Honor Device Co., Ltd. + Suite 3401, Unit A, Building 6, Shum Yip Sky Park, No. 8089, Hongli West Road, Xiangmihu Street, Futian District + Shenzhen Guangdong 518040 + CN + +80-3C-20 (hex) HUAWEI TECHNOLOGIES CO.,LTD +803C20 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +A4-DD-58 (hex) HUAWEI TECHNOLOGIES CO.,LTD +A4DD58 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +E0-28-B1 (hex) Shenzhen Skyworth Digital Technology CO., Ltd +E028B1 (base 16) Shenzhen Skyworth Digital Technology CO., Ltd + 4F,Block A, Skyworth?Building, + Shenzhen Guangdong 518057 + CN + +C0-8D-51 (hex) Amazon Technologies Inc. +C08D51 (base 16) Amazon Technologies Inc. + P.O Box 8102 + Reno NV 89507 + US + +44-B4-B2 (hex) Amazon Technologies Inc. +44B4B2 (base 16) Amazon Technologies Inc. + P.O Box 8102 + Reno NV 89507 + US + +6C-15-24 (hex) IEEE Registration Authority +6C1524 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +78-03-4F (hex) Nokia +78034F (base 16) Nokia + 600 March Road + Kanata Ontario K2K 2E6 + CA + +00-12-93 (hex) ABB Switzerland Ltd. +001293 (base 16) ABB Switzerland Ltd. + Via Luserte Sud 9 Quartino + Quartino 6572 + CH + +C0-E9-11 (hex) Private +C0E911 (base 16) Private + +14-F5-92 (hex) Shenzhen SDG DONZHI Technology Co., Ltd +14F592 (base 16) Shenzhen SDG DONZHI Technology Co., Ltd + 1001 SDG Information Technology Building, No.2 Qiongyu Road, Science park Community, Yuehai Street, Nanshan District, + Shenzhen GuangDong 518000 + CN + +4C-09-FA (hex) FRONTIER SMART TECHNOLOGIES LTD +4C09FA (base 16) FRONTIER SMART TECHNOLOGIES LTD + 17 Waterloo Place + London SW1Y 4AR + GB + +24-2C-FE (hex) Zhejiang Tmall Technology Co., Ltd. +242CFE (base 16) Zhejiang Tmall Technology Co., Ltd. + No.969 Wenyi West Road, Wuchang Street, Yuhang District + Hangzhou Zhejiang 310024 + CN + +A0-42-D1 (hex) Huawei Device Co., Ltd. +A042D1 (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +2C-DC-78 (hex) Descartes Systems (USA) LLC +2CDC78 (base 16) Descartes Systems (USA) LLC + 2030 Powers Ferry Road SE + Atlanta GA 303339 + US + +58-87-9F (hex) Huawei Device Co., Ltd. +58879F (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +E8-D8-7E (hex) Amazon Technologies Inc. +E8D87E (base 16) Amazon Technologies Inc. + P.O Box 8102 + Reno NV 89507 + US + +9C-1F-CA (hex) Hangzhou AlmightyDigit Technology Co., Ltd +9C1FCA (base 16) Hangzhou AlmightyDigit Technology Co., Ltd + Room A0041, 10 / F, building 1, Haizhi center, Cangqian street, Yuhang District + Hangzhou Zhejiang 310000 + CN + +84-70-D7 (hex) eero inc. +8470D7 (base 16) eero inc. + 660 3rd Street + San Francisco CA 94107 + US + +E0-6C-4E (hex) Shenzhen TINNO Mobile Technology Corp. +E06C4E (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +58-1D-D8 (hex) Sagemcom Broadband SAS +581DD8 (base 16) Sagemcom Broadband SAS + 250, route de l'Empereur + Rueil Malmaison Cedex hauts de seine 92848 + FR + +80-6A-B0 (hex) Shenzhen TINNO Mobile Technology Corp. +806AB0 (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +F4-B3-B1 (hex) Silicon Laboratories +F4B3B1 (base 16) Silicon Laboratories + 400 West Cesar Chavez Street + Austin TX 78701 + US + +04-69-8F (hex) Juniper Networks +04698F (base 16) Juniper Networks + 1133 Innovation Way + Sunnyvale CA 94089 + US + +14-9B-F3 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +149BF3 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + NO.18 HAIBIN ROAD, + DONG GUAN GUANG DONG 523860 + CN + +10-07-1D (hex) Fiberhome Telecommunication Technologies Co.,LTD +10071D (base 16) Fiberhome Telecommunication Technologies Co.,LTD + No.5 DongXin Road + Wuhan Hubei 430074 + CN + +10-B2-32 (hex) Qingdao Intelligent&Precise Electronics Co.,Ltd. +10B232 (base 16) Qingdao Intelligent&Precise Electronics Co.,Ltd. + No.218 Qianwangang Road + Qingdao Shangdong 266510 + CN + +B8-50-D8 (hex) Beijing Xiaomi Mobile Software Co., Ltd +B850D8 (base 16) Beijing Xiaomi Mobile Software Co., Ltd + The Rainbow City Office Building, 68 Qinghe Middle Street Haidian District + Beijing Beijing 100085 + CN + +C0-9F-51 (hex) SERNET (SUZHOU) TECHNOLOGIES CORPORATION +C09F51 (base 16) SERNET (SUZHOU) TECHNOLOGIES CORPORATION + NO.8 Tangzhuang Road,Suzhou Industrial Park,Su ZhouCity,JiangSu Province,China + Suzhou 215021 + CN + +80-02-F4 (hex) IEEE Registration Authority +8002F4 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +5C-53-C3 (hex) Ubee Interactive Co., Limited +5C53C3 (base 16) Ubee Interactive Co., Limited + Flat/RM 1202, 12/F, AT Tower, 180 Electric Road + North Point 00000 + HK + 9C-FF-C2 (hex) AVI Systems GmbH 9CFFC2 (base 16) AVI Systems GmbH Dr. Franz Wilhelmstraße 2A @@ -39044,12 +39629,6 @@ BC3E07 (base 16) Hitron Technologies. Inc Gyeonggi-do KSXX0024 KR -0C-EC-84 (hex) Shenzhen TINNO Mobile Technology Corp. -0CEC84 (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - 9C-DB-07 (hex) Thum+Mahr GmbH 9CDB07 (base 16) Thum+Mahr GmbH Heinrich-Hertz-Strasse 1-3 @@ -44579,12 +45158,6 @@ E47B3F (base 16) BEIJING CO-CLOUD TECHNOLOGY LTD. Shanghai Shanghai 201114 CN -00-18-48 (hex) Vecima Networks Inc. -001848 (base 16) Vecima Networks Inc. - 150 Cardinal Place - Saskatoon SK S7L 6H7 - CA - 00-16-FB (hex) SHENZHEN MTC CO LTD 0016FB (base 16) SHENZHEN MTC CO LTD 5/F BenYuan Bldg,6015 ShenNan Road @@ -46655,12 +47228,6 @@ DCC0EB (base 16) ASSA ABLOY CÔTE PICARDE Cupertino CA 95014 US -A0-F8-95 (hex) Shenzhen TINNO Mobile Technology Corp. -A0F895 (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F.,H-3 Building,OCT Eastern lndustrial Park. - Nanshan District, Shenzhen GUANGDONG 518053 - CN - 00-78-CD (hex) Ignition Design Labs 0078CD (base 16) Ignition Design Labs 1550 Technology Drive @@ -50480,12 +51047,6 @@ B8F828 (base 16) Changshu Gaoshida Optoelectronic Technology Co. Ltd. Glockengießerweg 2 Bielefeld 33659 DE -2C-55-3C (hex) Gainspeed, Inc. -2C553C (base 16) Gainspeed, Inc. - 295 Santa Ana Court - Sunnyvale CA 94085 - US - 24-80-00 (hex) Westcontrol AS 248000 (base 16) Westcontrol AS Breivikvg 7 @@ -55382,12 +55943,6 @@ D4AAFF (base 16) MICRO WORLD Shanghai 200127 CN -00-25-CA (hex) LS Research, LLC -0025CA (base 16) LS Research, LLC - W66 N220 Commerce Court - Cedarburg WI 53012 - US - 00-25-B4 (hex) Cisco Systems, Inc 0025B4 (base 16) Cisco Systems, Inc 80 West Tasman Drive @@ -55532,12 +56087,6 @@ D4AAFF (base 16) MICRO WORLD Menlo Park CA 94025-1431 US -00-24-E4 (hex) Withings -0024E4 (base 16) Withings - 37bis rue du General Leclerc - Issy les Moulineaux 92442 - FR - 00-24-DE (hex) GLOBAL Technology Inc. 0024DE (base 16) GLOBAL Technology Inc. No.168,Shanshan Rd., Wangchun Industrial Park, @@ -57893,12 +58442,6 @@ D4AAFF (base 16) MICRO WORLD Vuokatti Kainuu 88610 FI -00-1A-35 (hex) BARTEC GmbH -001A35 (base 16) BARTEC GmbH - Schulstraße 30 - Gotteszell Bavaria 94239 - DE - 00-1A-37 (hex) Lear Corporation 001A37 (base 16) Lear Corporation Industriestrasse 48 @@ -59633,12 +60176,6 @@ D4AAFF (base 16) MICRO WORLD Soenderborg DK 6400 DK -00-13-B4 (hex) Appear TV -0013B4 (base 16) Appear TV - P.O. Box 8 Lilleaker - Oslo NO-0216 - NO - 00-13-AE (hex) Radiance Technologies, Inc. 0013AE (base 16) Radiance Technologies, Inc. 350 Wynn Dr. @@ -68321,12 +68858,6 @@ F854B8 (base 16) Amazon Technologies Inc. York YO30 4RY GB -B4-39-39 (hex) Shenzhen TINNO Mobile Technology Corp. -B43939 (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - A0-AB-51 (hex) WEIFANG GOERTEK ELECTRONICS CO.,LTD A0AB51 (base 16) WEIFANG GOERTEK ELECTRONICS CO.,LTD Gaoxin 2 Road, Free Trade Zone,Weifang,Shandong,261205,P.R.China @@ -71657,12 +72188,6 @@ E428A4 (base 16) Prama India Private Limited Reno NV 89507 US -00-0F-A0 (hex) CANON KOREA BUSINESS SOLUTIONS INC. -000FA0 (base 16) CANON KOREA BUSINESS SOLUTIONS INC. - Canon BS Tower, 607 Teheran-ro - Seoul Gangnam-gu 06173 - KR - 40-8C-1F (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD 408C1F (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD NO.18 HAIBIN ROAD, @@ -72809,12 +73334,6 @@ ECA81F (base 16) Technicolor CH USA Inc. Lawrenceville GA 30044 US -90-B5-7F (hex) Shenzhen iComm Semiconductor CO.,LTD -90B57F (base 16) Shenzhen iComm Semiconductor CO.,LTD - Room 504A,Block B,Digital Building,Gargen City,No.1079,Nanhai Road,Nanshan District,Shenzhen. - Shenzhen 518067 - CN - C0-F8-27 (hex) Rapidmax Technology Corporation C0F827 (base 16) Rapidmax Technology Corporation 3F., No.531, Zhongzheng Rd. Xindian Dist. @@ -74633,6 +75152,24 @@ D850A1 (base 16) Hunan Danuo Technology Co.,LTD Ankara 06520 TR +48-51-D0 (hex) Jiangsu Xinsheng Intelligent Technology Co., Ltd. +4851D0 (base 16) Jiangsu Xinsheng Intelligent Technology Co., Ltd. + 18th Floor,Inno laser Building,18-69 Changwu Mid Road,Changzhou Science & Education Town,Wujin District,Changzhou,Jiangsu213000,China + Changzhou Jiangsu 213000 + CN + +80-77-A4 (hex) TECNO MOBILE LIMITED +8077A4 (base 16) TECNO MOBILE LIMITED + ROOMS 05-15, 13A/F., SOUTH TOWER, WORLD FINANCE CENTRE, HARBOUR CITY, 17 CANTON ROAD, TSIM SHA TSUI, KOWLOON, HONG KONG + Hong Kong Hong Kong 999077 + HK + +7C-6C-F0 (hex) Shenzhen TINNO Mobile Technology Corp. +7C6CF0 (base 16) Shenzhen TINNO Mobile Technology Corp. + 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen + Shenzhen guangdong 518053 + CN + 00-C3-0A (hex) Xiaomi Communications Co Ltd 00C30A (base 16) Xiaomi Communications Co Ltd #019, 9th Floor, Building 6, 33 Xi'erqi Middle Road @@ -74663,30 +75200,714 @@ B42875 (base 16) Futecho Solutions Private Limited San Francisco CA 94107 US -48-51-D0 (hex) Jiangsu Xinsheng Intelligent Technology Co., Ltd. -4851D0 (base 16) Jiangsu Xinsheng Intelligent Technology Co., Ltd. - 18th Floor,Inno laser Building,18-69 Changwu Mid Road,Changzhou Science & Education Town,Wujin District,Changzhou,Jiangsu213000,China - Changzhou Jiangsu 213000 - CN - -80-77-A4 (hex) TECNO MOBILE LIMITED -8077A4 (base 16) TECNO MOBILE LIMITED - ROOMS 05-15, 13A/F., SOUTH TOWER, WORLD FINANCE CENTRE, HARBOUR CITY, 17 CANTON ROAD, TSIM SHA TSUI, KOWLOON, HONG KONG - Hong Kong Hong Kong 999077 - HK - -7C-6C-F0 (hex) Shenzhen TINNO Mobile Technology Corp. -7C6CF0 (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - 00-A0-A2 (hex) B810 S.R.L. 00A0A2 (base 16) B810 S.R.L. Via E. Lazzaretti 2/1 Reggio Emilia (RE) 42122 IT +38-68-BE (hex) Sichuan Tianyi Comheart Telecom Co.,LTD +3868BE (base 16) Sichuan Tianyi Comheart Telecom Co.,LTD + No.198,First Section,Snow Mountain Avenue, Jinyuan Town, Dayi County + Chengdu Sichuan 611330 + CN + +DC-9A-7D (hex) HISENSE VISUAL TECHNOLOGY CO.,LTD +DC9A7D (base 16) HISENSE VISUAL TECHNOLOGY CO.,LTD + Qianwangang Road 218 + Qingdao Shandong 266510 + CN + +28-A5-3F (hex) vivo Mobile Communication Co., Ltd. +28A53F (base 16) vivo Mobile Communication Co., Ltd. + No.1, vivo Road, Chang'an + Dongguan Guangdong 523860 + CN + +8C-49-B6 (hex) vivo Mobile Communication Co., Ltd. +8C49B6 (base 16) vivo Mobile Communication Co., Ltd. + No.1, vivo Road, Chang'an + Dongguan Guangdong 523860 + CN + +84-F1-D0 (hex) EHOOME IOT PRIVATE LIMITED +84F1D0 (base 16) EHOOME IOT PRIVATE LIMITED + A-13, SECTOR-83, + NOIDA UTTAR PRADESH 201301 + IN + +20-8B-D1 (hex) NXP Semiconductor (Tianjin) LTD. +208BD1 (base 16) NXP Semiconductor (Tianjin) LTD. + No.15 Xinghua Avenue, Xiqing Economic Development Area + Tianjin 300385 + CN + +00-0F-A0 (hex) Canon Korea Inc. +000FA0 (base 16) Canon Korea Inc. + 607, Teheran-ro, Gangnam-gu + Seoul Gangnam-gu 06173 + KR + +30-BB-7D (hex) OnePlus Technology (Shenzhen) Co., Ltd +30BB7D (base 16) OnePlus Technology (Shenzhen) Co., Ltd + 18C02, 18C03, 18C04 ,18C05,TAIRAN BUILDING, + Shenzhen Guangdong 518000 + CN + +6C-67-EF (hex) HUAWEI TECHNOLOGIES CO.,LTD +6C67EF (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +88-69-3D (hex) HUAWEI TECHNOLOGIES CO.,LTD +88693D (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +00-99-1D (hex) HUAWEI TECHNOLOGIES CO.,LTD +00991D (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +30-CB-36 (hex) Belden Singapore Pte. Ltd. +30CB36 (base 16) Belden Singapore Pte. Ltd. + 151 Lorong Chuan #05-01 New Tech Park Singapore + Singapore 556741 + SG + +B8-3F-D2 (hex) Mellanox Technologies, Inc. +B83FD2 (base 16) Mellanox Technologies, Inc. + 350 Oakmead Parkway, Suite 100 + Sunnyvale CA 94085 + US + +28-3E-0C (hex) Preferred Robotics, Inc. +283E0C (base 16) Preferred Robotics, Inc. + Otemachi Bldg. 1-6-1 Otemachi + Chiyoda-ku Tokyo 100-0004 + JP + +8C-17-59 (hex) Intel Corporate +8C1759 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +04-BC-9F (hex) Calix Inc. +04BC9F (base 16) Calix Inc. + 2777 Orchard Pkwy + San Jose CA 95131 + US + +6C-A4-01 (hex) essensys plc +6CA401 (base 16) essensys plc + Aldgate Tower, Leman Street + London E1 8FA + GB + +34-92-C2 (hex) Square Route Co., Ltd. +3492C2 (base 16) Square Route Co., Ltd. + Area-Shinagawa 13F, 1-9-36, Konan, Minato-ku + Tokyo Tokyo 108-0075 + JP + +34-BD-20 (hex) Hangzhou Hikrobot Technology Co., Ltd. +34BD20 (base 16) Hangzhou Hikrobot Technology Co., Ltd. + Room 304, Unit B, Building 2, 399 Danfeng Road, Binjiang District, Hangzhou, Zhejiang + Hangzhou 310052 + CN + +64-C2-69 (hex) eero inc. +64C269 (base 16) eero inc. + 660 3rd Street + San Francisco CA 94107 + US + +B0-4A-6A (hex) Samsung Electronics Co.,Ltd +B04A6A (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +A8-79-8D (hex) Samsung Electronics Co.,Ltd +A8798D (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +5C-ED-F4 (hex) Samsung Electronics Co.,Ltd +5CEDF4 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +28-3D-C2 (hex) Samsung Electronics Co.,Ltd +283DC2 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +BC-6E-6D (hex) EM Microelectronic +BC6E6D (base 16) EM Microelectronic + Rue des Sors 3 + Marin-Epagnier Neuchatel 2074 + CH + +00-D4-9E (hex) Intel Corporate +00D49E (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +CC-F3-05 (hex) SHENZHEN TIAN XING CHUANG ZHAN ELECTRONIC CO.,LTD +CCF305 (base 16) SHENZHEN TIAN XING CHUANG ZHAN ELECTRONIC CO.,LTD + Second floor, Building A, FengHangAvenue, Hangcheng Street, Bao'an District + Shenzhen Guangdong 518126 + CN + +AC-2A-A1 (hex) Cisco Systems, Inc +AC2AA1 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +F8-E9-4F (hex) Cisco Systems, Inc +F8E94F (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +30-89-4A (hex) Intel Corporate +30894A (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +E0-6C-C5 (hex) Huawei Device Co., Ltd. +E06CC5 (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +30-96-3B (hex) Huawei Device Co., Ltd. +30963B (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +8C-6B-DB (hex) Huawei Device Co., Ltd. +8C6BDB (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +10-DA-49 (hex) Huawei Device Co., Ltd. +10DA49 (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +60-18-3A (hex) Huawei Device Co., Ltd. +60183A (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +18-C0-07 (hex) Huawei Device Co., Ltd. +18C007 (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +98-59-7A (hex) Intel Corporate +98597A (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +64-49-7D (hex) Intel Corporate +64497D (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +B8-D6-1A (hex) Espressif Inc. +B8D61A (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +00-25-CA (hex) Laird Connectivity +0025CA (base 16) Laird Connectivity + W66 N220 Commerce Court + Cedarburg WI 53012 + US + +90-B5-7F (hex) Shenzhen iComm Semiconductor CO.,LTD +90B57F (base 16) Shenzhen iComm Semiconductor CO.,LTD + Room 601,Block B ,Digital Building,Garden City + Shenzhen No.1079 Nanhai Road,Nanshan District 518067 + CN + +74-56-3C (hex) GIGA-BYTE TECHNOLOGY CO.,LTD. +74563C (base 16) GIGA-BYTE TECHNOLOGY CO.,LTD. + Pin-Jen City, Taoyuan, Taiwan, R.O.C. + Pin-Jen Taoyuan 324 + TW + +D8-9C-8E (hex) Comcast Cable Corporation +D89C8E (base 16) Comcast Cable Corporation + 1800 Arch Street + Philadelphia PA 19103 + US + +04-B9-7D (hex) AiVIS Co., Itd. +04B97D (base 16) AiVIS Co., Itd. + 112, Dumipo-ro, Jung-gu + Incheon Incheon 22394 + KR + +BC-F4-D4 (hex) CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. +BCF4D4 (base 16) CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. + B22 Building,NO.51 Tongle Road, Shajing Town, Jiangnan District, Nanning, Guangxi Province, China + Nanning Guangxi 530007 + CN + +C4-C0-63 (hex) New H3C Technologies Co., Ltd +C4C063 (base 16) New H3C Technologies Co., Ltd + 466 Changhe Road, Binjiang District + Hangzhou Zhejiang 310052 + CN + +EC-55-1C (hex) HUAWEI TECHNOLOGIES CO.,LTD +EC551C (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +E0-79-8D (hex) Silicon Laboratories +E0798D (base 16) Silicon Laboratories + 400 West Cesar Chavez Street + Austin TX 78701 + US + +B4-83-51 (hex) Intel Corporate +B48351 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +34-AD-61 (hex) CELESTICA INC. +34AD61 (base 16) CELESTICA INC. + 1900-5140 Yonge Street PO Box 42 + Toronto Ontario M2N 6L7 + CA + +54-43-B2 (hex) Espressif Inc. +5443B2 (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +C0-DD-8A (hex) Facebook Technologies, LLC +C0DD8A (base 16) Facebook Technologies, LLC + 1601 Willow Rd + Menlo Park CA 94025 + US + +AC-D3-1D (hex) Cisco Meraki +ACD31D (base 16) Cisco Meraki + 500 Terry A. Francois Blvd + San Francisco 94158 + US + +10-96-1A (hex) CHIPSEA TECHNOLOGIES (SHENZHEN) CORP. +10961A (base 16) CHIPSEA TECHNOLOGIES (SHENZHEN) CORP. + 9F,BLOCK A,GARDEN CITY DIGITAL BUILDING,NO.1079 NANHAI ROAD,NANSHAN DISTRICT + SHEN ZHEN GUANG DONG 518000 + CN + +BC-E9-E2 (hex) Brocade Communications Systems LLC +BCE9E2 (base 16) Brocade Communications Systems LLC + 1320 Ridder Park Dr + San Jose CA 95131 + US + +00-18-48 (hex) Vecima Networks Inc. +001848 (base 16) Vecima Networks Inc. + 150 Cardinal Place + Saskatoon SK S7L 6H7 + CA + +2C-55-3C (hex) Vecima Networks Inc. +2C553C (base 16) Vecima Networks Inc. + 150 Cardinal Place + Saskatoon SK S7L 6H7 + CA + +AC-BF-71 (hex) Bose Corporation +ACBF71 (base 16) Bose Corporation + The Mountain + Framingham MA 01701-9168 + US + +18-A5-9C (hex) IEEE Registration Authority +18A59C (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +74-84-69 (hex) Nintendo Co.,Ltd +748469 (base 16) Nintendo Co.,Ltd + 11-1 HOKOTATE-CHO KAMITOBA,MINAMI-KU + KYOTO KYOTO 601-8501 + JP + +74-71-8B (hex) Apple, Inc. +74718B (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +70-31-7F (hex) Apple, Inc. +70317F (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +A4-CF-99 (hex) Apple, Inc. +A4CF99 (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +4C-2E-B4 (hex) Apple, Inc. +4C2EB4 (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +B4-19-74 (hex) Apple, Inc. +B41974 (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +04-E8-92 (hex) SHENNAN CIRCUITS CO.,LTD +04E892 (base 16) SHENNAN CIRCUITS CO.,LTD + Gao Qiao Industrial Park East,Long Gang District, + Shenzhen Guangdong 518117 + CN + +60-95-BD (hex) Apple, Inc. +6095BD (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +00-1A-35 (hex) BARTEC GmbH +001A35 (base 16) BARTEC GmbH + Max-Eyth-Straße 16 + Bad Mergentheim Bavaria 97980 + DE + +8C-CB-DF (hex) FOXCONN INTERCONNECT TECHNOLOGY +8CCBDF (base 16) FOXCONN INTERCONNECT TECHNOLOGY + 66-1 Zhongshan Road, Tucheng District + New Taipei City Taiwan 23680 + TW + +98-F1-12 (hex) Hangzhou Hikvision Digital Technology Co.,Ltd. +98F112 (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd. + No.555 Qianmo Road + Hangzhou Zhejiang 310052 + CN + +84-69-93 (hex) HP Inc. +846993 (base 16) HP Inc. + 10300 Energy Dr + Spring TX 77389 + US + +74-6F-88 (hex) zte corporation +746F88 (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +98-C8-1C (hex) BAYTEC LIMITED +98C81C (base 16) BAYTEC LIMITED + 107C, 31/f, The gateway, Tower 5, Harbour City, 15 canton road, Tsim Sha Tsui, Hong Kong + Harbour 999077 + HK + +D0-98-9C (hex) ConMet +D0989C (base 16) ConMet + 5701 SE Columbia Way + Vancouver WA 98661 + US + +1C-A4-10 (hex) Amlogic, Inc. +1CA410 (base 16) Amlogic, Inc. + 2518 Mission College Blvd, Suite 120 + Santa Clara CA 95054 + US + +24-26-D6 (hex) HUAWEI TECHNOLOGIES CO.,LTD +2426D6 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +70-A6-BD (hex) Honor Device Co., Ltd. +70A6BD (base 16) Honor Device Co., Ltd. + Suite 3401, Unit A, Building 6, Shum Yip Sky Park, No. 8089, Hongli West Road, Xiangmihu Street, Futian District + Shenzhen Guangdong 518040 + CN + +EC-81-9C (hex) HUAWEI TECHNOLOGIES CO.,LTD +EC819C (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +20-0B-16 (hex) Texas Instruments +200B16 (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +88-01-F9 (hex) Texas Instruments +8801F9 (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +F8-55-48 (hex) Texas Instruments +F85548 (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +68-E7-4A (hex) Texas Instruments +68E74A (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +4C-9E-6C (hex) BROADEX TECHNOLOGIES CO.LTD +4C9E6C (base 16) BROADEX TECHNOLOGIES CO.LTD + NO.306 YATAI ROAD + JIAXING ZHEJIANG 314006 + CN + +AC-A3-2F (hex) Solidigm Technology +ACA32F (base 16) Solidigm Technology + 1921 Corporate Center Circle, Suite 3B + Longmont 80501 + US + +AC-71-2E (hex) Fortinet, Inc. +AC712E (base 16) Fortinet, Inc. + 899 Kifer Road + Sunnyvale 94086 + US + +E4-B6-33 (hex) Wuxi Stars Microsystem Technology Co., Ltd +E4B633 (base 16) Wuxi Stars Microsystem Technology Co., Ltd + Room 2101, Tower C, Swan Tower, Wuxi Software Park, 111 Linghu Avenue, Xinwu District + Wuxi 214135 + CN + +08-51-04 (hex) Huawei Device Co., Ltd. +085104 (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +78-5B-64 (hex) Huawei Device Co., Ltd. +785B64 (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +98-D9-3D (hex) Demant Enterprise A/S +98D93D (base 16) Demant Enterprise A/S + Kongebakken 9 + Smorum 2765 + DK + +B4-A6-78 (hex) Zhejiang Tmall Technology Co., Ltd. +B4A678 (base 16) Zhejiang Tmall Technology Co., Ltd. + No.969 Wenyi West Road, Wuchang Street, Yuhang District + Hangzhou Zhejiang 310024 + CN + +AC-C4-BD (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +ACC4BD (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + NO.18 HAIBIN ROAD, + DONG GUAN GUANG DONG 523860 + CN + +54-E1-5B (hex) Huawei Device Co., Ltd. +54E15B (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +54-2F-04 (hex) Shanghai Longcheer Technology Co., Ltd. +542F04 (base 16) Shanghai Longcheer Technology Co., Ltd. + Bldg 1,No.401,Caobao RD,Xuhui Dist + Shanghai 200233 + CN + +C4-A1-0E (hex) IEEE Registration Authority +C4A10E (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +0C-EC-84 (hex) Shenzhen TINNO Mobile Technology Corp. +0CEC84 (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +B4-39-39 (hex) Shenzhen TINNO Mobile Technology Corp. +B43939 (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +8C-98-06 (hex) SHENZHEN SEI ROBOTICS CO.,LTD +8C9806 (base 16) SHENZHEN SEI ROBOTICS CO.,LTD + the 4th floor,Productivity Building D,#5 Hi-Tech Middle 2nd Road,Shenzhen Hi-Tech Industrial Park, Nanshan District,Shenzhen,China + Shenzhen 518000 + CN + +20-08-89 (hex) zte corporation +200889 (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +70-70-FC (hex) GOLD&WATER INDUSTRIAL LIMITED +7070FC (base 16) GOLD&WATER INDUSTRIAL LIMITED + NO.77 Leighton Road, 17/F Leighton Centre Causeway Bay ,HongKong + HongKong 999077 + HK + +88-F2-BD (hex) GD Midea Air-Conditioning Equipment Co.,Ltd. +88F2BD (base 16) GD Midea Air-Conditioning Equipment Co.,Ltd. + Midea Global Innovation Center,Beijiao Town,Shunde + Foshan Guangdong 528311 + CN + +A0-F8-95 (hex) Shenzhen TINNO Mobile Technology Corp. +A0F895 (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +6C-08-31 (hex) ANALOG SYSTEMS +6C0831 (base 16) ANALOG SYSTEMS + UNIT 12, 38 DLF INDUSTRIAL AREA KIRTI NAGAR NEW DELHI + NEW DELHI DELHI 110015 + IN + +70-AC-08 (hex) Silicon Laboratories +70AC08 (base 16) Silicon Laboratories + 400 West Cesar Chavez Street + Austin TX 78701 + US + +2C-07-F6 (hex) SKG Health Technologies Co., Ltd. +2C07F6 (base 16) SKG Health Technologies Co., Ltd. + 23A Floor,Building 3,Zhongke R&D Park,No.009,Gaoxin South 1st Road, High-tech Zone Community,Yuehai street, Nanshan District,Shenzhen City,Guangdong Province,P.R.China + Shenzhen 518000 + CN + +00-24-E4 (hex) Withings +0024E4 (base 16) Withings + 2 rue Maurice Hartmann + Issy-les-Moulineaux 92130 + FR + +A4-7E-FA (hex) Withings +A47EFA (base 16) Withings + 2 rue Maurice Hartmann + Issy-les-Moulineaux 92130 + FR + +3C-26-E4 (hex) Cisco Systems, Inc +3C26E4 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +38-91-B7 (hex) Cisco Systems, Inc +3891B7 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +34-5D-A8 (hex) Cisco Systems, Inc +345DA8 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +78-91-DE (hex) Guangdong ACIGA Science&Technology Co.,Ltd +7891DE (base 16) Guangdong ACIGA Science&Technology Co.,Ltd + L203 Biguiyuan International Club, Beijiao Town, Shunde District + Fo Shan Guangdong 528312 + CN + +E0-80-6B (hex) Xiaomi Communications Co Ltd +E0806B (base 16) Xiaomi Communications Co Ltd + #019, 9th Floor, Building 6, 33 Xi'erqi Middle Road + Beijing Haidian District 100085 + CN + +70-50-E7 (hex) IEEE Registration Authority +7050E7 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +00-13-B4 (hex) Appear AS +0013B4 (base 16) Appear AS + P.O. Box 8 Lilleaker + Oslo NO-0216 + NO + +38-FD-F5 (hex) Renesas Electronics (Penang) Sdn. Bhd. +38FDF5 (base 16) Renesas Electronics (Penang) Sdn. Bhd. + Phase 3, Bayan Lepas FIZ + Bayan Lepas Penang 11900 + MY + +38-12-7B (hex) Crenet Labs Co., Ltd. +38127B (base 16) Crenet Labs Co., Ltd. + Rm. 1, 10F., No. 181, Sec. 1, Datong Rd. + New Taipei City Xizhi Dist. 221451 + TW + +B0-E4-5C (hex) Samsung Electronics Co.,Ltd +B0E45C (base 16) Samsung Electronics Co.,Ltd + 129, Samsung-ro, Youngtongl-Gu + Suwon Gyeonggi-Do 16677 + KR + +DC-36-0C (hex) Hitron Technologies. Inc +DC360C (base 16) Hitron Technologies. Inc + No. 1-8, Lising 1st Rd. Hsinchu Science Park, Hsinchu, 300, Taiwan, R.O.C + Hsin-chu Taiwan 300 + TW + 84-80-94 (hex) Meter, Inc. 848094 (base 16) Meter, Inc. 148 Townsend St @@ -78839,12 +80060,6 @@ D843ED (base 16) Suzuken Nagoya Aich 4610015 JP -BC-41-01 (hex) Shenzhen TINNO Mobile Technology Corp. -BC4101 (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - 04-3A-0D (hex) SM Optics S.r.l. 043A0D (base 16) SM Optics S.r.l. via Michelangelo Buonarroti, 1 @@ -79847,12 +81062,6 @@ DCA904 (base 16) Apple, Inc. Hong Kong Hong Kong 999077 HK -A0-4C-5B (hex) Shenzhen TINNO Mobile Technology Corp. -A04C5B (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - 48-88-03 (hex) ManTechnology Inc. 488803 (base 16) ManTechnology Inc. 12th Fl, 308-4 Seongsoodong 2ga, Seongdonggu @@ -80579,12 +81788,6 @@ E80945 (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd. Bayan Lepas Penang 11900 MY -B0-A2-E7 (hex) Shenzhen TINNO Mobile Technology Corp. -B0A2E7 (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - 7C-25-87 (hex) chaowifi.com 7C2587 (base 16) chaowifi.com No. 502 1th Building TaiHe Square @@ -86237,12 +87440,6 @@ E83A12 (base 16) Samsung Electronics Co.,Ltd Chicago IL 60654 US -30-E0-90 (hex) Linctronix Ltd, -30E090 (base 16) Linctronix Ltd, - ​9F-1, No.66, Chongqing Rd., - Banqiao Dist., China 22063 - TW - A4-DC-BE (hex) HUAWEI TECHNOLOGIES CO.,LTD A4DCBE (base 16) HUAWEI TECHNOLOGIES CO.,LTD No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park @@ -91655,12 +92852,6 @@ E08A7E (base 16) Exponent Menlo Park CA 94025 US -A8-B0-AE (hex) LEONI -A8B0AE (base 16) LEONI - 3945 Freedom Circle - Santa Clara California 95054 - US - E4-27-71 (hex) Smartlabs E42771 (base 16) Smartlabs 72, Oktyabrskaya Street @@ -91739,12 +92930,6 @@ B4A4E3 (base 16) Cisco Systems, Inc Tainan 70955 TW -AC-83-F0 (hex) ImmediaTV Corporation -AC83F0 (base 16) ImmediaTV Corporation - 2005 De La Cruz Blvd - Santa Clara California 95050 - US - CC-6B-98 (hex) Minetec Wireless Technologies CC6B98 (base 16) Minetec Wireless Technologies 10 Kembla Way @@ -97226,12 +98411,6 @@ D8D67E (base 16) GSK CNC EQUIPMENT CO.,LTD Portland OR 97223 US -00-13-95 (hex) congatec AG -001395 (base 16) congatec AG - Auwiesenstrasse 5 - Deggendorf 94469 - DE - 00-13-56 (hex) FLIR Radiation Inc 001356 (base 16) FLIR Radiation Inc 100 Midland Rd @@ -98519,12 +99698,6 @@ D8D67E (base 16) GSK CNC EQUIPMENT CO.,LTD Allendale NJ 07401 US -00-0D-A9 (hex) T.E.A.M. S.L. -000DA9 (base 16) T.E.A.M. S.L. - Parque Tecnologico Edificio 108 - ZAMUDIO Bizkaia 48170 - ES - 00-0D-AB (hex) Parker Hannifin GmbH Electromechanical Division Europe 000DAB (base 16) Parker Hannifin GmbH Electromechanical Division Europe Robert-Bosch-Straße 22 @@ -107270,12 +108443,6 @@ B848AA (base 16) EM Microelectronic Marin-Epagnier Neuchatel 2074 CH -D8-34-EE (hex) Stem Audio -D834EE (base 16) Stem Audio - 2552 White Road, Suite A - Irvine CA 92614 - US - F8-57-2E (hex) Core Brands, LLC F8572E (base 16) Core Brands, LLC 5919 Sea Otter Place @@ -107798,12 +108965,6 @@ CCDB93 (base 16) Cisco Systems, Inc San Jose CA 94568 US -18-D6-1C (hex) Shenzhen TINNO Mobile Technology Corp. -18D61C (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - 54-48-E6 (hex) Beijing Xiaomi Mobile Software Co., Ltd 5448E6 (base 16) Beijing Xiaomi Mobile Software Co., Ltd Xiaomi Campus, No. 33 Xi erqi Middle Road, Haidian District @@ -109565,12 +110726,6 @@ F845C4 (base 16) Shenzhen Netforward Micro-Electronic Co., Ltd. Beijing 100053 CN -9C-4F-5F (hex) TAP Sound System -9C4F5F (base 16) TAP Sound System - 15 rue Castel - Fontenay-sous-Bois 94120 - FR - 00-08-0C (hex) VDA Group S.p.a. 00080C (base 16) VDA Group S.p.a. Viale Lino Zanussi 3 @@ -112079,6 +113234,30 @@ B4B742 (base 16) Amazon Technologies Inc. Reno NV 89507 US +60-BE-B4 (hex) S-Bluetech co., limited +60BEB4 (base 16) S-Bluetech co., limited + Room 202, Block A, Donghai Wang Mansion, 369 Bulonglu + Shenzhen Guangdong 518000 + CN + +90-DF-7D (hex) Realme Chongqing Mobile Telecommunications Corp.,Ltd. +90DF7D (base 16) Realme Chongqing Mobile Telecommunications Corp.,Ltd. + No.178 Yulong Avenue, Yufengshan, Yubei District, Chongqing. + Chongqing China 401120 + CN + +50-C1-F0 (hex) NXP Semiconductor (Tianjin) LTD. +50C1F0 (base 16) NXP Semiconductor (Tianjin) LTD. + No.15 Xinghua Avenue, Xiqing Economic Development Area + Tianjin 300385 + CN + +F4-84-8D (hex) TP-LINK TECHNOLOGIES CO.,LTD. +F4848D (base 16) TP-LINK TECHNOLOGIES CO.,LTD. + Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan + Shenzhen Guangdong 518057 + CN + A8-54-A2 (hex) Heimgard Technologies AS A854A2 (base 16) Heimgard Technologies AS Dronning Mauds gate 15 @@ -112097,6 +113276,570 @@ BC1D89 (base 16) Motorola Mobility LLC, a Lenovo Company Chicago IL 60654 US +B8-7E-E5 (hex) Intelbras +B87EE5 (base 16) Intelbras + BR 101, km 210, S/N° + São José Santa Catarina 88104800 + BR + +58-11-22 (hex) ASUSTek COMPUTER INC. +581122 (base 16) ASUSTek COMPUTER INC. + 15,Li-Te Rd., Peitou, Taipei 112, Taiwan + Taipei Taiwan 112 + TW + +74-69-4A (hex) Sichuan Tianyi Comheart Telecom Co.,LTD +74694A (base 16) Sichuan Tianyi Comheart Telecom Co.,LTD + No.198,First Section,Snow Mountain Avenue, Jinyuan Town, Dayi County + Chengdu Sichuan 611330 + CN + +78-15-2D (hex) UNION CHIP TECHNOLOGY LIMITED +78152D (base 16) UNION CHIP TECHNOLOGY LIMITED + 5th Floor, Building A1, Hangcheng Jinchi Industrial Park, 8TH North Road, Hangcheng Street, Bao 'an District, Shenzhen + shenzhen 518000 + CN + +94-AB-FE (hex) Nokia +94ABFE (base 16) Nokia + 600 March Road + Kanata Ontario K2K 2E6 + CA + +98-A9-2D (hex) New H3C Technologies Co., Ltd +98A92D (base 16) New H3C Technologies Co., Ltd + 466 Changhe Road, Binjiang District + Hangzhou Zhejiang 310052 + CN + +00-A5-54 (hex) Intel Corporate +00A554 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +0C-86-29 (hex) IEEE Registration Authority +0C8629 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +50-DC-D0 (hex) Observint Technologies, Inc. +50DCD0 (base 16) Observint Technologies, Inc. + 11000 N Mopac Expressway Suite 300 + Austin TX 78759 + US + +D4-F0-EA (hex) Beijing Xiaomi Mobile Software Co., Ltd +D4F0EA (base 16) Beijing Xiaomi Mobile Software Co., Ltd + The Rainbow City Office Building, 68 Qinghe Middle Street Haidian District + Beijing Beijing 100085 + CN + +58-76-AC (hex) SERNET (SUZHOU) TECHNOLOGIES CORPORATION +5876AC (base 16) SERNET (SUZHOU) TECHNOLOGIES CORPORATION + NO.8 Tangzhuang Road,Suzhou Industrial Park,Su ZhouCity,JiangSu Province,China + Suzhou 215021 + CN + +E0-03-6B (hex) Samsung Electronics Co.,Ltd +E0036B (base 16) Samsung Electronics Co.,Ltd + 129, Samsung-ro, Youngtongl-Gu + Suwon Gyeonggi-Do 16677 + KR + +80-69-1A (hex) Belkin International Inc. +80691A (base 16) Belkin International Inc. + 12045 East Waterfront Drive + Playa Vista 90094 + US + +64-31-72 (hex) ZHEJIANG HISING TECHNOLOGY CO.,LTD +643172 (base 16) ZHEJIANG HISING TECHNOLOGY CO.,LTD + Room 201 and 202,Building 5,328 Pingjiang Road,Yuecheng District,Shaoxing + Shaoxing Zhejiang 312000 + CN + +D0-FC-D0 (hex) HUMAX Co., Ltd. +D0FCD0 (base 16) HUMAX Co., Ltd. + HUMAX Village, 216, Hwangsaeul-ro, Bu + Seongnam-si Gyeonggi-do 463-875 + KR + +20-FA-DB (hex) Huahao Kunpeng Technology (chengDu) Co.,Ltd. +20FADB (base 16) Huahao Kunpeng Technology (chengDu) Co.,Ltd. + No.99, Hangtian Road, Section 2, East Third Ring Road,Chenghua District, Chengdu + Chengdu 610051 + CN + +0C-8B-95 (hex) Espressif Inc. +0C8B95 (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +68-5E-1C (hex) Texas Instruments +685E1C (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +38-AB-41 (hex) Texas Instruments +38AB41 (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +CC-47-92 (hex) ASIX Electronics Corporation +CC4792 (base 16) ASIX Electronics Corporation + 4F, No. 8, Hsin Ann Road, Hsinchu Science Park + Hsinchu 30078 + TW + +E0-46-EE (hex) NETGEAR +E046EE (base 16) NETGEAR + 350 East Plumeria Drive + San Jose CA 95134 + US + +9C-4F-5F (hex) Google, Inc. +9C4F5F (base 16) Google, Inc. + 15 rue Castel + Fontenay-sous-Bois 94120 + FR + +A0-29-42 (hex) Intel Corporate +A02942 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +10-71-B3 (hex) Zyxel Communications Corporation +1071B3 (base 16) Zyxel Communications Corporation + No. 6 Innovation Road II, Science Park + Hsichu Taiwan 300 + TW + +04-63-D0 (hex) Huawei Device Co., Ltd. +0463D0 (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +F0-D4-15 (hex) Intel Corporate +F0D415 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +18-69-D4 (hex) Samsung Electronics Co.,Ltd +1869D4 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +9C-95-61 (hex) Hui Zhou Gaoshengda Technology Co.,LTD +9C9561 (base 16) Hui Zhou Gaoshengda Technology Co.,LTD + No.75,Zhongkai High-Tech Development District,Huizhou + Hui Zhou Guangdong 516006 + CN + +DC-BE-49 (hex) ITEL MOBILE LIMITED +DCBE49 (base 16) ITEL MOBILE LIMITED + RM B3 & B4 BLOCK B, KO FAI INDUSTRIAL BUILDING NO.7 KO FAI ROAD, YAU TONG, KLN, H.K + Hong Kong KOWLOON 999077 + HK + +14-44-8F (hex) Edgecore Networks Corporation +14448F (base 16) Edgecore Networks Corporation + 1 Creation RD 3. + Hsinchu 30077 + TW + +34-25-BE (hex) Amazon Technologies Inc. +3425BE (base 16) Amazon Technologies Inc. + P.O Box 8102 + Reno NV 89507 + US + +D4-A3-EB (hex) Shenzhen iComm Semiconductor CO.,LTD +D4A3EB (base 16) Shenzhen iComm Semiconductor CO.,LTD + Room 601,Block B ,Digital Building,Garden City + Shenzhen No.1079 Nanhai Road,Nanshan District 518067 + CN + +9C-BF-CD (hex) HUAWEI TECHNOLOGIES CO.,LTD +9CBFCD (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +B8-9F-CC (hex) HUAWEI TECHNOLOGIES CO.,LTD +B89FCC (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +9C-E0-41 (hex) Nokia +9CE041 (base 16) Nokia + 600 March Road + Kanata Ontario K2K 2E6 + CA + +78-34-86 (hex) Nokia +783486 (base 16) Nokia + 600 March Road + Kanata Ontario K2K 2E6 + CA + +D4-D8-53 (hex) Intel Corporate +D4D853 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +8C-76-3F (hex) ARRIS Group, Inc. +8C763F (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +28-12-93 (hex) Honor Device Co., Ltd. +281293 (base 16) Honor Device Co., Ltd. + Suite 3401, Unit A, Building 6, Shum Yip Sky Park, No. 8089, Hongli West Road, Xiangmihu Street, Futian District + Shenzhen Guangdong 518040 + CN + +C0-A9-38 (hex) HUAWEI TECHNOLOGIES CO.,LTD +C0A938 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +C0-ED-E5 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +C0EDE5 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + NO.18 HAIBIN ROAD, + DONG GUAN GUANG DONG 523860 + CN + +10-A5-62 (hex) Iton Technology Corp. +10A562 (base 16) Iton Technology Corp. + Room 1302, Block A, Building 4, Tianan Cyber Park, Huangge Road,Longgang District + Shenzhen Guangdong 518116 + CN + +5C-24-E2 (hex) Suzhou Denbom Electronic S&T Co., Ltd +5C24E2 (base 16) Suzhou Denbom Electronic S&T Co., Ltd + 3F,Building 2, No.415,Changyang Street + Suzhou Jiangsu 215000 + CN + +64-98-9E (hex) TRINNOV AUDIO +64989E (base 16) TRINNOV AUDIO + 5 rue Edmond Michelet + NEUILLY PLAISANCE Ile-de-France 93360 + FR + +BC-C7-46 (hex) Hon Hai Precision IND.CO.,LTD +BCC746 (base 16) Hon Hai Precision IND.CO.,LTD + No. 66 Chung Shan Road TU-Cheng Industrial district TAIPEI TAIWAN + TAIPEI TAIWAN 33859 + CN + +30-E8-E4 (hex) Qorvo International Pte. Ltd. +30E8E4 (base 16) Qorvo International Pte. Ltd. + 1 Changi Business Park Avenue 1 + #04-01 486058 + SG + +00-0D-A9 (hex) INGETEAM +000DA9 (base 16) INGETEAM + Parque Tecnologico de Bizkaia, Edificio 110 + Zamudio Bizkaia 48170 + ES + +30-E0-90 (hex) Genevisio Ltd. +30E090 (base 16) Genevisio Ltd. + 13F, No.33, Sec. 1, Minsheng Rd. + New Taipei City Banqiao Dist. 220871 + TW + +64-FD-96 (hex) Sagemcom Broadband SAS +64FD96 (base 16) Sagemcom Broadband SAS + 250, route de l'Empereur + Rueil Malmaison Cedex hauts de seine 92848 + FR + +FC-B9-7E (hex) GE Appliances +FCB97E (base 16) GE Appliances + 4000 Buechel Bank Road + Louisville KY 40225 + US + +88-03-4C (hex) WEIFANG GOERTEK ELECTRONICS CO.,LTD +88034C (base 16) WEIFANG GOERTEK ELECTRONICS CO.,LTD + Gaoxin 2 Road, Free Trade Zone,Weifang,Shandong,261205,P.R.China + Weifang Shandong 261205 + CN + +48-DC-9D (hex) Grandprint(Beijing) Technology Co., LTD. +48DC9D (base 16) Grandprint(Beijing) Technology Co., LTD. + Room 259, 2 / F, Building 5, 8 Dongbeiwang West Road, Haidian District, + Beijing 100089 + CN + +C8-EB-EC (hex) Shenzhen YOUHUA Technology Co., Ltd +C8EBEC (base 16) Shenzhen YOUHUA Technology Co., Ltd + Room 407 Shenzhen University-town Business Park,Lishan Road,Taoyuan Street,Nanshan District + Shenzhen Guangdong 518055 + CN + +04-7C-16 (hex) Micro-Star INTL CO., LTD. +047C16 (base 16) Micro-Star INTL CO., LTD. + No.69, Lide St., + New Taipei City Taiwan 235 + TW + +E0-D7-38 (hex) WireStar Networks +E0D738 (base 16) WireStar Networks + PO Box 10966 + College Station TX 77842 + US + +40-42-44 (hex) Cisco Systems, Inc +404244 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +04-B6-BE (hex) CIG SHANGHAI CO LTD +04B6BE (base 16) CIG SHANGHAI CO LTD + 5th Floor, Building 8 No 2388 Chenhang Road + SHANGHAI 201114 + CN + +7C-EC-B1 (hex) Apple, Inc. +7CECB1 (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +5C-E9-1E (hex) Apple, Inc. +5CE91E (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +A8-B0-AE (hex) BizLink Special Cables Germany GmbH +A8B0AE (base 16) BizLink Special Cables Germany GmbH + Eschstrasse 1 + Friesoythe 26169 + DE + +94-C5-A6 (hex) ITEL MOBILE LIMITED +94C5A6 (base 16) ITEL MOBILE LIMITED + RM B3 & B4 BLOCK B, KO FAI INDUSTRIAL BUILDING NO.7 KO FAI ROAD, YAU TONG, KLN, H.K + Hong Kong KOWLOON 999077 + HK + +48-57-D2 (hex) Broadcom Limited +4857D2 (base 16) Broadcom Limited + 15191 Alton Parkway + Irvine CA 92618 + US + +9C-21-83 (hex) Broadcom Limited +9C2183 (base 16) Broadcom Limited + 15191 Alton Parkway + Irvine CA 92618 + US + +24-1F-BD (hex) Extreme Networks, Inc. +241FBD (base 16) Extreme Networks, Inc. + 6480 Via Del Oro + San Jose CA 95119 + US + +D8-34-EE (hex) SHURE INCORPORATED +D834EE (base 16) SHURE INCORPORATED + 5800 W. TOUHY AVE. + NILES IL 60714 + US + +F0-B6-61 (hex) eero inc. +F0B661 (base 16) eero inc. + 660 3rd Street + San Francisco CA 94107 + US + +04-25-F0 (hex) Nokia +0425F0 (base 16) Nokia + 600 March Road + Kanata Ontario K2K 2E6 + CA + +1C-BC-EC (hex) silex technology, Inc. +1CBCEC (base 16) silex technology, Inc. + 2-3-1 Hikaridai, Seika-cho, Souraku-gun + Kyoto 619-0237 + JP + +E0-6A-05 (hex) Shenzhen YOUHUA Technology Co., Ltd +E06A05 (base 16) Shenzhen YOUHUA Technology Co., Ltd + Room 407 Shenzhen University-town Business Park,Lishan Road,Taoyuan Street,Nanshan District + Shenzhen Guangdong 518055 + CN + +90-CD-1F (hex) Quectel Wireless Solutions Co.,Ltd. +90CD1F (base 16) Quectel Wireless Solutions Co.,Ltd. + 7th Floor, Hongye Building, No.1801 Hongmei Road, Xuhui District + Shanghai 200233 + CN + +90-23-5B (hex) Amazon Technologies Inc. +90235B (base 16) Amazon Technologies Inc. + P.O Box 8102 + Reno NV 89507 + US + +14-13-0B (hex) Garmin International +14130B (base 16) Garmin International + 1200 E. 151st St + Olathe KS 66062 + US + +48-9B-E0 (hex) Realme Chongqing Mobile Telecommunications Corp.,Ltd. +489BE0 (base 16) Realme Chongqing Mobile Telecommunications Corp.,Ltd. + No.178 Yulong Avenue, Yufengshan, Yubei District, Chongqing. + Chongqing China 401120 + CN + +5C-FA-25 (hex) Sagemcom Broadband SAS +5CFA25 (base 16) Sagemcom Broadband SAS + 250, route de l'Empereur + Rueil Malmaison Cedex hauts de seine 92848 + FR + +40-3B-7B (hex) Huawei Device Co., Ltd. +403B7B (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +08-30-CE (hex) Fiberhome Telecommunication Technologies Co.,LTD +0830CE (base 16) Fiberhome Telecommunication Technologies Co.,LTD + No.5 DongXin Road + Wuhan Hubei 430074 + CN + +B0-A2-E7 (hex) Shenzhen TINNO Mobile Technology Corp. +B0A2E7 (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +A0-4C-5B (hex) Shenzhen TINNO Mobile Technology Corp. +A04C5B (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +BC-41-01 (hex) Shenzhen TINNO Mobile Technology Corp. +BC4101 (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +74-97-79 (hex) CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. +749779 (base 16) CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. + B22 Building,NO.51 Tongle Road, Shajing Town, Jiangnan District, Nanning, Guangxi Province, China + Nanning Guangxi 530007 + CN + +F8-CD-C8 (hex) Sichuan Tianyi Comheart Telecom Co.,LTD +F8CDC8 (base 16) Sichuan Tianyi Comheart Telecom Co.,LTD + No.198,First Section,Snow Mountain Avenue, Jinyuan Town, Dayi County + Chengdu Sichuan 611330 + CN + +B0-28-5B (hex) JUHUA Technology Inc. +B0285B (base 16) JUHUA Technology Inc. + No.8,Yanbao Block,Hutian Road,Pingdi Street,Longgang District + Shenzhen City Guangdong Province 518117 + CN + +18-D6-1C (hex) Shenzhen TINNO Mobile Technology Corp. +18D61C (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +34-CF-6C (hex) Hangzhou Taili wireless communication equipment Co.,Ltd +34CF6C (base 16) Hangzhou Taili wireless communication equipment Co.,Ltd + Room 1901, No.258, Zhonghe Middle Road, Shangcheng District, Hangzhou + Hangzhou Zhejiang 310003 + CN + +E0-F6-78 (hex) Fiberhome Telecommunication Technologies Co.,LTD +E0F678 (base 16) Fiberhome Telecommunication Technologies Co.,LTD + No.5 DongXin Road + Wuhan Hubei 430074 + CN + +28-F7-D6 (hex) Fiberhome Telecommunication Technologies Co.,LTD +28F7D6 (base 16) Fiberhome Telecommunication Technologies Co.,LTD + No.5 DongXin Road + Wuhan Hubei 430074 + CN + +C8-24-96 (hex) Jiangsu Yinhe Electronics Co.,Ltd. +C82496 (base 16) Jiangsu Yinhe Electronics Co.,Ltd. + No.188 Nanhuan Road, TangQiao Town + Zhangjiagang Jiangsu 215611 + CN + +24-18-C0 (hex) E. Wehrle GmbH +2418C0 (base 16) E. Wehrle GmbH + Obertalstraße 8 + 78120 Furtwangen Baden-Württemberg 78120 + DE + +14-5B-B9 (hex) ConMet +145BB9 (base 16) ConMet + 5701 SE Columbia Way + Vancouver WA 98661 + US + +AC-83-F0 (hex) Cobalt Digital Inc. +AC83F0 (base 16) Cobalt Digital Inc. + 2506 Galen Drive + Champaign IL 61821 + US + +40-22-30 (hex) Shenzhen SuperElectron Technology Co.,Ltd. +402230 (base 16) Shenzhen SuperElectron Technology Co.,Ltd. + 1213-1214, haosheng business center, dongbin road, nanshan street, nanshan district, shenzhen city + Shenzhen Guangdong 518000 + CN + +B8-B4-09 (hex) Samsung Electronics Co.,Ltd +B8B409 (base 16) Samsung Electronics Co.,Ltd + 129, Samsung-ro, Youngtongl-Gu + Suwon Gyeonggi-Do 16677 + KR + +00-13-95 (hex) congatec GmbH +001395 (base 16) congatec GmbH + Auwiesenstrasse 5 + Deggendorf 94469 + DE + +D4-E2-2F (hex) Roku, Inc +D4E22F (base 16) Roku, Inc + 1155 Coleman Ave + San Jose CA 95110 + US + F8-D0-27 (hex) Seiko Epson Corporation F8D027 (base 16) Seiko Epson Corporation 2070 Kotobuki Koaka @@ -114863,12 +116606,6 @@ C08135 (base 16) Ningbo Forfan technology Co., LTD LIBERTYVILLE IL 60048 US -B4-C0-F5 (hex) Shenzhen TINNO Mobile Technology Corp. -B4C0F5 (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - 40-62-31 (hex) GIFA 406231 (base 16) GIFA 11th Fl., Suojia Business Building , No.7 Hangkong Road , Baoan District @@ -118919,12 +120656,6 @@ D8E0B8 (base 16) BULAT LLC Hsichu Taiwan 300 TW -C0-C9-76 (hex) Shenzhen TINNO Mobile Technology Corp. -C0C976 (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - 58-8B-F3 (hex) Zyxel Communications Corporation 588BF3 (base 16) Zyxel Communications Corporation No. 6 Innovation Road II, Science Park @@ -121199,12 +122930,6 @@ A8CA7B (base 16) HUAWEI TECHNOLOGIES CO.,LTD Dongguan 523808 CN -BC-44-34 (hex) Shenzhen TINNO Mobile Technology Corp. -BC4434 (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - 04-BF-6D (hex) Zyxel Communications Corporation 04BF6D (base 16) Zyxel Communications Corporation No. 6 Innovation Road II, Science Park @@ -123221,12 +124946,6 @@ E855B4 (base 16) SAI Technology Inc. Vikmanshyttan Dalarna SE-776 70 SE -C0-EE-40 (hex) Laird Technologies -C0EE40 (base 16) Laird Technologies - 50 South Main St - Akron Ohio 44308 - US - F4-B8-A7 (hex) zte corporation F4B8A7 (base 16) zte corporation 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China @@ -124712,12 +126431,6 @@ C098E5 (base 16) University of Michigan Seoul 152-789 KR -44-3C-9C (hex) Pintsch Tiefenbach GmbH -443C9C (base 16) Pintsch Tiefenbach GmbH - Beisenbruchstrasse 10 - Sprockhoevel 45549 - DE - 28-FC-51 (hex) The Electric Controller and Manufacturing Co., LLC 28FC51 (base 16) The Electric Controller and Manufacturing Co., LLC PO Box 468 @@ -135533,12 +137246,6 @@ A07332 (base 16) Cashmaster International Limited Hubbard OR 97032 US -00-0E-DD (hex) SHURE INCORPORATED -000EDD (base 16) SHURE INCORPORATED - 5800 W. TOUHY AVE. - NILES IL 60714 - US - 00-0E-C2 (hex) Lowrance Electronics, Inc. 000EC2 (base 16) Lowrance Electronics, Inc. 12000 E. Skelly Drive @@ -138539,12 +140246,6 @@ A06A00 (base 16) Verilink Corporation Elmsford NY 10523 US -00-04-35 (hex) InfiNet LLC -000435 (base 16) InfiNet LLC - Serafimy Deryabinoy str. 24 - Yekaterinburg 620149 - RU - 00-04-37 (hex) Powin Information Technology, Inc. 000437 (base 16) Powin Information Technology, Inc. 8F, No. 70, Zhou-Z St. @@ -140483,12 +142184,6 @@ A06A00 (base 16) Verilink Corporation Yokohama City 226 JP -00-10-43 (hex) A2 CORPORATION -001043 (base 16) A2 CORPORATION - 6-14-11 YUTAKA-CHO - TOKYO - JP - 00-10-A5 (hex) OXFORD INSTRUMENTS 0010A5 (base 16) OXFORD INSTRUMENTS Halifax Road @@ -145481,18 +147176,6 @@ C440F6 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD DONG GUAN GUANG DONG 523860 CN -A4-7D-9F (hex) Shenzhen iComm Semiconductor CO.,LTD -A47D9F (base 16) Shenzhen iComm Semiconductor CO.,LTD - Room 504A,Block B,Digital Building,Gargen City,No.1079,Nanhai Road,Nanshan District,Shenzhen. - Shenzhen 518067 - CN - -84-EA-97 (hex) Shenzhen iComm Semiconductor CO.,LTD -84EA97 (base 16) Shenzhen iComm Semiconductor CO.,LTD - Room 501A,Block B,Digital Building,Garden City,No.1079 Nanhai Road,Nanshan District - Shenzhen 518067 - CN - 00-55-B1 (hex) Shanghai Baud Data Communication Co.,Ltd. 0055B1 (base 16) Shanghai Baud Data Communication Co.,Ltd. NO.123 JULI RD @@ -145613,12 +147296,6 @@ C094AD (base 16) zte corporation shenzhen guangdong 518057 CN -D0-21-AC (hex) Yo Labs LLC -D021AC (base 16) Yo Labs LLC - 3460 Hillview Ave. - Palo Alto CA 94304 - US - 34-2B-70 (hex) Arris 342B70 (base 16) Arris 2500 Walsh Ave. @@ -146039,12 +147716,6 @@ A8F766 (base 16) ITE Tech Inc Hsinchu Taiwan 30076 TW -14-B2-E5 (hex) Shenzhen iComm Semiconductor CO.,LTD -14B2E5 (base 16) Shenzhen iComm Semiconductor CO.,LTD - Room 504A,Block B,Digital Building,Gargen City,No.1079,Nanhai Road,Nanshan District,Shenzhen. - Shenzhen 518067 - CN - 00-21-3E (hex) TomTom International BV 00213E (base 16) TomTom International BV Oosterdoksstraat 114 @@ -147884,12 +149555,6 @@ FC5C45 (base 16) Ruckus Wireless Sunnyvale CA 94089 US -E0-CB-56 (hex) Shenzhen iComm Semiconductor CO.,LTD -E0CB56 (base 16) Shenzhen iComm Semiconductor CO.,LTD - Room 504A,Block B,Digital Building,Gargen City,No.1079,Nanhai Road,Nanshan District,Shenzhen. - Shenzhen 518067 - CN - 4C-02-20 (hex) Xiaomi Communications Co Ltd 4C0220 (base 16) Xiaomi Communications Co Ltd #019, 9th Floor, Building 6, 33 Xi'erqi Middle Road @@ -148139,12 +149804,6 @@ E0B72E (base 16) ShenZhen Qualmesh Technology Co.,Ltd. Shenzhen Guangdong 518055 CN -80-C3-BA (hex) Sennheiser electronic GmbH & Co. KG -80C3BA (base 16) Sennheiser electronic GmbH & Co. KG - Am Labor 1 - Wedemark Niedersachsen 30900 - DE - 04-42-1A (hex) ASUSTek COMPUTER INC. 04421A (base 16) ASUSTek COMPUTER INC. 15,Li-Te Rd., Peitou, Taipei 112, Taiwan @@ -149240,12 +150899,6 @@ F0A951 (base 16) HUAWEI TECHNOLOGIES CO.,LTD Dongguan 523808 CN -00-90-3F (hex) WorldCast Systems -00903F (base 16) WorldCast Systems - 20 Avenue Neil Armstrong - Mérignac 33700 - FR - 64-D6-9A (hex) Intel Corporate 64D69A (base 16) Intel Corporate Lot 8, Jalan Hi-Tech 2/3 @@ -149522,30 +151175,12 @@ D83DCC (base 16) shenzhen UDD Technologies,co.,Ltd Round Rock TX 78682 US -6C-8D-77 (hex) Cisco Systems, Inc -6C8D77 (base 16) Cisco Systems, Inc - 80 West Tasman Drive - San Jose CA 94568 - US - -74-11-B2 (hex) Cisco Systems, Inc -7411B2 (base 16) Cisco Systems, Inc - 80 West Tasman Drive - San Jose CA 94568 - US - B4-E2-65 (hex) Shenzhen SDMC Technology Co.,LTD B4E265 (base 16) Shenzhen SDMC Technology Co.,LTD 19/F, Changhong Science & Technology Mansion, No.18, Keji South 12th Road, High-tech Industrial Park, Nanshan District Shenzhen GUANGDONG 518027 CN -10-54-D2 (hex) IEEE Registration Authority -1054D2 (base 16) IEEE Registration Authority - 445 Hoes Lane - Piscataway NJ 08554 - US - EC-7C-5C (hex) Juniper Networks EC7C5C (base 16) Juniper Networks 1133 Innovation Way @@ -149558,6 +151193,768 @@ EC7C5C (base 16) Juniper Networks Shenzhen Guangdong 518057 CN +CC-60-C8 (hex) Microsoft Corporation +CC60C8 (base 16) Microsoft Corporation + One Microsoft Way + REDMOND WA 98052 + US + +6C-8D-77 (hex) Cisco Systems, Inc +6C8D77 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +74-11-B2 (hex) Cisco Systems, Inc +7411B2 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +B4-17-A8 (hex) Facebook Technologies, LLC +B417A8 (base 16) Facebook Technologies, LLC + 1 Hacker Way + Menlo Park CA 94025 + US + +10-54-D2 (hex) IEEE Registration Authority +1054D2 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +00-10-43 (hex) A2 CORPORATION +001043 (base 16) A2 CORPORATION + 1-7-1 Togoshi + Tokyo Shinagawa-ku 1420041 + JP + +C4-DF-39 (hex) Realme Chongqing Mobile Telecommunications Corp.,Ltd. +C4DF39 (base 16) Realme Chongqing Mobile Telecommunications Corp.,Ltd. + No.178 Yulong Avenue, Yufengshan, Yubei District, Chongqing. + Chongqing China 401120 + CN + +10-63-4B (hex) SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. +10634B (base 16) SHENZHEN MERCURY COMMUNICATION TECHNOLOGIES CO.,LTD. + 3/F, Building R1-B, High-Tech Industrial Park, Nanshan District + Shenzhen Guangdong 518057 + CN + +80-97-33 (hex) Shenzhen Elebao Technology Co., Ltd +809733 (base 16) Shenzhen Elebao Technology Co., Ltd + F/6, Tower A, Zhihuichuangxin Center Bldg,Qianjin Road, XixiangTown, Bao’an District + shenzhen GUANGDONG 518126 + CN + +F4-6D-2F (hex) TP-LINK TECHNOLOGIES CO.,LTD. +F46D2F (base 16) TP-LINK TECHNOLOGIES CO.,LTD. + Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan + Shenzhen Guangdong 518057 + CN + +00-A2-65 (hex) M2Motive Technology Inc. +00A265 (base 16) M2Motive Technology Inc. + Room 402,No. 125 North Jiangsu Road, Changning District + Shanghai Shanghai 200042 + CN + +D8-36-5F (hex) Intelbras +D8365F (base 16) Intelbras + BR 101, km 210, S/N° + São José Santa Catarina 88104800 + BR + +74-04-F1 (hex) Intel Corporate +7404F1 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +00-04-35 (hex) InfiNet LLC +000435 (base 16) InfiNet LLC + Office 425, 69/75 Vavilova str. + Moscow\ 117335 + RU + +E8-4D-EC (hex) Xerox Corporation +E84DEC (base 16) Xerox Corporation + 800 Phillips Rd + Webster NY 14450 + US + +C8-B8-2F (hex) eero inc. +C8B82F (base 16) eero inc. + 660 3rd Street + San Francisco CA 94107 + US + +54-78-C9 (hex) AMPAK Technology,Inc. +5478C9 (base 16) AMPAK Technology,Inc. + 3F, No.15-1 Zhonghua Road, Hsinchu Industrail Park, Hukou, + Hsinchu Hsinchu,Taiwan R.O.C. 30352 + TW + +FC-10-1A (hex) Palo Alto Networks +FC101A (base 16) Palo Alto Networks + 3000 Tannery Way + Santa Clara CA 95054 + US + +6C-AE-E3 (hex) Nokia +6CAEE3 (base 16) Nokia + 600 March Road + Kanata Ontario K2K 2E6 + CA + +0C-AC-8A (hex) Sagemcom Broadband SAS +0CAC8A (base 16) Sagemcom Broadband SAS + 250, route de l'Empereur + Rueil Malmaison Cedex hauts de seine 92848 + FR + +B8-5D-C3 (hex) HUAWEI TECHNOLOGIES CO.,LTD +B85DC3 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +B4-3A-E2 (hex) HUAWEI TECHNOLOGIES CO.,LTD +B43AE2 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +D0-A4-6F (hex) China Dragon Technology Limited +D0A46F (base 16) China Dragon Technology Limited + B4 Bldg.Haoshan 1st Industry Park, + Shenzhen Guangdong 518104 + CN + +F0-C8-B5 (hex) HUAWEI TECHNOLOGIES CO.,LTD +F0C8B5 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +2C-60-CD (hex) NR ELECTRIC CO., LTD +2C60CD (base 16) NR ELECTRIC CO., LTD + 69,Suyuan Avenue + Nanjing Jiangsu 211102 + CN + +8C-1E-80 (hex) Cisco Systems, Inc +8C1E80 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +A4-1E-E1 (hex) Taicang T&W Electronics +A41EE1 (base 16) Taicang T&W Electronics + 89# Jiang Nan RD + Suzhou Jiangsu 215412 + CN + +E8-FB-1C (hex) AzureWave Technology Inc. +E8FB1C (base 16) AzureWave Technology Inc. + 8F., No. 94, Baozhong Rd. + New Taipei City Taiwan 231 + TW + +B4-A7-C6 (hex) SERVERCOM (INDIA) PRIVATE LIMITED +B4A7C6 (base 16) SERVERCOM (INDIA) PRIVATE LIMITED + E-43/1 OKHLA INDUSTRIAL AREA PHASE-II NEW DELHI SOUTH DELHI + NEW DELHI NA + IN + +1C-76-F2 (hex) Samsung Electronics Co.,Ltd +1C76F2 (base 16) Samsung Electronics Co.,Ltd + 129, Samsung-ro, Youngtongl-Gu + Suwon Gyeonggi-Do 16677 + KR + +80-C3-BA (hex) Sennheiser Consumer Audio GmbH +80C3BA (base 16) Sennheiser Consumer Audio GmbH + Am Labor 1 + Wedemark Niedersachsen 30900 + DE + +40-35-E6 (hex) Samsung Electronics Co.,Ltd +4035E6 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +28-6B-35 (hex) Intel Corporate +286B35 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +30-43-D7 (hex) IEEE Registration Authority +3043D7 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +C0-EE-40 (hex) Laird Connectivity +C0EE40 (base 16) Laird Connectivity + 50 South Main St + Akron Ohio 44308 + US + +A4-7D-9F (hex) Shenzhen iComm Semiconductor CO.,LTD +A47D9F (base 16) Shenzhen iComm Semiconductor CO.,LTD + Room 601,Block B ,Digital Building,Garden City + Shenzhen No.1079 Nanhai Road,Nanshan District 518067 + CN + +84-EA-97 (hex) Shenzhen iComm Semiconductor CO.,LTD +84EA97 (base 16) Shenzhen iComm Semiconductor CO.,LTD + Room 601,Block B ,Digital Building,Garden City + Shenzhen No.1079 Nanhai Road,Nanshan District 518067 + CN + +14-B2-E5 (hex) Shenzhen iComm Semiconductor CO.,LTD +14B2E5 (base 16) Shenzhen iComm Semiconductor CO.,LTD + Room 601,Block B ,Digital Building,Garden City + Shenzhen No.1079 Nanhai Road,Nanshan District 518067 + CN + +E0-CB-56 (hex) Shenzhen iComm Semiconductor CO.,LTD +E0CB56 (base 16) Shenzhen iComm Semiconductor CO.,LTD + Room 601,Block B ,Digital Building,Garden City + Shenzhen No.1079 Nanhai Road,Nanshan District 518067 + CN + +30-04-5C (hex) Shenzhen SuperElectron Technology Co.,Ltd. +30045C (base 16) Shenzhen SuperElectron Technology Co.,Ltd. + 1213-1214, haosheng business center, dongbin road, nanshan street, nanshan district, shenzhen city + Shenzhen Guangdong 518000 + CN + +90-79-CF (hex) zte corporation +9079CF (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +88-8F-A4 (hex) Huawei Device Co., Ltd. +888FA4 (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +3C-82-C0 (hex) Technicolor CH USA Inc. +3C82C0 (base 16) Technicolor CH USA Inc. + 5030 Sugarloaf Parkway Bldg 6 + Lawrenceville GA 30044 + US + +C4-DE-E2 (hex) Espressif Inc. +C4DEE2 (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +68-B6-B3 (hex) Espressif Inc. +68B6B3 (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +FC-A0-5A (hex) Oray.com co., LTD. +FCA05A (base 16) Oray.com co., LTD. + 8008Rm, building No.1 GuoDing d. Yangpu District + Shanghai Shanghai 200433 + CN + +90-48-6C (hex) Ring LLC +90486C (base 16) Ring LLC + 1523 26th St + Santa Monica CA 90404 + US + +3C-46-45 (hex) Shanghai Infinity Wireless Technologies Co.,Ltd. +3C4645 (base 16) Shanghai Infinity Wireless Technologies Co.,Ltd. + Room 522, Building A, No.1687 Changyang Road, Yangpu District, Shanghai + Shanghai Shanghai 200082 + CN + +A4-F9-33 (hex) Intel Corporate +A4F933 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +10-F6-0A (hex) Intel Corporate +10F60A (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +70-D8-23 (hex) Intel Corporate +70D823 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +50-68-AC (hex) Huawei Device Co., Ltd. +5068AC (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +50-39-2F (hex) INGRAM MICRO SERVICES +50392F (base 16) INGRAM MICRO SERVICES + 100 CHEMIN DE BAILLOT + MONTAUBAN 82000 + FR + +FC-84-17 (hex) Honor Device Co., Ltd. +FC8417 (base 16) Honor Device Co., Ltd. + Suite 3401, Unit A, Building 6, Shum Yip Sky Park, No. 8089, Hongli West Road, Xiangmihu Street, Futian District + Shenzhen Guangdong 518040 + CN + +2C-A7-9E (hex) HUAWEI TECHNOLOGIES CO.,LTD +2CA79E (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +0C-7F-B2 (hex) ARRIS Group, Inc. +0C7FB2 (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +AC-B5-66 (hex) Renesas Electronics (Penang) Sdn. Bhd. +ACB566 (base 16) Renesas Electronics (Penang) Sdn. Bhd. + Phase 3, Bayan Lepas FIZ + Bayan Lepas Penang 11900 + MY + +10-24-07 (hex) HUAWEI TECHNOLOGIES CO.,LTD +102407 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +74-D9-EB (hex) Petabit Scale, Inc. +74D9EB (base 16) Petabit Scale, Inc. + 5814 Lonetree Blvd, Ste 200 + Rocklin CA 95765 + US + +D0-21-AC (hex) Yohana +D021AC (base 16) Yohana + 3460 Hillview Ave. + Palo Alto CA 94304 + US + +38-1F-26 (hex) IEEE Registration Authority +381F26 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +7C-E1-52 (hex) THE GOODYEAR TIRE & RUBBER COMPANY +7CE152 (base 16) THE GOODYEAR TIRE & RUBBER COMPANY + 200 Innovation Way + Akron OH 44316 + US + +28-CD-C1 (hex) Raspberry Pi Trading Ltd +28CDC1 (base 16) Raspberry Pi Trading Ltd + Maurice Wilkes Building, Cowley Road + Cambridge CB4 0DS + GB + +00-90-3F (hex) WorldCast Systems +00903F (base 16) WorldCast Systems + 20 Avenue Neil Armstrong + Mérignac 33700 + FR + +50-E6-36 (hex) AVM Audiovisuelles Marketing und Computersysteme GmbH +50E636 (base 16) AVM Audiovisuelles Marketing und Computersysteme GmbH + Alt-Moabit 95 + Berlin Berlin 10559 + DE + +78-20-BD (hex) Polysense (Beijing) Technologies Co. Ltd +7820BD (base 16) Polysense (Beijing) Technologies Co. Ltd + 9  Shangdi 3rd Street, D508B3/5(4)F Bldg D, Haidian Dist. + Beijing 100085 + CN + +EC-21-25 (hex) Toshiba Corp. +EC2125 (base 16) Toshiba Corp. + 1-1 Shibaura 1-Chome, Minato-Ku + Tokyo 105-8001 + JP + +68-4E-05 (hex) HUNAN FN-LINK TECHNOLOGY LIMITED +684E05 (base 16) HUNAN FN-LINK TECHNOLOGY LIMITED + No.8, Litong Road, Liuyan Economic & Tec + Changsha HUNAN 410329 + CN + +04-99-BB (hex) Apple, Inc. +0499BB (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +F0-4D-D4 (hex) Sagemcom Broadband SAS +F04DD4 (base 16) Sagemcom Broadband SAS + 250, route de l'Empereur + Rueil Malmaison Cedex hauts de seine 92848 + FR + +00-E5-F1 (hex) BUFFALO.INC +00E5F1 (base 16) BUFFALO.INC + AKAMONDORI Bld.,30-20,Ohsu 3-chome,Naka-ku + Nagoya Aichi Pref. 460-8315 + JP + +A8-51-AB (hex) Apple, Inc. +A851AB (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +5C-1B-F4 (hex) Apple, Inc. +5C1BF4 (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +34-EE-2A (hex) ConMet +34EE2A (base 16) ConMet + 5701 SE Columbia Way + Vancouver WA 98661 + US + +78-66-9D (hex) Hui Zhou Gaoshengda Technology Co.,LTD +78669D (base 16) Hui Zhou Gaoshengda Technology Co.,LTD + No.2, Jin-da Road, Huinan High-tech Industrial Park, Hui-ao Avenue + Huizhou Guangdong 516025 + CN + +48-46-8D (hex) Zepcam B.V. +48468D (base 16) Zepcam B.V. + Delftechpark, 17-19 + Delft 2628 XJ + NL + +90-49-92 (hex) YSTen Technology Co.,Ltd +904992 (base 16) YSTen Technology Co.,Ltd + Room 1715,17/F North Star Times Tower,Chaoyang District,Beijing. + Beijing 100101 + CN + +AC-29-29 (hex) Infinix mobility limited +AC2929 (base 16) Infinix mobility limited + RMS 05-15, 13A/F SOUTH TOWER WORLD FINANCE CTR HARBOUR CITY 17 CANTON RD TST KLN HONG KONG + HongKong HongKong 999077 + HK + +3C-CE-0D (hex) Shenzhen juduoping Technology Co.,Ltd +3CCE0D (base 16) Shenzhen juduoping Technology Co.,Ltd + Baoan Xin'an Streat + Shenzhen 002052 + CN + +00-0E-DD (hex) SHURE INCORPORATED +000EDD (base 16) SHURE INCORPORATED + 5800 W. TOUHY AVE. + NILES IL 60714 + US + +18-FD-74 (hex) Routerboard.com +18FD74 (base 16) Routerboard.com + Mikrotikls SIA + Riga Riga LV1009 + LV + +40-D9-5A (hex) AMPAK Technology,Inc. +40D95A (base 16) AMPAK Technology,Inc. + 3F, No.15-1 Zhonghua Road, Hsinchu Industrail Park, Hukou, + Hsinchu Hsinchu,Taiwan R.O.C. 30352 + TW + +4C-D0-DD (hex) HUAWEI TECHNOLOGIES CO.,LTD +4CD0DD (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +E4-90-2A (hex) HUAWEI TECHNOLOGIES CO.,LTD +E4902A (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +90-5E-44 (hex) HUAWEI TECHNOLOGIES CO.,LTD +905E44 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +E8-3A-4B (hex) China Mobile Group Device Co.,Ltd. +E83A4B (base 16) China Mobile Group Device Co.,Ltd. + 32 Xuanwumen West Street,Xicheng District + Beijing 100053 + CN + +60-E9-AA (hex) CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. +60E9AA (base 16) CLOUD NETWORK TECHNOLOGY SINGAPORE PTE. LTD. + B22 Building,NO.51 Tongle Road, Shajing Town, Jiangnan District, Nanning, Guangxi Province, China + Nanning Guangxi 530007 + CN + +24-0F-5E (hex) Shenzhen z-router Technology Co., Ltd +240F5E (base 16) Shenzhen z-router Technology Co., Ltd + 406,Block A,Taojindi Building ,Tenglong Road,Longhua New District, + Shenzhen GuangDong 518000 + CN + +6C-97-6D (hex) Motorola Mobility LLC, a Lenovo Company +6C976D (base 16) Motorola Mobility LLC, a Lenovo Company + 222 West Merchandise Mart Plaza + Chicago IL 60654 + US + +64-11-A4 (hex) Motorola Mobility LLC, a Lenovo Company +6411A4 (base 16) Motorola Mobility LLC, a Lenovo Company + 222 West Merchandise Mart Plaza + Chicago IL 60654 + US + +10-F0-68 (hex) Ruckus Wireless +10F068 (base 16) Ruckus Wireless + 350 West Java Drive + Sunnyvale CA 94089 + US + +F0-87-7F (hex) Magnetar Technology Shenzhen Co., LTD. +F0877F (base 16) Magnetar Technology Shenzhen Co., LTD. + Room211, Building1, No.26 Puzai Road, Pingdi Longgang + Shenzhen GUANGDONG 518117 + CN + +74-D4-DD (hex) Quanta Computer Inc. +74D4DD (base 16) Quanta Computer Inc. + No. 211, Wenhua 2nd Rd., Guishan Dist. + Taoyuan City Taiwan 33377 + TW + +C8-D6-B7 (hex) Solidigm Technology +C8D6B7 (base 16) Solidigm Technology + 1921 Corporate Center Circle, Suite 3B + Longmont CO 80501 + US + +60-5B-30 (hex) Dell Inc. +605B30 (base 16) Dell Inc. + One Dell Way + Round Rock TX 78682 + US + +44-3C-9C (hex) Pintsch GmbH +443C9C (base 16) Pintsch GmbH + Huenxer Strasse 149 + Dinslaken 46537 + DE + +D8-80-DC (hex) Huawei Device Co., Ltd. +D880DC (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +E8-B3-EF (hex) Fiberhome Telecommunication Technologies Co.,LTD +E8B3EF (base 16) Fiberhome Telecommunication Technologies Co.,LTD + No.5 DongXin Road + Wuhan Hubei 430074 + CN + +B4-9F-4D (hex) Fiberhome Telecommunication Technologies Co.,LTD +B49F4D (base 16) Fiberhome Telecommunication Technologies Co.,LTD + No.5 DongXin Road + Wuhan Hubei 430074 + CN + +F4-6C-68 (hex) Wistron Neweb Corporation +F46C68 (base 16) Wistron Neweb Corporation + No.20,Park Avenue II,Hsinchu Science Park + Hsin-Chu R.O.C. 308 + TW + +84-93-B2 (hex) zte corporation +8493B2 (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +20-64-DE (hex) Sunitec Enterprise Co.,Ltd +2064DE (base 16) Sunitec Enterprise Co.,Ltd + 3F.,No.98-1,Mincyuan Rd.Sindian City + Taipei County 231 231141 + CN + +74-B7-25 (hex) Huawei Device Co., Ltd. +74B725 (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +40-8E-DF (hex) Huawei Device Co., Ltd. +408EDF (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +A4-0F-98 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +A40F98 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + NO.18 HAIBIN ROAD, + DONG GUAN GUANG DONG 523860 + CN + +BC-44-34 (hex) Shenzhen TINNO Mobile Technology Corp. +BC4434 (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +70-66-2A (hex) Sony Interactive Entertainment Inc. +70662A (base 16) Sony Interactive Entertainment Inc. + 1-7-1 Konan + Minato-ku Tokyo 108-0075 + JP + +34-AC-11 (hex) China Mobile Group Device Co.,Ltd. +34AC11 (base 16) China Mobile Group Device Co.,Ltd. + 32 Xuanwumen West Street,Xicheng District + Beijing 100053 + CN + +44-32-C2 (hex) GOAL Co., Ltd. +4432C2 (base 16) GOAL Co., Ltd. + 2-16-6 Mitsuyakita Yodogawa-ku + Osaka-shi Osaka-fu 532-0032 + JP + +18-B1-85 (hex) Qiao Information Technology (Zhengzhou) Co., Ltd. +18B185 (base 16) Qiao Information Technology (Zhengzhou) Co., Ltd. + Room 405-1, Area A, 4th Floor, Wisdom Island Building, No.6, Zhongdao East, Zhengdong New District, + Zhengzhou Henan 450000 + CN + +A0-B7-65 (hex) Espressif Inc. +A0B765 (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +CC-DB-A7 (hex) Espressif Inc. +CCDBA7 (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +C8-6C-20 (hex) Sichuan Tianyi Comheart Telecom Co.,LTD +C86C20 (base 16) Sichuan Tianyi Comheart Telecom Co.,LTD + No.198,First Section,Snow Mountain Avenue, Jinyuan Town, Dayi County + Chengdu Sichuan 611330 + CN + +C0-C9-76 (hex) Shenzhen TINNO Mobile Technology Corp. +C0C976 (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +B4-C0-F5 (hex) Shenzhen TINNO Mobile Technology Corp. +B4C0F5 (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +E8-F7-91 (hex) Xiaomi Communications Co Ltd +E8F791 (base 16) Xiaomi Communications Co Ltd + #019, 9th Floor, Building 6, 33 Xi'erqi Middle Road + Beijing Haidian District 100085 + CN + +0C-97-5F (hex) Aruba, a Hewlett Packard Enterprise Company +0C975F (base 16) Aruba, a Hewlett Packard Enterprise Company + 3333 Scott Blvd + Santa Clara CA 95054 + US + +DC-71-DD (hex) AX Technologies +DC71DD (base 16) AX Technologies + 1400 Broadway, 18th Floor + New York City NY 10018 + US + +54-A9-C8 (hex) Home Control Singapore Pte Ltd +54A9C8 (base 16) Home Control Singapore Pte Ltd + 151 Lorong Chuan + Singapore 556741 + SG + +30-7F-10 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +307F10 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + NO.18 HAIBIN ROAD, + DONG GUAN GUANG DONG 523860 + CN + +A4-90-CE (hex) vivo Mobile Communication Co., Ltd. +A490CE (base 16) vivo Mobile Communication Co., Ltd. + No.1, vivo Road, Chang'an + Dongguan Guangdong 523860 + CN + +F8-B8-B4 (hex) Shenzhen Skyworth Digital Technology CO., Ltd +F8B8B4 (base 16) Shenzhen Skyworth Digital Technology CO., Ltd + 4F,Block A, Skyworth?Building, + Shenzhen Guangdong 518057 + CN + +B0-FB-DD (hex) Shenzhen SuperElectron Technology Co.,Ltd. +B0FBDD (base 16) Shenzhen SuperElectron Technology Co.,Ltd. + 1213-1214, haosheng business center, dongbin road, nanshan street, nanshan district, shenzhen city + Shenzhen Guangdong 518000 + CN + +3C-69-D1 (hex) ADC Automotive Distance Control System GmbH +3C69D1 (base 16) ADC Automotive Distance Control System GmbH + Peter-Dornier Strasse 10 + Lindau Bavaria 88131 + DE + +E0-9C-8D (hex) Seakeeper, Inc. +E09C8D (base 16) Seakeeper, Inc. + 45310 Abell House Lane Suite 350 + California MD 20619 + US + +04-BA-D6 (hex) D-Link Corporation +04BAD6 (base 16) D-Link Corporation + No.289, Sinhu 3rd Rd., Neihu District, + Taipei City 114 + TW + +30-3F-5D (hex) PT HAN SUNG ELECTORONICS INDONESIA +303F5D (base 16) PT HAN SUNG ELECTORONICS INDONESIA + JL.PALEM 1 BLOK DS-6 + KAWASAN INDUSTRI BATIK LIPPO CIKARANG, DESA CIBATU, KECAMATAN CIKARANG SELATAN BEKASI JAWA BARAT 17550 + ID + 7C-8A-E1 (hex) COMPAL INFORMATION (KUNSHAN) CO., LTD. 7C8AE1 (base 16) COMPAL INFORMATION (KUNSHAN) CO., LTD. NO. 25, THE 3RD Street KUNSHAN EXPORT PROCESSING ZONE @@ -151094,12 +153491,6 @@ A8F5DD (base 16) ARRIS Group, Inc. San Diego CA 92121 US -44-D3-AD (hex) Shenzhen TINNO Mobile Technology Corp. -44D3AD (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F, H-3 Building, Qiao Cheng Eastern Industrial Park, Overseas Chinese Town, Shenzhen - Shenzhen guangdong 518053 - CN - 9C-82-75 (hex) Yichip Microelectronics (Hangzhou) Co.,Ltd 9C8275 (base 16) Yichip Microelectronics (Hangzhou) Co.,Ltd Room 401, Building 15, No.498 Guoshoujing Road, Pudong Software Park @@ -153608,12 +155999,6 @@ F4F5DB (base 16) Xiaomi Communications Co Ltd NO.68, Qinghe Middle Street Haidian District, Beijing 100085 CN -F4-E2-04 (hex) Traqueur -F4E204 (base 16) Traqueur - 1, rue Royale - Saint-Cloud 92210 - FR - CC-22-37 (hex) IEEE Registration Authority CC2237 (base 16) IEEE Registration Authority 445 Hoes Lane @@ -159467,12 +161852,6 @@ ACCF85 (base 16) HUAWEI TECHNOLOGIES CO.,LTD Cupertino CA 95014 US -00-73-8D (hex) Shenzhen TINNO Mobile Technology Corp. -00738D (base 16) Shenzhen TINNO Mobile Technology Corp. - 4/F.,H-3 Building,OCT Eastern lndustrial Park. NO.1 XiangShan East Road. - GuangDong 518053 - US - 34-BA-75 (hex) Everest Networks, Inc 34BA75 (base 16) Everest Networks, Inc 2933 Bunker Hill Ln., Suite 100 @@ -171515,12 +173894,6 @@ EC6C9F (base 16) Chengdu Volans Technology CO.,LTD Kennesaw Georgia 30144 US -00-16-A3 (hex) Ingeteam Transmission&Distribution, S.A. -0016A3 (base 16) Ingeteam Transmission&Distribution, S.A. - C/ Usausuaga, 7 - Basauri Bizkaia 48970 - ES - 00-16-A0 (hex) Auto-Maskin 0016A0 (base 16) Auto-Maskin Sophie Radichs Vei 7 @@ -182996,12 +185369,6 @@ ECC302 (base 16) HUMAX Co., Ltd. Seongnam-si Gyeonggi-do 463-875 KR -98-C9-7C (hex) Shenzhen iComm Semiconductor CO.,LTD -98C97C (base 16) Shenzhen iComm Semiconductor CO.,LTD - Room 504A,Block B,Digital Building,Garden City,No.1079 Nanhai Road,Nanshan District,Shenzhen - shenzhen Guangdong 518067 - CN - 00-C3-43 (hex) E-T-A Circuit Breakers Ltd 00C343 (base 16) E-T-A Circuit Breakers Ltd 6 Telford Close @@ -186803,18 +189170,6 @@ BC6193 (base 16) Xiaomi Communications Co Ltd Beijing Haidian District 100085 CN -94-7F-D8 (hex) Shenzhen Skyworth Digital Technology CO., Ltd -947FD8 (base 16) Shenzhen Skyworth Digital Technology CO., Ltd - 4F,Block A, Skyworth?Building, - Shenzhen Guangdong 518057 - CN - -C8-54-A4 (hex) Infinix mobility limited -C854A4 (base 16) Infinix mobility limited - RMS 05-15, 13A/F SOUTH TOWER WORLD FINANCE CTR HARBOUR CITY 17 CANTON RD TST KLN HONG KONG - HongKong HongKong 999077 - HK - EC-71-DB (hex) Reolink Innovation Limited EC71DB (base 16) Reolink Innovation Limited 705,7/F,FA YUEN COMMERCIAL BUILDING,75-77 FA YUEN STREET @@ -186833,6 +189188,12 @@ F8E57E (base 16) Cisco Systems, Inc Seoul Seocho-Gu #137-902 KR +94-7F-D8 (hex) Shenzhen Skyworth Digital Technology CO., Ltd +947FD8 (base 16) Shenzhen Skyworth Digital Technology CO., Ltd + 4F,Block A, Skyworth?Building, + Shenzhen Guangdong 518057 + CN + 38-5B-44 (hex) Silicon Laboratories 385B44 (base 16) Silicon Laboratories 400 West Cesar Chavez Street @@ -186863,8 +189224,632 @@ CCEB18 (base 16) OOO TSS Moscow Moscow 105187 RU +6C-24-08 (hex) LCFC(Hefei) Electronics Technology Co., Ltd +6C2408 (base 16) LCFC(Hefei) Electronics Technology Co., Ltd + No.3188-1,YunGu Road(Comprehensive Bonded Zone),Hefei Economic and Technological Development Area + HEFEI ANHUI 230601 + CN + +EC-60-73 (hex) TP-LINK TECHNOLOGIES CO.,LTD. +EC6073 (base 16) TP-LINK TECHNOLOGIES CO.,LTD. + Building 24(floors 1,3,4,5)and 28(floors 1-4)Central Science and Technology Park,Shennan Road,Nanshan + Shenzhen Guangdong 518057 + CN + +10-4D-15 (hex) Viaanix Inc +104D15 (base 16) Viaanix Inc + 434 N Main St. + Wichita KS 67202 + US + +50-A0-15 (hex) Shenzhen Yipingfang Network Technology Co., Ltd. +50A015 (base 16) Shenzhen Yipingfang Network Technology Co., Ltd. + 21 / F, Kangjia R & D building, No.28, Keji South 12th Road, Nanshan District, Shenzhen City, Guangdong Province, China + Shenzhen Nanshan District 518000 + CN + +C8-54-A4 (hex) Infinix mobility limited +C854A4 (base 16) Infinix mobility limited + RMS 05-15, 13A/F SOUTH TOWER WORLD FINANCE CTR HARBOUR CITY 17 CANTON RD TST KLN HONG KONG + HongKong HongKong 999077 + HK + A0-09-2E (hex) zte corporation A0092E (base 16) zte corporation 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China shenzhen guangdong 518057 CN + +B0-AF-F7 (hex) Shenzhen Yipingfang Network Technology Co., Ltd. +B0AFF7 (base 16) Shenzhen Yipingfang Network Technology Co., Ltd. + 21 / F, Kangjia R & D building, No.28, Keji South 12th Road, Nanshan District, Shenzhen City, Guangdong Province, China + Shenzhen Nanshan District 518000 + CN + +70-85-C4 (hex) Ruijie Networks Co.,LTD +7085C4 (base 16) Ruijie Networks Co.,LTD + No. 2, 7th floor, xingwangruijie, haixi hi-tech industrial park, high-tech zone, fuzhou city + Fuzhou Fujian 350002 + CN + +5C-C5-63 (hex) HUNAN FN-LINK TECHNOLOGY LIMITED +5CC563 (base 16) HUNAN FN-LINK TECHNOLOGY LIMITED + No.8, Litong Road, Liuyan Economic & Tec + Changsha HUNAN 410329 + CN + +74-DD-CB (hex) China Leadshine Technology Co.,Ltd +74DDCB (base 16) China Leadshine Technology Co.,Ltd + 9-11, Building A3, Nanshan Ipark, No.1001 Xueyuan Avenue, Nanshan? + SHENZHEN 518000 + CN + +A8-B1-3B (hex) HP Inc. +A8B13B (base 16) HP Inc. + 10300 Energy Dr + Spring TX 77389 + US + +C4-38-75 (hex) Sonos, Inc. +C43875 (base 16) Sonos, Inc. + 614 Chapala St + Santa Barbara 93101 + US + +68-B6-91 (hex) Amazon Technologies Inc. +68B691 (base 16) Amazon Technologies Inc. + P.O Box 8102 + Reno NV 89507 + US + +DC-A9-56 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +DCA956 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + NO.18 HAIBIN ROAD, + DONG GUAN GUANG DONG 523860 + CN + +48-27-C5 (hex) HUAWEI TECHNOLOGIES CO.,LTD +4827C5 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +BC-D2-06 (hex) HUAWEI TECHNOLOGIES CO.,LTD +BCD206 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +20-89-8A (hex) Shenzhen Skyworth Digital Technology CO., Ltd +20898A (base 16) Shenzhen Skyworth Digital Technology CO., Ltd + 4F,Block A, Skyworth?Building, + Shenzhen Guangdong 518057 + CN + +14-75-5B (hex) Intel Corporate +14755B (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +F4-E2-04 (hex) COYOTE SYSTEM +F4E204 (base 16) COYOTE SYSTEM + 1, rue Royale + Saint-Cloud 92210 + FR + +F8-4E-58 (hex) Samsung Electronics Co.,Ltd +F84E58 (base 16) Samsung Electronics Co.,Ltd + 129, Samsung-ro, Youngtongl-Gu + Suwon Gyeonggi-Do 16677 + KR + +B4-70-64 (hex) Samsung Electronics Co.,Ltd +B47064 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +CC-82-7F (hex) Advantech Technology (CHINA) Co., Ltd. +CC827F (base 16) Advantech Technology (CHINA) Co., Ltd. + No.666, Han-Pu Rd. Yu-Shan + Kun-Shan Jiang Su 215316 + CN + +88-3F-0C (hex) system a.v. co., ltd. +883F0C (base 16) system a.v. co., ltd. + 5-16-1,Aoe,KIta-Ku + Okayama Okayama 700-0941 + JP + +C8-BE-35 (hex) Extreme Networks, Inc. +C8BE35 (base 16) Extreme Networks, Inc. + 6480 Via Del Oro + San Jose CA 95119 + US + +78-AF-08 (hex) Intel Corporate +78AF08 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +D8-10-68 (hex) Murata Manufacturing Co., Ltd. +D81068 (base 16) Murata Manufacturing Co., Ltd. + 1-10-1, Higashikotari + Nagaokakyo-shi Kyoto 617-8555 + JP + +5C-04-5A (hex) Company NA Stage & Light +5C045A (base 16) Company NA Stage & Light + Lambertu 9 + M?rupe LV-2167 + LV + +58-C3-56 (hex) EM Microelectronic +58C356 (base 16) EM Microelectronic + Rue des Sors 3 + Marin-Epagnier Neuchatel 2074 + CH + +A0-ED-FB (hex) Quectel Wireless Solutions Co.,Ltd. +A0EDFB (base 16) Quectel Wireless Solutions Co.,Ltd. + 7th Floor, Hongye Building, No.1801 Hongmei Road, Xuhui District + Shanghai 200233 + CN + +B0-23-47 (hex) Shenzhen Giant Microelectronics Company Limited +B02347 (base 16) Shenzhen Giant Microelectronics Company Limited + Room 2108, 21 Floor, Building No.1 of Chang Fu Jin Mao Tower, No.5 Shi Hua Road, Futian District + Shenzhen Guangdong 518000 + CN + +CC-DD-58 (hex) Robert Bosch GmbH +CCDD58 (base 16) Robert Bosch GmbH + Mittlerer Pfad 9 + Stuttgart 70499 + DE + +4C-2E-5E (hex) Samsung Electronics Co.,Ltd +4C2E5E (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +64-5D-F4 (hex) Samsung Electronics Co.,Ltd +645DF4 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +88-FC-5D (hex) Cisco Systems, Inc +88FC5D (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +F4-C8-8A (hex) Intel Corporate +F4C88A (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +18-3C-98 (hex) Shenzhen Hengyi Technology Co., LTD +183C98 (base 16) Shenzhen Hengyi Technology Co., LTD + Floor 5, Zone 1, Block B, Mingyou Purchasing Center, Baoyuan Road, Xixiang Street + Shenzhen Guangdong 518102 + CN + +78-71-04 (hex) Sichuan Tianyi Comheart Telecom Co.,LTD +787104 (base 16) Sichuan Tianyi Comheart Telecom Co.,LTD + No.198,First Section,Snow Mountain Avenue, Jinyuan Town, Dayi County + Chengdu Sichuan 611330 + CN + +64-C5-82 (hex) China Mobile Group Device Co.,Ltd. +64C582 (base 16) China Mobile Group Device Co.,Ltd. + 32 Xuanwumen West Street,Xicheng District + Beijing 100053 + CN + +D4-E0-53 (hex) Aruba, a Hewlett Packard Enterprise Company +D4E053 (base 16) Aruba, a Hewlett Packard Enterprise Company + 3333 Scott Blvd + Santa Clara CA 95054 + US + +5C-A4-F4 (hex) zte corporation +5CA4F4 (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +94-09-C9 (hex) ALPSALPINE CO .,LTD +9409C9 (base 16) ALPSALPINE CO .,LTD + nishida 6-1 + kakuda-City Miyagi-Pref 981-1595 + JP + +50-28-4A (hex) Intel Corporate +50284A (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +98-C9-7C (hex) Shenzhen iComm Semiconductor CO.,LTD +98C97C (base 16) Shenzhen iComm Semiconductor CO.,LTD + Room 601,Block B ,Digital Building,Garden City + Shenzhen No.1079 Nanhai Road,Nanshan District 518067 + CN + +24-6C-60 (hex) Huawei Device Co., Ltd. +246C60 (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +28-A3-31 (hex) Sierra Wireless +28A331 (base 16) Sierra Wireless + 13811 Wireless Way + Richmond BC V6V 3A4 + CA + +18-34-AF (hex) Kaonmedia CO., LTD. +1834AF (base 16) Kaonmedia CO., LTD. + 884-3, Seongnam-daero, Bundang-gu + Seongnam-si Gyeonggi-do 13517 + KR + +EC-62-60 (hex) Espressif Inc. +EC6260 (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +B0-6E-72 (hex) Realme Chongqing Mobile Telecommunications Corp.,Ltd. +B06E72 (base 16) Realme Chongqing Mobile Telecommunications Corp.,Ltd. + No.178 Yulong Avenue, Yufengshan, Yubei District, Chongqing. + Chongqing China 401120 + CN + +30-3E-A7 (hex) Intel Corporate +303EA7 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +60-7D-09 (hex) Luxshare Precision Industry Co., Ltd +607D09 (base 16) Luxshare Precision Industry Co., Ltd + 2nd floor,A building,Sanyo New Industrial Area,West Area of Maoyi, Shajing Street,Bao'an District + Shenzhen City Guangdong Province 518100 + CN + +CC-3E-79 (hex) ARRIS Group, Inc. +CC3E79 (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +28-F5-D1 (hex) ARRIS Group, Inc. +28F5D1 (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +10-E1-77 (hex) ARRIS Group, Inc. +10E177 (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +28-82-7C (hex) Bosch Automative products(Suzhou)Co.,Ltd Changzhou Branch +28827C (base 16) Bosch Automative products(Suzhou)Co.,Ltd Changzhou Branch + No.17 Longmen Road + Changzhou JiangSu 213164 + CN + +90-D4-73 (hex) vivo Mobile Communication Co., Ltd. +90D473 (base 16) vivo Mobile Communication Co., Ltd. + No.1, vivo Road, Chang'an + Dongguan Guangdong 523860 + CN + +08-EB-F6 (hex) HUAWEI TECHNOLOGIES CO.,LTD +08EBF6 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +C8-F0-9E (hex) Espressif Inc. +C8F09E (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +DC-54-75 (hex) Espressif Inc. +DC5475 (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +0C-7B-C8 (hex) Cisco Meraki +0C7BC8 (base 16) Cisco Meraki + 500 Terry A. Francois Blvd + San Francisco 94158 + US + +00-16-A3 (hex) INGETEAM +0016A3 (base 16) INGETEAM + Parque Tecnologico de Bizkaia, Edificio 110 + Zamudio Bizkaia 48170 + ES + +24-CF-24 (hex) Beijing Xiaomi Mobile Software Co., Ltd +24CF24 (base 16) Beijing Xiaomi Mobile Software Co., Ltd + The Rainbow City Office Building, 68 Qinghe Middle Street Haidian District + Beijing Beijing 100085 + CN + +1C-0D-7D (hex) Apple, Inc. +1C0D7D (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +14-F2-87 (hex) Apple, Inc. +14F287 (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +58-55-95 (hex) Apple, Inc. +585595 (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +F0-6C-5D (hex) Xiaomi Communications Co Ltd +F06C5D (base 16) Xiaomi Communications Co Ltd + #019, 9th Floor, Building 6, 33 Xi'erqi Middle Road + Beijing Haidian District 100085 + CN + +40-B0-2F (hex) Miele & Cie. KG +40B02F (base 16) Miele & Cie. KG + Carl-Miele-Straße 29 + Gütersloh 33332 + DE + +18-66-F0 (hex) Jupiter Systems +1866F0 (base 16) Jupiter Systems + 31015 Huntwood Ave + Hayward CA 94544-7007 + US + +74-60-4C (hex) RØDE +74604C (base 16) RØDE + 107 Carnarvon St + Silverwater NSW 2128 + AU + +28-6F-40 (hex) Tonly Technology Co. Ltd +286F40 (base 16) Tonly Technology Co. Ltd + Section 37, Zhongkai Hi-Tech Development Zone + Huizhou Guangdong 516006 + CN + +BC-C7-DA (hex) Earda Technologies co Ltd +BCC7DA (base 16) Earda Technologies co Ltd + Block A,Lianfeng Creative Park, #2 Jisheng Rd., Nansha District + Guangzhou Guangdong 511455 + CN + +0C-86-C7 (hex) Jabil Circuit (Guangzhou) Limited +0C86C7 (base 16) Jabil Circuit (Guangzhou) Limited + Huangpu 128, JunCheng Road + GuangZhou Guangdong 510530 + CN + +14-94-6C (hex) Apple, Inc. +14946C (base 16) Apple, Inc. + 1 Infinite Loop + Cupertino CA 95014 + US + +1C-59-74 (hex) IEEE Registration Authority +1C5974 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +58-00-32 (hex) Genexis B.V. +580032 (base 16) Genexis B.V. + Lodewijkstraat 1A + Eindhoven 5652AC + NL + +B4-7D-76 (hex) KNS Group LLC +B47D76 (base 16) KNS Group LLC + Room 4, Office IV, Floor 4 Rochdelskaya street, 15, b.15 Moscow, 123376 Russia + Moscow 123376 + RU + +C0-AD-97 (hex) TECNO MOBILE LIMITED +C0AD97 (base 16) TECNO MOBILE LIMITED + ROOMS 05-15, 13A/F., SOUTH TOWER, WORLD FINANCE CENTRE, HARBOUR CITY, 17 CANTON ROAD, TSIM SHA TSUI, KOWLOON, HONG KONG + Hong Kong Hong Kong 999077 + HK + +B0-38-E2 (hex) Wanan Hongsheng Electronic Co.Ltd +B038E2 (base 16) Wanan Hongsheng Electronic Co.Ltd + 1st section of industrial pack,Wan'An County,Ji'An City,jiangxi province + Wanan China/jiangxi 343800 + CN + +4C-53-69 (hex) YanFeng Visteon(ChongQing) Automotive Electronic Co.,Ltd +4C5369 (base 16) YanFeng Visteon(ChongQing) Automotive Electronic Co.,Ltd + No.8,Gang’an 2nd Road,Jiangbei District,Chongqing,P.R.China + ChongQing ChongQing 400025 + CN + +70-A9-83 (hex) Cisco Systems, Inc +70A983 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +BC-FA-EB (hex) Cisco Systems, Inc +BCFAEB (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +74-76-7D (hex) shenzhen kexint technology co.,ltd +74767D (base 16) shenzhen kexint technology co.,ltd + 5th Floor, Building 2, Chunhu Industrial Park, Dongshen Road, Pinghu Street, Longgang District, Shenzhen City + shenzhen guangdong 518000 + CN + +E0-48-D8 (hex) Guangzhi Wulian Technology(Guangzhou) Co., Ltd +E048D8 (base 16) Guangzhi Wulian Technology(Guangzhou) Co., Ltd + Room 1407, Fuli yingkai building, No. 16, Huaxia Road, Tianhe District, + Guangzhou 510623 + CN + +F8-E4-A4 (hex) Fiberhome Telecommunication Technologies Co.,LTD +F8E4A4 (base 16) Fiberhome Telecommunication Technologies Co.,LTD + No.5 DongXin Road + Wuhan Hubei 430074 + CN + +84-85-53 (hex) Biznes Systema Telecom, LLC +848553 (base 16) Biznes Systema Telecom, LLC + room XXII/1, fl 3, block 3, 6 Barklaya street, Moscow, 121087, Russia + Moscow 121087 + RU + +D8-88-63 (hex) HUAWEI TECHNOLOGIES CO.,LTD +D88863 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +C0-3E-50 (hex) HUAWEI TECHNOLOGIES CO.,LTD +C03E50 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +80-60-36 (hex) HUAWEI TECHNOLOGIES CO.,LTD +806036 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +A0-C9-8B (hex) Nokia Solutions and Networks GmbH & Co. KG +A0C98B (base 16) Nokia Solutions and Networks GmbH & Co. KG + Werinherstrasse 91 + München Bavaria D-81541 + DE + +18-BB-1C (hex) Huawei Device Co., Ltd. +18BB1C (base 16) Huawei Device Co., Ltd. + No.2 of Xincheng Road, Songshan Lake Zone + Dongguan Guangdong 523808 + CN + +4C-9D-22 (hex) ACES Co.,Ltd +4C9D22 (base 16) ACES Co.,Ltd + TianGong Avenue #916, Tianfu New Area + ChengDu Sichuan Province 610000 + CN + +88-C9-E8 (hex) Sony Corporation +88C9E8 (base 16) Sony Corporation + Sony City Osaki 2-10-1 + Shinagawa-ku Tokyo 141-8610 + JP + +B8-F0-B9 (hex) zte corporation +B8F0B9 (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +80-5B-65 (hex) LG Innotek +805B65 (base 16) LG Innotek + 26, Hanamsandan 5beon-ro + Gwangju Gwangsan-gu 506-731 + KR + +D4-43-0E (hex) Zhejiang Dahua Technology Co., Ltd. +D4430E (base 16) Zhejiang Dahua Technology Co., Ltd. + No.1199,Waterfront Road + Hangzhou Zhejiang 310053 + CN + +00-73-8D (hex) Shenzhen TINNO Mobile Technology Corp. +00738D (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +44-D3-AD (hex) Shenzhen TINNO Mobile Technology Corp. +44D3AD (base 16) Shenzhen TINNO Mobile Technology Corp. + Building, No.33, Xiandong Rd, Xili + Nanshan District, Shenzhen PRC 518053 + CN + +F8-5E-0B (hex) Realme Chongqing Mobile Telecommunications Corp.,Ltd. +F85E0B (base 16) Realme Chongqing Mobile Telecommunications Corp.,Ltd. + No.178 Yulong Avenue, Yufengshan, Yubei District, Chongqing. + Chongqing China 401120 + CN + +94-D3-31 (hex) Xiaomi Communications Co Ltd +94D331 (base 16) Xiaomi Communications Co Ltd + #019, 9th Floor, Building 6, 33 Xi'erqi Middle Road + Beijing Haidian District 100085 + CN + +5C-8C-30 (hex) Taicang T&W Electronics +5C8C30 (base 16) Taicang T&W Electronics + 89# Jiang Nan RD + Suzhou Jiangsu 215412 + CN + +2C-9D-65 (hex) vivo Mobile Communication Co., Ltd. +2C9D65 (base 16) vivo Mobile Communication Co., Ltd. + No.1, vivo Road, Chang'an + Dongguan Guangdong 523860 + CN + +44-88-16 (hex) Cisco Systems, Inc +448816 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +34-DD-04 (hex) Minut AB +34DD04 (base 16) Minut AB + Baltzarsgatan 23 + Malmö 21136 + SE + +60-1E-98 (hex) Axevast Technology +601E98 (base 16) Axevast Technology + 5F., No. 20, Guanqian Road, Zhongzheng District + Taipei City 100007 + TW + +A8-F7-D9 (hex) Mist Systems, Inc. +A8F7D9 (base 16) Mist Systems, Inc. + 1601 South De Anza Blvd, Suite 248 + Cupertino CA 95014 + US + +2C-3B-70 (hex) AzureWave Technology Inc. +2C3B70 (base 16) AzureWave Technology Inc. + 8F., No. 94, Baozhong Rd. + New Taipei City Taiwan 231 + TW diff --git a/hwdb.d/ma-medium.txt b/hwdb.d/ma-medium.txt index aa83fc256..0410acdf8 100644 --- a/hwdb.d/ma-medium.txt +++ b/hwdb.d/ma-medium.txt @@ -2258,12 +2258,6 @@ C00000-CFFFFF (base 16) Goetting KG Lehrte Select Your State or Province 31275 DE -50-FF-99 (hex) Coyote Sytem -100000-1FFFFF (base 16) Coyote Sytem - 24 quai Gallieni - Suresnes 92150 - FR - 50-FF-99 (hex) Shenzhen Haipengxin Electronic Co., Ltd. D00000-DFFFFF (base 16) Shenzhen Haipengxin Electronic Co., Ltd. Block C3,Mingzhuo Xingye Technology Park,Guangming Avenue @@ -3719,12 +3713,6 @@ B00000-BFFFFF (base 16) Fibergate Inc. Beijing Beijing 100037 CN -9C-43-1E (hex) Phoenix Audio Technologies -E00000-EFFFFF (base 16) Phoenix Audio Technologies - 2552 White Road, Suite A - Irvine CA 92614 - US - 70-69-79 (hex) Rivian Automotive LLC C00000-CFFFFF (base 16) Rivian Automotive LLC 13250 N. Haggerty Road @@ -4874,6 +4862,168 @@ C00000-CFFFFF (base 16) Shenzhen Micro&Nano Perception Computing Technology Tokyo Minato 108-0023 JP +0C-86-29 (hex) BEIJING BEIBIANZHIDA TECHNOLOGY CO.,LTD +D00000-DFFFFF (base 16) BEIJING BEIBIANZHIDA TECHNOLOGY CO.,LTD + 27 Shucun West Road, Haidian District, + Beijing 100089 + CN + +0C-86-29 (hex) Shenzhen protostellar technology Co., Ltd +500000-5FFFFF (base 16) Shenzhen protostellar technology Co., Ltd + 4/F, #16, DaKan Yangmen industrial park, XiLi town, Nanshan district + ShenZhen GuangDong 518055 + CN + +0C-86-29 (hex) HONGKONG SAINT TECH INDUSTRIAL LIMITED +900000-9FFFFF (base 16) HONGKONG SAINT TECH INDUSTRIAL LIMITED + RM 1904A 19/F LUCKY COMMERCIAL CENTRE NO.103 DES VOEUX ROAD WEST HK + HONGKONG 999077 + CN + +0C-86-29 (hex) Akribis Systems +B00000-BFFFFF (base 16) Akribis Systems + Block 5012 Techplace II, #01-05 Ang Mo Kio Avenue 5 + Singapore Singapore 569876 + SG + +6C-93-08 (hex) Shenzhen C & D Electronics Co., Ltd. +500000-5FFFFF (base 16) Shenzhen C & D Electronics Co., Ltd. + 9th FIoor, Building 9, No.1 Qingxiang road, BaoNeng Science and TechnoIogy Industrial Park, Longhua New District + ShenZhen GuangDong 518000 + CN + +6C-93-08 (hex) Braums +000000-0FFFFF (base 16) Braums + Unit M 10-16 South st + Rydalmere NSW 2116 + AU + +50-FF-99 (hex) COYOTE SYSTEM +100000-1FFFFF (base 16) COYOTE SYSTEM + 24 quai Gallieni + Suresnes 92150 + FR + +30-43-D7 (hex) Apollo Infoways Private Limited +200000-2FFFFF (base 16) Apollo Infoways Private Limited + G-149, Sector-63 + Noida Uttar Pradesh 201301 + IN + +30-43-D7 (hex) Luxshare Electronic Technology (Kunshan) LTD +300000-3FFFFF (base 16) Luxshare Electronic Technology (Kunshan) LTD + No.158,Jinchang Road,Jinxi Town,Kunshan City,Jiangsu Province,215324, China + Kunshan Jiangsu 215324 + CN + +6C-93-08 (hex) WATERFORD CONSULTANTS LLC +100000-1FFFFF (base 16) WATERFORD CONSULTANTS LLC + 7430 NEW TECHNOLOGY WAY, Suite 150 + FREDERICK 21703 + US + +30-43-D7 (hex) DIGICITI Technology Co.,Ltd +700000-7FFFFF (base 16) DIGICITI Technology Co.,Ltd + Room 3502,Building 1,Huide Building,North Station Community,Minzhi Street,Longhua District + Shenzhen Guangdong 518000 + CN + +38-1F-26 (hex) SERNET (SUZHOU) TECHNOLOGIES CORPORATION +100000-1FFFFF (base 16) SERNET (SUZHOU) TECHNOLOGIES CORPORATION + NO.8 Tangzhuang Road,Suzhou Industrial Park,Su ZhouCity,JiangSu Province,China + Suzhou 215021 + CN + +38-1F-26 (hex) Avon Protection +800000-8FFFFF (base 16) Avon Protection + 503 8th Street + Cadillac MI 49601 + US + +18-A5-9C (hex) Thermia AB +500000-5FFFFF (base 16) Thermia AB + Snickaregatan 1 + Arvika 67134 + SE + +38-1F-26 (hex) SMS Evoko Group AB +900000-9FFFFF (base 16) SMS Evoko Group AB + Hastholmsvagen 32 + Nacka 13130 + SE + +18-A5-9C (hex) CAL-COMP INDUSTRIA E COMERCIO DE ELETRONICOS E INFORMATICA LTDA +B00000-BFFFFF (base 16) CAL-COMP INDUSTRIA E COMERCIO DE ELETRONICOS E INFORMATICA LTDA + AVENIDA TORQUATO TAPAJOS, 7503 TARUMA GALPAO 2 - CNPJ: 07.200.194/0003-80 + MANAUS AMAZONAS 69041-025 + BR + +18-A5-9C (hex) BlueEyes Technology +C00000-CFFFFF (base 16) BlueEyes Technology + 7F-3, No.200, Sec. 4, Wenxin Rd., + Taichung City Taiwan 40462 + TW + +9C-43-1E (hex) SHURE INCORPORATED +E00000-EFFFFF (base 16) SHURE INCORPORATED + 2552 White Road, Suite A + Irvine CA 92614 + US + +1C-59-74 (hex) Globe Tracker ApS +E00000-EFFFFF (base 16) Globe Tracker ApS + Strandgade 91 + kobenhavn State / Province* 1401 + DK + +C4-A1-0E (hex) Connectlab SRL +D00000-DFFFFF (base 16) Connectlab SRL + via donatello 30 + Milano Milano 20131 - Milano + IT + +C4-A1-0E (hex) BARTEC PIXAVI AS +100000-1FFFFF (base 16) BARTEC PIXAVI AS + Vestre Svanholmen 24 + SANDNES Rogaland 4313 + NO + +C4-A1-0E (hex) HYOSUNG HEAVY INDUSTRIES +000000-0FFFFF (base 16) HYOSUNG HEAVY INDUSTRIES + 119, Mapo-daero (Gongdeok-dong), Mapo-gu + Seoul 04144 + KR + +C4-A1-0E (hex) Consolinno Energy GmbH +300000-3FFFFF (base 16) Consolinno Energy GmbH + Franz-Mayer-Straße 1 + Regensburg Bayern 93053 + DE + +C4-A1-0E (hex) Ayla Networks (Shenzhen) Co., Ltd. +800000-8FFFFF (base 16) Ayla Networks (Shenzhen) Co., Ltd. + Room 1501, Building B, Innovation Building, No.198 Daxin Road, Majialong Community ,Nantou Street,Nanshan District, + Shenzhen 518000 + CN + +70-50-E7 (hex) Shenzhen Dangs Science and Technology CO.,Ltd. +800000-8FFFFF (base 16) Shenzhen Dangs Science and Technology CO.,Ltd. + 9th Floor of GDC Building, Gaoxin Middle 3rd St.,Nanshan District + Shenzhen GuangDong 518063 + CN + +70-50-E7 (hex) KFBIO (KONFOONG BIOINFORMATION TECH CO.,LTD) +E00000-EFFFFF (base 16) KFBIO (KONFOONG BIOINFORMATION TECH CO.,LTD) + 3F,No.4Building,Yuyao Technology Innovation Center + Ningbo ZheJiang Province, P.R.C. 315400 + CN + +80-02-F4 (hex) BK Networks Co,. Ltd. +000000-0FFFFF (base 16) BK Networks Co,. Ltd. + 330 Suin-ro, Gwonseon-gu + Suwon-si Gyeonggi-do 16371 + KR + 4C-4B-F9 (hex) Shenzhen dingsheng technology co., LTD 400000-4FFFFF (base 16) Shenzhen dingsheng technology co., LTD Floor 3, building 5, kaijeda industrial zone, no.97, huaxing road, langkou community, dalang street, longhua district @@ -9452,11 +9602,11 @@ D00000-DFFFFF (base 16) Shenzhen Vitalitim Technology Co., Ltd Guangzhou 511400 CN -10-54-D2 (hex) SHENZHEN CARSAFE TECHNOLOGY DEVELOPMENT CO.,LTD -700000-7FFFFF (base 16) SHENZHEN CARSAFE TECHNOLOGY DEVELOPMENT CO.,LTD - Bldg.7, N.Industrial Park,No.18 Makan Rd,Xili,Nanshan, - SHENZHEN 518000 - CN +10-54-D2 (hex) Raylogic Control Systems Private Limited +400000-4FFFFF (base 16) Raylogic Control Systems Private Limited + Unit 6, 3rd floor, HILIFE, PM Road, Santacruz West + Mumbai Maharashtra 400054 + IN 10-54-D2 (hex) Bamboo Dynamics Corporation., Ltd. 900000-9FFFFF (base 16) Bamboo Dynamics Corporation., Ltd. @@ -9464,11 +9614,215 @@ D00000-DFFFFF (base 16) Shenzhen Vitalitim Technology Co., Ltd Zhubei City, Hsinchu County Taiwan 302058 TW -10-54-D2 (hex) Raylogic Control Systems Private Limited -400000-4FFFFF (base 16) Raylogic Control Systems Private Limited - Unit 6, 3rd floor, HILIFE, PM Road, Santacruz West - Mumbai Maharashtra 400054 - IN +10-54-D2 (hex) SHENZHEN CARSAFE TECHNOLOGY DEVELOPMENT CO.,LTD +700000-7FFFFF (base 16) SHENZHEN CARSAFE TECHNOLOGY DEVELOPMENT CO.,LTD + Bldg.7, N.Industrial Park,No.18 Makan Rd,Xili,Nanshan, + SHENZHEN 518000 + CN + +0C-86-29 (hex) Shanghai Prophet Electronic Technology Co.,Ltd +000000-0FFFFF (base 16) Shanghai Prophet Electronic Technology Co.,Ltd + 9th Floor, Building 3, 1535 Hongmei Road, Xuhui District, + Shanghai 200030 + CN + +0C-86-29 (hex) Beijing Qinmu Data Technology Co., Ltd. +100000-1FFFFF (base 16) Beijing Qinmu Data Technology Co., Ltd. + Room101,Office 701,Floor7,Building4,Courtyard1,Nongda South Road,Haidian District. + Beijing 100085 + CN + +0C-86-29 (hex) C&A Marketing, INC. +600000-6FFFFF (base 16) C&A Marketing, INC. + 114 Tived Lane East + Edison NJ 08837 + US + +10-54-D2 (hex) Sybersense +500000-5FFFFF (base 16) Sybersense + Unit 10, 16F, Hi-Tech Industrial Centre, Block A, 5-21 Pat Tin Par Street, Tsuen Wan NT, HK + Hong Kong 999077 + CN + +0C-86-29 (hex) MyGregor Ltd +800000-8FFFFF (base 16) MyGregor Ltd + 11A, Carnegie str. + Sofia 1000 + BG + +6C-93-08 (hex) Annapurna labs +D00000-DFFFFF (base 16) Annapurna labs + Matam Scientific Industries Center, Building 8.2 + Mail box 15123 Haifa 3508409 + IL + +6C-93-08 (hex) Shenzhen TOPWAY Technology Co.,LTD +A00000-AFFFFF (base 16) Shenzhen TOPWAY Technology Co.,LTD + Bld.20 Zone 5, Baiwangxin Industry Park, Songbai Rd.Nanshan Dist + ShenZhen Guangdong 518055 + CN + +6C-93-08 (hex) Shenzhen DOOGEE Hengtong Technology CO., LTD +900000-9FFFFF (base 16) Shenzhen DOOGEE Hengtong Technology CO., LTD + B, 2/F, Building A4, Silicon Valley Power Digital Industrial Park, No. 22, Dafu Industrial Zone, Guanlan Aobei Community, Guanlan Street, Longhua New District + Shenzhen Guangdong 518000 + CN + +30-43-D7 (hex) SYMES SA +000000-0FFFFF (base 16) SYMES SA + 4 allée technopolis, chemin des presses + Cagnes sur mer PACA 06800 + FR + +30-43-D7 (hex) Shenzhen Mees Hi-Tech Co., Ltd +500000-5FFFFF (base 16) Shenzhen Mees Hi-Tech Co., Ltd + 2F & 4F,Building 3 North District,2nd Qianjin Road,Liutang Village, Xixiang,Bao'an District + Shenzhen Guangdong 518102 + CN + +30-43-D7 (hex) Annapurna labs +D00000-DFFFFF (base 16) Annapurna labs + Matam Scientific Industries Center, Building 8.2 + Mail box 15123 Haifa 3508409 + IL + +6C-93-08 (hex) ANDDORO LLC +E00000-EFFFFF (base 16) ANDDORO LLC + 1430 Broadway NY + New York NY 10018 + US + +38-1F-26 (hex) Zhejiang Huazhou Intelligent Equipment Co,. Ltd +500000-5FFFFF (base 16) Zhejiang Huazhou Intelligent Equipment Co,. Ltd + Building3, No.416DdongshengAvenue, Wuzhen, Tongxiang, + Jiaxing 314000 + CN + +38-1F-26 (hex) Synamedia +200000-2FFFFF (base 16) Synamedia + Luipaardstraat 12 + Kortrijk West-Vlaanderen 8500 + BE + +38-1F-26 (hex) Jade Bird Fire Co., Ltd. +C00000-CFFFFF (base 16) Jade Bird Fire Co., Ltd. + Jade Bird Building, 207 Chengfu Road, Haidian District, Beijing, P.R.China + Beijing 100871 + CN + +18-A5-9C (hex) Omwave +000000-0FFFFF (base 16) Omwave + 5 rue Barbes + Montrouge 92120 + FR + +18-A5-9C (hex) INTEGRAL PLUS +600000-6FFFFF (base 16) INTEGRAL PLUS + ul. Khalitova, 2 + Kazan 420029 + RU + +18-A5-9C (hex) estun automation co.,ltd +900000-9FFFFF (base 16) estun automation co.,ltd + 1888 Jiyin Avenue,Jiangning District + nanjing 211100 + CN + +1C-59-74 (hex) Shenzhen Geshem Technology Co Ltd +D00000-DFFFFF (base 16) Shenzhen Geshem Technology Co Ltd + 12th Floor, Block B, Building 7, International Innovation Valley + Shenzhen Gunagdong 518000 + CN + +1C-59-74 (hex) King-On Technology Ltd. +C00000-CFFFFF (base 16) King-On Technology Ltd. + 13F, No.207-2, Sec#3, Beixin Rd., Xindian District. + New Taipei Taiwan 23146 + TW + +1C-59-74 (hex) Topway Global Technology Limited +800000-8FFFFF (base 16) Topway Global Technology Limited + Room 1003, 10 / F, Tower 1, Lippo Centre, 89 Queensway, Hong Kong + Hong Kong Hong Kong 999077 + HK + +18-A5-9C (hex) Annapurna labs +D00000-DFFFFF (base 16) Annapurna labs + Matam Scientific Industries Center, Building 8.2 + Mail box 15123 Haifa 3508409 + IL + +18-A5-9C (hex) BMC Messsysteme GmbH +E00000-EFFFFF (base 16) BMC Messsysteme GmbH + Haupstr. 21 + Maisach 82216 + DE + +1C-59-74 (hex) Jiangsu Welm Technology Co.,Ltd +300000-3FFFFF (base 16) Jiangsu Welm Technology Co.,Ltd + No.158 Jianghai WestRoad,Haian + Haian Jiangsu 226100 + CN + +1C-59-74 (hex) Logical Infrastructure PTY LTD +100000-1FFFFF (base 16) Logical Infrastructure PTY LTD + unit 2, 8 Carbine way + Mornington Victoria 3931 + AU + +1C-59-74 (hex) Shenzhen Shi Fang Communication Technology Co., Ltd +500000-5FFFFF (base 16) Shenzhen Shi Fang Communication Technology Co., Ltd + 601-6 Mitehuapujing No.9 Jinxiu Mid Road Longtian Street Pingshan Distinct + Shenzhen Guangdong 518118 + CN + +1C-59-74 (hex) Lynxi Technologies Co.,Ltd. +700000-7FFFFF (base 16) Lynxi Technologies Co.,Ltd. + RM 801, 8/F, No. 67 North 4th Ring West Road + Beijing Beijing 100084 + CN + +6C-15-24 (hex) D-HOME SMAART +900000-9FFFFF (base 16) D-HOME SMAART + 8, rue Edouard Herriot + Marigny le Châtel 10350 + FR + +6C-15-24 (hex) Forcite Helmet Systems Pty Ltd +300000-3FFFFF (base 16) Forcite Helmet Systems Pty Ltd + 35-39 Bourke Road, Alexandria + Sydney NSW 2015 + AU + +6C-15-24 (hex) Kunshan Abram Software Technology Co.,Ltd. +600000-6FFFFF (base 16) Kunshan Abram Software Technology Co.,Ltd. + Room 704, No. 666, Changjiang South Road + Kunshan Jiangsu 215300 + CN + +6C-15-24 (hex) Magicyo Technology CO., LTD. +400000-4FFFFF (base 16) Magicyo Technology CO., LTD. + 7F, Tower A, YuZhou Building, No.78 North Keyuan + Shenzhen Nanshan District 518057 + CN + +70-50-E7 (hex) shenzhen newbridge communication equipment CO.,LTD +C00000-CFFFFF (base 16) shenzhen newbridge communication equipment CO.,LTD + 5 / F, No. 1 building, Jinli Industrial Park, No. 1, LanJin Sixth Road, Nanbu community, Longtian street, Pingshan District, Shenzhen + SHENZHEN GUANGDONG 518000 + CN + +70-50-E7 (hex) Quantumdoor Technologies, Inc. +400000-4FFFFF (base 16) Quantumdoor Technologies, Inc. + 1st Floor 108-1,Buiding5,East Districe,No.10 Xibeiwang East Road,haidian Districe, + beijing 102629 + CN + +80-02-F4 (hex) Shenzhen Suanzi Technology Co., Ltd +300000-3FFFFF (base 16) Shenzhen Suanzi Technology Co., Ltd + Room 207, Research Building, Tsinghua Information Port, No.1, Songpingshan New East Road, Nanshan District + Shenzhen Guangdong 518057 + CN 20-85-93 (hex) UNILUMIN GROUP CO.,LTD 300000-3FFFFF (base 16) UNILUMIN GROUP CO.,LTD @@ -14333,16 +14687,202 @@ A00000-AFFFFF (base 16) Pavana Technologies JSC. Vinh Phuc 35000 VN +10-54-D2 (hex) COSMO AIOT TECHNOLOGY CO LTD +E00000-EFFFFF (base 16) COSMO AIOT TECHNOLOGY CO LTD + Haier Information Industrial Complex, No.1 HaierRoad + Qingdao Shandong 266101 + CN + +10-54-D2 (hex) GIPS Technology Co., Ltd. +000000-0FFFFF (base 16) GIPS Technology Co., Ltd. + Rm. 2, 6F., No. 395, Sec. 1, Linsen Rd., East Dist. + Tainan City TAIWAN 701024 + TW + +10-54-D2 (hex) Embion B.V. +A00000-AFFFFF (base 16) Embion B.V. + Biestraat 1b + Gilze Noord-Brabant 5126NH + NL + +10-54-D2 (hex) Little Array Technology (Shenzhen) Co., Ltd. +300000-3FFFFF (base 16) Little Array Technology (Shenzhen) Co., Ltd. + Unit 215, 2F, A1, Zhimei Industry Park, Fuhai Industrial Zone B2, Fuyong Street, Baoan District + Shenzhen Guangdong 518103 + CN + 10-54-D2 (hex) LUXSHARE-ICT Co., Ltd. C00000-CFFFFF (base 16) LUXSHARE-ICT Co., Ltd. 1F, No. 22, Lane 35, Jihu Road, Neihu district Taipei City Taiwan 114754 TW -10-54-D2 (hex) Little Array Technology (Shenzhen) Co., Ltd. -300000-3FFFFF (base 16) Little Array Technology (Shenzhen) Co., Ltd. - Unit 215, 2F, A1, Zhimei Industry Park, Fuhai Industrial Zone B2, Fuyong Street, Baoan District - Shenzhen Guangdong 518103 +0C-86-29 (hex) SHENZHEN YINGMU TECHNOLOGY.,LTD +C00000-CFFFFF (base 16) SHENZHEN YINGMU TECHNOLOGY.,LTD + 8 / F, Zone D, building F1, TCL International E city, Shuguang community, Xili street, Nanshan District, + Shenzhen 518000 + CN + +0C-86-29 (hex) Nipron Co.,Ltd +A00000-AFFFFF (base 16) Nipron Co.,Ltd + 1-3-30 Nishinagasucho + Amagasaki-shi Hyogo-ken 660-0805 + JP + +0C-86-29 (hex) FX TECHNOLOGY LIMITED +E00000-EFFFFF (base 16) FX TECHNOLOGY LIMITED + 38a High Street, Northwood + Middlesex - HA6 1BN + GB + +6C-93-08 (hex) LightnTec GmbH +300000-3FFFFF (base 16) LightnTec GmbH + Haid-und-Neu-Strasse 7 + Karlsruhe 76131 + DE + +30-43-D7 (hex) Sprocomm Technologies Co., Ltd.Guangming Branch +600000-6FFFFF (base 16) Sprocomm Technologies Co., Ltd.Guangming Branch + Area A 3rd Floor, Area A 5rd Floor and 6th Floor, 301, building 2, 7th Industrial Park, Yulv Community,Yutang Street, Guangming District, + Shenzhen 518000 + CN + +6C-93-08 (hex) Shenzhen haichangxing Technology Co., Ltd. +C00000-CFFFFF (base 16) Shenzhen haichangxing Technology Co., Ltd. + Room 3102, 31 / F, Wen an Center, Wenjin Square, Luohu + SHENZHEN GUANGZHOU 518000 + CN + +30-43-D7 (hex) Shenzhen juduoping Technology Co.,Ltd +100000-1FFFFF (base 16) Shenzhen juduoping Technology Co.,Ltd + Baoan Xin'an Streat + Shenzhen 002052 + CN + +30-43-D7 (hex) Guangdong Hongqin Telecom Technology Co. Ltd. +E00000-EFFFFF (base 16) Guangdong Hongqin Telecom Technology Co. Ltd. + 10 Keyuan Road, Songshan Lake + Dongguan Guangdong 523808 + CN + +38-1F-26 (hex) Bosch Automotive Electronics India Pvt. Ltd. +300000-3FFFFF (base 16) Bosch Automotive Electronics India Pvt. Ltd. + Naganathapura + Bengaluru Karnataka 560100 + IN + +18-A5-9C (hex) ePower Network Solution Co., Ltd. +700000-7FFFFF (base 16) ePower Network Solution Co., Ltd. + No. 2, Aly. 1, Ln. 85, Xinshu Rd., Xinzhuang Dist., + New Taipei City , 242063 + TW + +18-A5-9C (hex) Erba Lachema s.r.o. +A00000-AFFFFF (base 16) Erba Lachema s.r.o. + Karasek1d + Brno 62100 + CZ + +1C-59-74 (hex) Shenzhen Hanshine Technology Co.Ltd. +000000-0FFFFF (base 16) Shenzhen Hanshine Technology Co.Ltd. + Buiding 2 ,row 3,number 2 industrail zone,yulv community,Yutang street + Shenzhen Guangdong 518000 + CN + +1C-59-74 (hex) Chongqing Taishan Cable Co., Ltd +200000-2FFFFF (base 16) Chongqing Taishan Cable Co., Ltd + 17 Shiyan Avenue, Yufengshan Town, Yubei District, + Chongqing 400000 + CN + +6C-15-24 (hex) Telsonic AG +100000-1FFFFF (base 16) Telsonic AG + Industriestrasse 6b + Bronschhofen St.Gallen 9552 + CH + +18-A5-9C (hex) Beijing QS Medical Technology Co., Ltd. +300000-3FFFFF (base 16) Beijing QS Medical Technology Co., Ltd. + Building 5, No.11, Kechuang 14th Street, Economic-Technological Development Area + Beijing 100176 + CN + +6C-15-24 (hex) SYMLINK CORPORATION +D00000-DFFFFF (base 16) SYMLINK CORPORATION + 6F., No. 13, Lane. 35, Jihu Rd., Neihu Dist., Neihu Technology Park + Taipei 11492 + TW + +C4-A1-0E (hex) Clinton Electronics Corporation +B00000-BFFFFF (base 16) Clinton Electronics Corporation + 6701 Clinton Road + Loves Park IL 61111 + US + +C4-A1-0E (hex) Wistron InfoComn (Kunshan) Co., Ltd. +200000-2FFFFF (base 16) Wistron InfoComn (Kunshan) Co., Ltd. + No.88 Hongyan Road, Kunshan Economic & Technological Development Zone + Kunshan Jiangsu 215300 + CN + +C4-A1-0E (hex) Guangzhou South Satellite Navigation Instrument Co., Ltd. +700000-7FFFFF (base 16) Guangzhou South Satellite Navigation Instrument Co., Ltd. + Area A Layer 6, Area A Layer 5, Area A Layer 4, No.39, Sicheng Road, Tianhe District, + Guangzhou GuangDong 510663 + CN + +6C-15-24 (hex) Motium Pty Ltd +700000-7FFFFF (base 16) Motium Pty Ltd + 11/4 Brodie Hall Drive, + Bentley Western Australia 6102 + AU + +6C-15-24 (hex) AEC s.r.l. +E00000-EFFFFF (base 16) AEC s.r.l. + Via Zambon, 33/A + Creazzo Vicenza 36051 + IT + +70-50-E7 (hex) Shenzhen C & D Electronics Co., Ltd. +000000-0FFFFF (base 16) Shenzhen C & D Electronics Co., Ltd. + 9th FIoor, Building 9, No.1 Qingxiang road, BaoNeng Science and TechnoIogy Industrial Park, Longhua New District + ShenZhen GuangDong 518000 + CN + +70-50-E7 (hex) Beijing Shannoncyber Technology Co.,Ltd +B00000-BFFFFF (base 16) Beijing Shannoncyber Technology Co.,Ltd + 913 9/F,building 8,yard 2,Shenggu Middle Road,Chaoyang District + beijing beijing 100029 + CN + +C4-A1-0E (hex) Harbour Cross Technology Ltd +400000-4FFFFF (base 16) Harbour Cross Technology Ltd + Unit 622 One Island South, 2 Heung Yip Road, Wong Chuk Hang, + Hong Kong China 000000 + HK + +70-50-E7 (hex) Annapurna labs +100000-1FFFFF (base 16) Annapurna labs + Matam Scientific Industries Center, Building 8.2 + Mail box 15123 Haifa 3508409 + IL + +80-02-F4 (hex) XUNDI(XIAMEN) ELECTRONIC TECHNOLOGY CO.,LTD. +900000-9FFFFF (base 16) XUNDI(XIAMEN) ELECTRONIC TECHNOLOGY CO.,LTD. + SECOND FLOOR, NO. 943-4, TONGLONG 2ND ROAD, TORCH HIGH-TECH (XIANG 'AN) INDUSTRY DISTRICT,XIAMEN CITY, FUJIAN PROVINCE,CHINA + XIAMEN 361106 + CN + +80-02-F4 (hex) PassiveLogic +A00000-AFFFFF (base 16) PassiveLogic + 6405 S 3000 E, Suite 300 + Holladay UT 84121 + US + +80-02-F4 (hex) Sichuan Fanyi Technology Co. Ltd. +500000-5FFFFF (base 16) Sichuan Fanyi Technology Co. Ltd. + No. 1707, Unit 1, Building 1, 888, Middle Section of Yizhou Avenue, Gaoxin District + Chengdu Sichuan 650000 CN 4C-4B-F9 (hex) Shandong Linkotech Electronic Co., Ltd. @@ -17420,12 +17960,6 @@ A00000-AFFFFF (base 16) Winsonic Electronics Co., Ltd. Suwon-si Gyeonggi-do 443-270 KR -7C-70-BC (hex) Ametek VIS -A00000-AFFFFF (base 16) Ametek VIS - 287 27 Road - Grand Junction CO 81503 - US - BC-34-00 (hex) Parlay Labs dba Highfive C00000-CFFFFF (base 16) Parlay Labs dba Highfive 471 Emerson St. @@ -19067,12 +19601,6 @@ DC-36-43 (hex) Hangzhou Chingan Tech Co., Ltd. hangzhou zhejiang 310000 CN -08-26-AE (hex) Flextronics International Kft. -A00000-AFFFFF (base 16) Flextronics International Kft. - Zrínyi Miklós str. 38. - Zalaegerszeg 8900 - HU - 08-26-AE (hex) Wuhan Tianyu Information Industry Co., Ltd. 000000-0FFFFF (base 16) Wuhan Tianyu Information Industry Co., Ltd. Tianyu Building, S.&T.Park, Huazhong University of S.&T.,East Lake Development Zone @@ -19109,12 +19637,6 @@ DC-36-43 (hex) Hefei EA Excelsior Information Security Co., Ltd. Wijk en Aalburg 4261 LN NL -10-54-D2 (hex) ComNav Technology Ltd. -200000-2FFFFF (base 16) ComNav Technology Ltd. - 3 floor Building 2,No.618 Chengliu Middle RD. Malu town, - Shanghai 200000 - CN - 10-54-D2 (hex) Shenzhen Dinstech Technology Co.,Ltd. B00000-BFFFFF (base 16) Shenzhen Dinstech Technology Co.,Ltd. Shenzhen Qianhai Shenzhen-Hong Kong Cooperation Zone Nanshan Street Linhai Avenue No. 59 Seaside Avenue 3rd Floor D378, Port Building, Shipping Center @@ -19127,6 +19649,186 @@ D00000-DFFFFF (base 16) Sun wealth technology corporation limited shenzhen Guang dong 518000 CN +10-54-D2 (hex) ComNav Technology Ltd. +200000-2FFFFF (base 16) ComNav Technology Ltd. + 3 floor Building 2,No.618 Chengliu Middle RD. Malu town, + Shanghai 200000 + CN + +10-54-D2 (hex) Jiangxi Ofilm&Jvneng IoT Tech Co., Ltd. +100000-1FFFFF (base 16) Jiangxi Ofilm&Jvneng IoT Tech Co., Ltd. + Building 2 and 3,Intelligent Technology Industrial Park,high-tech industrial Development Zone, + Yingtan 335000 + CN + +0C-86-29 (hex) BADA SYSTEM co., Ltd +200000-2FFFFF (base 16) BADA SYSTEM co., Ltd + Saemalro 99 Kumsuk building 501 + Seoul 05808 + KR + +0C-86-29 (hex) HagerEnergy GmbH +700000-7FFFFF (base 16) HagerEnergy GmbH + Karlstrasse 5 + Osnabrueck 49074 + DE + +6C-93-08 (hex) Shenzhen EZpro Sound & Light Technology Co., Ltd. +B00000-BFFFFF (base 16) Shenzhen EZpro Sound & Light Technology Co., Ltd. + E2?TCL International E City,1001 Zhong Shan Yuan Rd,Nanshan District + shenz guangdong 518055 + CN + +30-43-D7 (hex) Xiaoniu network technology (Shanghai) Co., Ltd. +C00000-CFFFFF (base 16) Xiaoniu network technology (Shanghai) Co., Ltd. + Room 706, building 3, no.20 east road, jingan district. + Shang hai 200040 + CN + +6C-93-08 (hex) Estelar s.r.o +400000-4FFFFF (base 16) Estelar s.r.o + Palackého 744/1 + Holešov ?eská republika 76901 + CZ + +08-26-AE (hex) Flextronics International Kft +A00000-AFFFFF (base 16) Flextronics International Kft + Zrínyi Miklós str. 38. + Zalaegerszeg 8900 + HU + +38-1F-26 (hex) HWACHANG CORPORATION +D00000-DFFFFF (base 16) HWACHANG CORPORATION + 90, NONGGONGANJI-GIL + SOCHO-SI 24899 + KR + +38-1F-26 (hex) Airmaster A/S +400000-4FFFFF (base 16) Airmaster A/S + Industrivej 59 + Aars 9600 + DK + +18-A5-9C (hex) Actiontec Electronics Inc. +200000-2FFFFF (base 16) Actiontec Electronics Inc. + 2445 Augustine Dr #501 + Santa Clara CA 95054 + US + +18-A5-9C (hex) IT-1 +400000-4FFFFF (base 16) IT-1 + 260, Changnyong-daero + Yeongtong-gu, Suwon-si Gyeonggi-do 16229 + KR + +18-A5-9C (hex) Residence Control Ltd +800000-8FFFFF (base 16) Residence Control Ltd + Cvetan Vuchkov 7 + Sofia Sofia 1614 + BG + +7C-70-BC (hex) Motec GmbH +A00000-AFFFFF (base 16) Motec GmbH + 287 27 Road + Grand Junction CO 81503 + US + +1C-59-74 (hex) Syntax technology(tianjin)Co.,LTD +400000-4FFFFF (base 16) Syntax technology(tianjin)Co.,LTD + Room 510-5,Comprehensive Office Building,Carpet Industrial Park,Wuqing District + Tianjin Tianjin 301700 + CN + +1C-59-74 (hex) Beijing Flintec Electronic Technology Co.,Ltd. +B00000-BFFFFF (base 16) Beijing Flintec Electronic Technology Co.,Ltd. + Room 102,Building No.6,China Technology Venture Park,No.8,LaiGuangYing West Road,ChaoYang District + Beijing Beijing 100012 + CN + +1C-59-74 (hex) Shanghai Laisi Information Technology Co.,Ltd +900000-9FFFFF (base 16) Shanghai Laisi Information Technology Co.,Ltd + 1001,21#,No.1158 Zhongxin RD,Songjiang district Shanghai + shanghai 201614 + CN + +6C-15-24 (hex) Shenzhen Electron Technology Co., LTD. +500000-5FFFFF (base 16) Shenzhen Electron Technology Co., LTD. + Building 2, Yingfeng Industrial Zone, Tantou Community, Songgang Street, Bao'an District + Shenzhen Guangzhou 51800 + CN + +6C-15-24 (hex) ShenZhen Chainway Information Technology Co., Ltd. +800000-8FFFFF (base 16) ShenZhen Chainway Information Technology Co., Ltd. + 9F Building2, Phase2, Gaoxinqi Industrial Park , Bao'an District + ShenZhen GuangDong 518102 + CN + +6C-15-24 (hex) Annapurna labs +B00000-BFFFFF (base 16) Annapurna labs + Matam Scientific Industries Center, Building 8.2 + Mail box 15123 Haifa 3508409 + IL + +1C-59-74 (hex) Square Inc. +600000-6FFFFF (base 16) Square Inc. + 1455 Market St. + San Francisco CA 94103 + US + +6C-15-24 (hex) DEFA AS +000000-0FFFFF (base 16) DEFA AS + Blingsmovegen 30 + Nesbyen 3540 + NO + +6C-15-24 (hex) Linkplay +200000-2FFFFF (base 16) Linkplay + 891 W. Washington Ave. + Sunnyvale CA 94086 + US + +6C-15-24 (hex) CORAL-TAIYI +C00000-CFFFFF (base 16) CORAL-TAIYI + 8F-3, No. 200, Gangqian Rd, Neihu District + Taipei City 114 + TW + +C4-A1-0E (hex) O-NET Industrial Technologies (Shenzhen) Limited +500000-5FFFFF (base 16) O-NET Industrial Technologies (Shenzhen) Limited + 501, Maile building, building 2, No. 28, Cuijing Road, Zhukeng community, Longtian street, Pingshan District + Shenzhen Guangdong 518118 + CN + +70-50-E7 (hex) Nippon Pulse America, Inc. +600000-6FFFFF (base 16) Nippon Pulse America, Inc. + 4 Corporate Drive + Radford VA 24141-5100 + US + +C4-A1-0E (hex) Focus-on +C00000-CFFFFF (base 16) Focus-on + Kerkeplaat 12 + Dordrecht 3313LC + NL + +70-50-E7 (hex) Eta Compute Inc. +D00000-DFFFFF (base 16) Eta Compute Inc. + 182 S. Murphy Ave + Sunnyvale CA 94086 + US + +80-02-F4 (hex) Sichuan lookout environment protection technology co.,Ltd +100000-1FFFFF (base 16) Sichuan lookout environment protection technology co.,Ltd + No. 1015, floor 10, unit 2, building 1, No. 1616, Nanhua Road, high tech Zone + Chengdu Sichuan 610052 + CN + +80-02-F4 (hex) Wuhan Glory Road Intelligent Technology Co., Ltd. +C00000-CFFFFF (base 16) Wuhan Glory Road Intelligent Technology Co., Ltd. + 18F,Magic Cube Building,Optics Valley Core Center,No.303 Optics Valley Avenue,East Lake High-tech Development Zone + Wuhan Hubei 430073 + CN + 20-85-93 (hex) Great Lite International 700000-7FFFFF (base 16) Great Lite International 11F., No.207-2, Sec. 3, Beixin Rd., Xindian Dist., @@ -22970,12 +23672,6 @@ CC-4F-5C (hex) Dtrovision Fair Lawn NJ 07410 US -CC-4F-5C (hex) Buttons (Beijing) Technology Limited -E00000-EFFFFF (base 16) Buttons (Beijing) Technology Limited - Room 202, Floor 2, Building No. 3, No. 9 Xiaoying Road, Chaoyang District - Beijing 100101 - CN - CC-4F-5C (hex) AZ-TECHNOLOGY SDN BHD A00000-AFFFFF (base 16) AZ-TECHNOLOGY SDN BHD A108 & A109 BLOCK A KELANA BUSINESS CENTRE NO: 97 JALAN SS7/2 KELANA JAYA @@ -23180,12 +23876,6 @@ A0-02-4A (hex) Vitec Imaging Solutions Spa Cassola Vicenza 36022 IT -78-D4-F1 (hex) Cartender -100000-1FFFFF (base 16) Cartender - Via della Meccanica 2a - Padova PD 35127 - IT - 78-D4-F1 (hex) Jiangsu byzoro intelligent technology Co.,Ltd B00000-BFFFFF (base 16) Jiangsu byzoro intelligent technology Co.,Ltd Room 301, Building D, Yunmi City, No.19 Ningshuang Road, Yuhuatai District @@ -23923,3 +24613,201 @@ E00000-EFFFFF (base 16) CEL Terminus (Shanghai) Information Technologies Co Matam Scientific Industries Center, Building 8.2 Mail box 15123 Haifa 3508409 IL + +10-54-D2 (hex) Lanao Communication Technology Limited +600000-6FFFFF (base 16) Lanao Communication Technology Limited + #B2, Zhongbaotong Creative Park Changfa West Road No.34 Bantian + Shenzhen 518029 + CN + +0C-86-29 (hex) Ag Express Electronics +400000-4FFFFF (base 16) Ag Express Electronics + 6280 NE 14th St + Des Moines IA 50313 + US + +6C-93-08 (hex) Liberty AV Solutions +700000-7FFFFF (base 16) Liberty AV Solutions + 1490 Garden of the Gods Road + Colorado Springs CO 80907 + US + +6C-93-08 (hex) Uconfree technology(shenzhen)limited +600000-6FFFFF (base 16) Uconfree technology(shenzhen)limited + Room 311 PuFeng commercial center PingHu street LongGang District ShenZhen China + SHENZHEN 518111 + CN + +0C-86-29 (hex) Annapurna labs +300000-3FFFFF (base 16) Annapurna labs + Matam Scientific Industries Center, Building 8.2 + Mail box 15123 Haifa 3508409 + IL + +6C-93-08 (hex) Hangzhou Risco System Co.,Ltd +800000-8FFFFF (base 16) Hangzhou Risco System Co.,Ltd + No. 19, Naxian street, Liangzhu street, Yuhang District + Hangzhou City Zhejiang Province 31000 + CN + +30-43-D7 (hex) Bodhi +A00000-AFFFFF (base 16) Bodhi + 3150 W. Prospect Road, Suite 330 + Fort Lauderdale FL 33309 + US + +6C-93-08 (hex) ZHEJIANG XIAN DA Environmental Technology Co., Ltd +200000-2FFFFF (base 16) ZHEJIANG XIAN DA Environmental Technology Co., Ltd + Room 103,1st F,unit A,Buliding 3,No. 8,Xiyuan 1st Road,Sandun Town,Xihu District + HANGZHOU ZHEJIANG 310000 + CN + +30-43-D7 (hex) Kesu (Shanghai) Electronic Technology Co., Ltd +800000-8FFFFF (base 16) Kesu (Shanghai) Electronic Technology Co., Ltd + 3-36588?No. 1800, Panyuan Road, Changxing Town, Chongming District + Shanghai 202150 + CN + +30-43-D7 (hex) PK Solutions LLC +900000-9FFFFF (base 16) PK Solutions LLC + 10811 E Harry + Wichita KS 67207 + US + +30-43-D7 (hex) FIBERME COMMUNICATIONS LLC +400000-4FFFFF (base 16) FIBERME COMMUNICATIONS LLC + 1749 Old Meadow Rd. + McLean VA 22102 + US + +38-1F-26 (hex) NOITAC sp. z o.o. sp.k. +600000-6FFFFF (base 16) NOITAC sp. z o.o. sp.k. + Szlak 28/3 + Kraków 31-153 + PL + +38-1F-26 (hex) JAESUNG INFORMATION & COMMUNICATION CO.LTD +000000-0FFFFF (base 16) JAESUNG INFORMATION & COMMUNICATION CO.LTD + 41-69, POWOL SAEMALGIL, YANG YANG-EUP + YANG YANG GUN 25017 + KR + +38-1F-26 (hex) Deutronic Elektronik GmbH +B00000-BFFFFF (base 16) Deutronic Elektronik GmbH + Deutronicstraße 5 + Adlkofen Bayern 84166 + DE + +38-1F-26 (hex) Annapurna labs +E00000-EFFFFF (base 16) Annapurna labs + Matam Scientific Industries Center, Building 8.2 + Mail box 15123 Haifa 3508409 + IL + +38-1F-26 (hex) RCE systems s.r.o. +700000-7FFFFF (base 16) RCE systems s.r.o. + Svatopluka Cecha 2008/1d + Brno CR 61200 + CZ + +38-1F-26 (hex) Sercomm Corporation. +A00000-AFFFFF (base 16) Sercomm Corporation. + 3F,No.81,Yu-Yih Rd.,Chu-Nan Chen + Miao-Lih Hsuan 115 + TW + +30-43-D7 (hex) Motec GmbH +B00000-BFFFFF (base 16) Motec GmbH + 287 27 Road + Grand Junction CO 81503 + US + +CC-4F-5C (hex) Beijing Techao Weijia Technology Limited +E00000-EFFFFF (base 16) Beijing Techao Weijia Technology Limited + Room 202, Floor 2, Building No. 3, No. 9 Xiaoying Road, Chaoyang District + Beijing 100101 + CN + +1C-59-74 (hex) Council Rock +A00000-AFFFFF (base 16) Council Rock + 11 Centre Park + Rochester 14614 + US + +C4-A1-0E (hex) Hainan World Electronic Science and Techology Co.,Ltd +600000-6FFFFF (base 16) Hainan World Electronic Science and Techology Co.,Ltd + Room 1502,15th Floor,Building 20,NO.487 Tianlin Road,Shanghai,200233 China + Shanghai Shanghai 200233 + CN + +C4-A1-0E (hex) Jiangsu Perceive World Technology Co.,Ltd. +A00000-AFFFFF (base 16) Jiangsu Perceive World Technology Co.,Ltd. + 4-5F,Hengsheng Science Park 70#,Zhonghui Ave 1588#,HuiShan District,Wuxi,Jiangsu,China + Wu xi Jiangsu 214181 + CN + +78-D4-F1 (hex) Silla Industries +100000-1FFFFF (base 16) Silla Industries + Via della Meccanica 2a + Padova PD 35127 + IT + +18-A5-9C (hex) Cuman +100000-1FFFFF (base 16) Cuman + Al-Farabi 97/1 + Nur-Sultan 010000 + KZ + +C4-A1-0E (hex) XI'AN YEP TELECOM TECHNOLOGY CO.,LTD +900000-9FFFFF (base 16) XI'AN YEP TELECOM TECHNOLOGY CO.,LTD + No.211 Tiangu 8th Road, High-tech Zone + Xi 'an Shaanxi 710065 + CN + +6C-15-24 (hex) STERIS +A00000-AFFFFF (base 16) STERIS + Unit 7 & 8, Stortford Hall Industrial Park, Dunmow Road + Bishops Stortford herts CM23 5GZ + GB + +C4-A1-0E (hex) Alio, Inc +E00000-EFFFFF (base 16) Alio, Inc + 10901 W. 120th Ave, Suite 380 + Bloomfield CO 80021 + US + +70-50-E7 (hex) Wall Box Chargers, S.L. +500000-5FFFFF (base 16) Wall Box Chargers, S.L. + Paseo Castellana 95, 28 floor + Madrid Madrid 28046 + ES + +70-50-E7 (hex) Guangzhou Tianhe High Tech Industrial Development Zone Zhongsheng Electrical Limited Company +A00000-AFFFFF (base 16) Guangzhou Tianhe High Tech Industrial Development Zone Zhongsheng Electrical Limited Company + D01, Zone D, No. 6 (Yishun), Huangcun North Road, Tianhe District + Guangzhou 510660 + CN + +70-50-E7 (hex) Electronic's Time SRL +200000-2FFFFF (base 16) Electronic's Time SRL + Via Madonna Piccola 32R/Q + Martina Franca Taranto 74015 + IT + +70-50-E7 (hex) Yoctopuce +700000-7FFFFF (base 16) Yoctopuce + Route de Cartigny 33 + Cartigny 1236 + CH + +70-50-E7 (hex) Skychers Creations ShenZhen Limited +300000-3FFFFF (base 16) Skychers Creations ShenZhen Limited + Room 907A, 9/F, Block T2, FongDa City, Longjing Village, Longzhu Avenue, Nanshan District + Shenzhen Guangdong 518073 + CN + +70-50-E7 (hex) Elastics.cloud +900000-9FFFFF (base 16) Elastics.cloud + 1730 North First Street, 5th Floor + San Jose CA 95112 + US diff --git a/hwdb.d/ma-small.txt b/hwdb.d/ma-small.txt index d6fd58e50..6a2179555 100644 --- a/hwdb.d/ma-small.txt +++ b/hwdb.d/ma-small.txt @@ -5687,6 +5687,12 @@ F74000-F74FFF (base 16) GE AVIC Civil Avionics Systems Company Limited Shanghai 200241 CN +8C-1F-64 (hex) Shenzhen zhushida Technology lnformation Co.,Ltd +A5D000-A5DFFF (base 16) Shenzhen zhushida Technology lnformation Co.,Ltd + 1309, Block A, Innovation Building, Majialong Industrial Zone, Nantou Street, Nanshan District, + SHENZHEN 518000 + CN + 8C-1F-64 (hex) EnviroNode IoT Solutions 1AF000-1AFFFF (base 16) EnviroNode IoT Solutions 4 Malvern Avenue @@ -5699,6 +5705,204 @@ F74000-F74FFF (base 16) GE AVIC Civil Avionics Systems Company Limited Weyarn Bayern 83629 DE +8C-1F-64 (hex) Gateview Technologies +B7B000-B7BFFF (base 16) Gateview Technologies + 104 White St #201 + Wake Forest 27587 + US + +8C-1F-64 (hex) Suntech Engineering +7D3000-7D3FFF (base 16) Suntech Engineering + 30, Gukgasandan-daero 34-gil, Guji-myeon, Dalseong-gun, Daegu, Republic of Korea + Daegu 43008 + KR + +8C-1F-64 (hex) Ascon Tecnologic S.r.l. +FF6000-FF6FFF (base 16) Ascon Tecnologic S.r.l. + via Indipendenza, 56 + Vigevano PV 27029 + IT + +8C-1F-64 (hex) Delta Computers LLC. +59F000-59FFFF (base 16) Delta Computers LLC. + Office 22/10, room part 22, room IV, floor 3, 41A, 3-rd Parkovaya str. + Moscow 105425 + RU + +8C-1F-64 (hex) Newtec A/S +BF0000-BF0FFF (base 16) Newtec A/S + Stærmosegårdsvej 18 + Odense SV Region Syd 5230 + DK + +8C-1F-64 (hex) Onto Innovation +FBA000-FBAFFF (base 16) Onto Innovation + 16 Jonspin rd + Wilmington MA 01887 + US + +8C-1F-64 (hex) Becton Dickinson +775000-775FFF (base 16) Becton Dickinson + 7 Loveton Circle + Sparks MD 21152 + US + +8C-1F-64 (hex) FIBERME COMMUNICATIONS LLC +C68000-C68FFF (base 16) FIBERME COMMUNICATIONS LLC + 1749 Old Meadow Rd. + McLean VA 22102 + US + +8C-1F-64 (hex) TechArgos +BFB000-BFBFFF (base 16) TechArgos + Nizhnyaya Krasnoselskaya Str. 35-64 + Moscow 105066 + RU + +8C-1F-64 (hex) DAVE SRL +967000-967FFF (base 16) DAVE SRL + VIA TALPONEDO 29/A + PORCIA PORDENONE 330850 + IT + +8C-1F-64 (hex) e.kundenservice Netz GmbH +855000-855FFF (base 16) e.kundenservice Netz GmbH + Steindamm 100 + Hamburg 20099 + DE + +8C-1F-64 (hex) Meiryo Denshi Corp. +EB5000-EB5FFF (base 16) Meiryo Denshi Corp. + 38-23 higashi maeda + Nishin City Aichi 470-0124 + JP + +8C-1F-64 (hex) Baker Hughes EMEA +40E000-40EFFF (base 16) Baker Hughes EMEA + Sensing House, Shannon Free Zone East + Shannon Co. Clare V14 V99 + IE + +8C-1F-64 (hex) Calnex Solutions plc +703000-703FFF (base 16) Calnex Solutions plc + Oracle Campus + Linlithgow West Lothian EH49 7LR + GB + +8C-1F-64 (hex) LLC NTPC +660000-660FFF (base 16) LLC NTPC + Kharkovsky alley 36g, office room 1 + Belgorod 308012 + RU + +8C-1F-64 (hex) Renukas Castle Hard- and Software +4E5000-4E5FFF (base 16) Renukas Castle Hard- and Software + Renukas Castle, 35th Ward, Kalyan Nagar, Ring Road, near Lions School + Gadag Karnataka 582103 + IN + +8C-1F-64 (hex) Tesat-Spacecom GmbH & Co. KG +F27000-F27FFF (base 16) Tesat-Spacecom GmbH & Co. KG + Gerberstrasse 49 + Backnang 71522 + DE + +8C-1F-64 (hex) MB connect line GmbH Fernwartungssysteme +059000-059FFF (base 16) MB connect line GmbH Fernwartungssysteme + Winnettener Straße 6 + Dinkelsbuehl Bavaria 91550 + DE + +8C-1F-64 (hex) JBF +F45000-F45FFF (base 16) JBF + via goretta 90 + mappano torino 10079 + IT + +8C-1F-64 (hex) REFU Storage System GmbH +53B000-53BFFF (base 16) REFU Storage System GmbH + Marktstraße 185 + Pfullingen 72793 + DE + +8C-1F-64 (hex) DEUTA-WERKE GmbH +883000-883FFF (base 16) DEUTA-WERKE GmbH + Paffrather Str. 140 + Bergisch Gladbach North Rhine-Westphalia 51465 + DE + +8C-1F-64 (hex) Micro Electroninc Products +765000-765FFF (base 16) Micro Electroninc Products + TT Vasumweg 150 + Amsterdam 1033 SH + NL + +8C-1F-64 (hex) Logical Product +622000-622FFF (base 16) Logical Product + 2-25-5,matoba,minamiku + Fukuoka Fukuoka 811-1314 + JP + +8C-1F-64 (hex) Dan Smith LLC +4D6000-4D6FFF (base 16) Dan Smith LLC + 4638 Cameron Ridge Drive, Apt 138 + Indianapolis IN 46240 + US + +8C-1F-64 (hex) INVIXIUM ACCESS INC +274000-274FFF (base 16) INVIXIUM ACCESS INC + 111 Gordon Baker Road, Suite #300 + Toronto Ontario M2H 3R1 + CA + +8C-1F-64 (hex) Sicon srl +B3B000-B3BFFF (base 16) Sicon srl + Via Sila 1/3 + Isola Vicentina Vicenza 36033 + IT + +8C-1F-64 (hex) Sanchar Telesystems limited +958000-958FFF (base 16) Sanchar Telesystems limited + A-78, GROUND FLOOR, OKHLA INDUSTRIAL AREA, PHASE - II, NEW DELHI + New Delhi Delhi 110020 + IN + +8C-1F-64 (hex) Tantronic AG +1EF000-1EFFFF (base 16) Tantronic AG + Gewerbering 12 + Wohlen AG 5610 + CH + +8C-1F-64 (hex) Tunstall A/S +F2C000-F2CFFF (base 16) Tunstall A/S + Niels Bohrs vej 42 + Stilling Skanderborg 8660 + DK + +8C-1F-64 (hex) Bunka Shutter Co., Ltd. +0B0000-0B0FFF (base 16) Bunka Shutter Co., Ltd. + 644-1 Tenjingoe,Ooaza-Kamiishizuka + Oyama Tochigi 323-0063 + JP + +8C-1F-64 (hex) Rodgers Instruments US LLC +A42000-A42FFF (base 16) Rodgers Instruments US LLC + 6497 NE Croeni Avenue + Hillsboro 97124 + US + +8C-1F-64 (hex) Potter Electric Signal Company +316000-316FFF (base 16) Potter Electric Signal Company + 1609 Park 370 Place + Hazelwood MO 63042 + US + +8C-1F-64 (hex) Diffraction Limited +8CF000-8CFFFF (base 16) Diffraction Limited + 59 Grenfell Crescent, Unit B + Ottawa ON K2G 0G3 + CA + 70-B3-D5 (hex) EVCO SPA A80000-A80FFF (base 16) EVCO SPA VIA FELTRE N. 81 @@ -9089,12 +9293,6 @@ CF3000-CF3FFF (base 16) Mesh Motion Inc Kocaeli Kocaeli 41470 TR -70-B3-D5 (hex) Nke -E75000-E75FFF (base 16) Nke - Rue Gutenberg - Hennebont Brittany 56700 - FR - 70-B3-D5 (hex) UNISOR MULTISYSTEMS LTD 05F000-05FFFF (base 16) UNISOR MULTISYSTEMS LTD HAYETZIRA 6 ST @@ -11063,12 +11261,6 @@ AD2000-AD2FFF (base 16) YUYAMA MFG Co.,Ltd Tehran 1533655514 IR -8C-1F-64 (hex) Flextronics International Kft. -A4C000-A4CFFF (base 16) Flextronics International Kft. - Zrínyi Miklós str. 38. - Zalaegerszeg 8900 - HU - 8C-1F-64 (hex) MB connect line GmbH Fernwartungssysteme 9F2000-9F2FFF (base 16) MB connect line GmbH Fernwartungssysteme Winnettener Straße 6 @@ -11267,6 +11459,204 @@ E02000-E02FFF (base 16) ITS Teknik A/S Vejle 7100 DK +8C-1F-64 (hex) NETGEN HITECH SOLUTIONS LLP +ED9000-ED9FFF (base 16) NETGEN HITECH SOLUTIONS LLP + B 301 KNOX PLAZA MALAD WEST + MUMBAI MAHARASHTRA 400064 + IN + +8C-1F-64 (hex) Wolfspyre Labs +9D4000-9D4FFF (base 16) Wolfspyre Labs + 5007 Highland Ct #WPL-IEEE + Austin TX 78731 + US + +8C-1F-64 (hex) Vekto +4AC000-4ACFFF (base 16) Vekto + Televisieweg 75 + Almere 1322AK + NL + +8C-1F-64 (hex) SpectraDynamics, Inc. +581000-581FFF (base 16) SpectraDynamics, Inc. + 1849 Cherry St. + Louisville CO 80027 + US + +8C-1F-64 (hex) IO Master Technology +BD3000-BD3FFF (base 16) IO Master Technology + 4F?1 No. 258, Lian Cheng Rd, Zhong He Dist + New Taipei City 235 Taipei 235 + TW + +8C-1F-64 (hex) ESCAD AUTOMATION GmbH +05F000-05FFFF (base 16) ESCAD AUTOMATION GmbH + Escadstr. 1 + Pfullendorf 88630 + DE + +8C-1F-64 (hex) TIFLEX +194000-194FFF (base 16) TIFLEX + 10 Avenue de la 1ère Armée Française Rhin - Danube + PONCIN 01450 + FR + +8C-1F-64 (hex) Neuralog LP +115000-115FFF (base 16) Neuralog LP + 4800 Sugar Grove Blvd., Ste. 200 + Stafford TX 77479 + US + +8C-1F-64 (hex) Sichuan Aiyijan Technology Company Ltd. +40C000-40CFFF (base 16) Sichuan Aiyijan Technology Company Ltd. + C1102 No. 65 Wuke West 1st Rd Wuhou District + Chengdu Sichuan 61000 + CN + +8C-1F-64 (hex) Rapidev Pvt Ltd +A44000-A44FFF (base 16) Rapidev Pvt Ltd + Office # G201-204 NSTP, NUST ISLAMABAD + ISLAMABAD Islamabad Capital Territory 44000 + PK + +8C-1F-64 (hex) Opgal Optronic Industries ltd +35C000-35CFFF (base 16) Opgal Optronic Industries ltd + Hanapach 11 + Karmiel 2165317 + IL + +8C-1F-64 (hex) Alifax S.r.l. +C24000-C24FFF (base 16) Alifax S.r.l. + VIA PETRARCA 2/1 + POLVERARA PD 35020 + IT + +8C-1F-64 (hex) Flextronics International Kft +A4C000-A4CFFF (base 16) Flextronics International Kft + Zrínyi Miklós str. 38. + Zalaegerszeg 8900 + HU + +8C-1F-64 (hex) Pietro Fiorentini Spa +8D9000-8D9FFF (base 16) Pietro Fiorentini Spa + Via Armenia, 16 + San Vito al Tagliamento (PN) 33078 + IT + +8C-1F-64 (hex) Flextronics International Kft +D02000-D02FFF (base 16) Flextronics International Kft + 38. Zrinyi Str. + Zalaegerszeg Zala 8900 + HU + +8C-1F-64 (hex) VMukti Solutions Private Limited +E30000-E30FFF (base 16) VMukti Solutions Private Limited + 3-4, Shivalik Plaza, Panjrapole, Ambawadi + Ahmedabad Gujarat 380015 + IN + +8C-1F-64 (hex) Autark GmbH +943000-943FFF (base 16) Autark GmbH + Platz des Friedens 8 + Baunatal Hessen D-34225 + DE + +8C-1F-64 (hex) ACTELSER S.L. +3F4000-3F4FFF (base 16) ACTELSER S.L. + CARRER ALBERT EINSTEIN, 44 + TERRASSA BARCELONA 08223 + ES + +8C-1F-64 (hex) Timegate Instruments Ltd. +7A7000-7A7FFF (base 16) Timegate Instruments Ltd. + Tutkijantie 7 + Oulu 90540 + FI + +8C-1F-64 (hex) GSP Sprachtechnologie GmbH +FED000-FEDFFF (base 16) GSP Sprachtechnologie GmbH + Teltowkanalstraße 1 + Berlin 12247 + DE + +8C-1F-64 (hex) Wartsila Voyage Limited +38E000-38EFFF (base 16) Wartsila Voyage Limited + 13-18 City Quay + Dublin 2 D02 ED70 + IE + +8C-1F-64 (hex) AZD Praha s.r.o., ZOZ Olomouc +FA2000-FA2FFF (base 16) AZD Praha s.r.o., ZOZ Olomouc + Zeleznicni + Olomouc czech republic 77900 + CZ + +8C-1F-64 (hex) Cubic ITS, Inc. dba GRIDSMART Technologies +52D000-52DFFF (base 16) Cubic ITS, Inc. dba GRIDSMART Technologies + 10545 Hardin Valley Rd + Knoxville TN 37932 + US + +70-B3-D5 (hex) Watteco +E75000-E75FFF (base 16) Watteco + Rue Gutenberg + Hennebont Brittany 56700 + FR + +8C-1F-64 (hex) Toolplanet Co., Ltd. +54F000-54FFFF (base 16) Toolplanet Co., Ltd. + 43-2 Himigaike-cho + Gifu-shi Gifu 500-8122 + JP + +8C-1F-64 (hex) AIDirections +702000-702FFF (base 16) AIDirections + Torch Tower + Dubai Dubai 74249 + AE + +8C-1F-64 (hex) Beijing Zhongchen Microelectronics Co.,Ltd +AB4000-AB4FFF (base 16) Beijing Zhongchen Microelectronics Co.,Ltd + Room 0309, 3rd Floor, Building 2, China Agricultural University International Pioneer Park, No. 10 Tianxiu Road, Haidian District + Beijing Beijing 100081 + CN + +8C-1F-64 (hex) Weidmann Tecnologia Electrica de Mexico +7B7000-7B7FFF (base 16) Weidmann Tecnologia Electrica de Mexico + Oscar Flores Tapia No. 304, Col. El Llano + Arteaga Coahuila 25350 + MX + +8C-1F-64 (hex) Ashinne Technology Co., Ltd +E7C000-E7CFFF (base 16) Ashinne Technology Co., Ltd + 10F-1, No.18, Lane 609, Sec.5, Chung Hsin Rd., San Chung Dist. + New Taipei City 241 + TW + +8C-1F-64 (hex) Forever Engineering Systems Pvt. Ltd. +AC5000-AC5FFF (base 16) Forever Engineering Systems Pvt. Ltd. + B-817, 8th floor, Advant Navis Business Park, Sector-142 + NOIDA Uttar Pradesh 201301 + IN + +8C-1F-64 (hex) AMESS +EEA000-EEAFFF (base 16) AMESS + C-1501, 60, Haan-ro + Gwangmyeong-si Gyeonggi-do 14322 + KR + +8C-1F-64 (hex) EVERNET CO,.LTD TAIWAN +B7C000-B7CFFF (base 16) EVERNET CO,.LTD TAIWAN + 12 F., No. 206-2, Sec. 2, Daxing W. Rd + Taoyuan Taiwan 330 + TW + +8C-1F-64 (hex) Farmote Limited +017000-017FFF (base 16) Farmote Limited + 92 Collingwood Street, Nelson + Nelson Nelson 7010 + NZ + 70-B3-D5 (hex) System West dba ICS Electronics E06000-E06FFF (base 16) System West dba ICS Electronics 7034 Commerce Circle Suite A @@ -16826,6 +17216,150 @@ E61000-E61FFF (base 16) Stange Elektronik GmbH yokohama kanagawa 2220033 JP +8C-1F-64 (hex) Beijing Tongtech Technology Co., Ltd. +12B000-12BFFF (base 16) Beijing Tongtech Technology Co., Ltd. + Room 3017, Building 1, Hongfu Science Park, Changping District + Beijing Beijing 100029 + CN + +8C-1F-64 (hex) EOLANE +911000-911FFF (base 16) EOLANE + ZI DU VAL D OMBREE + COMBREE - 49520 + FR + +8C-1F-64 (hex) Abbott Diagnostics Technologies AS +429000-429FFF (base 16) Abbott Diagnostics Technologies AS + P. O. Box 6863 Rodeløkka + Oslo Oslo 0504 + NO + +8C-1F-64 (hex) Huz Electronics Ltd +BC2000-BC2FFF (base 16) Huz Electronics Ltd + 10 Avondale road + Cwmbran S Wales NP44 1UD + GB + +8C-1F-64 (hex) Microlynx Systems Ltd +F3C000-F3CFFF (base 16) Microlynx Systems Ltd + #107, 1925 - 18 Ave NE + Calgary AB T2E 7T8 + CA + +8C-1F-64 (hex) ANDDORO LLC +6F9000-6F9FFF (base 16) ANDDORO LLC + 1430 Broadway NY + New York NY 10018 + US + +8C-1F-64 (hex) GIORDANO CONTROLS SPA +807000-807FFF (base 16) GIORDANO CONTROLS SPA + VIA PARALLELA 2/4 + VILLA BARTOLOMEA IT 37049 + IT + +8C-1F-64 (hex) VEILUX INC. +045000-045FFF (base 16) VEILUX INC. + 802 GREENVIEW DR. STE 200 + GRAND PRAIRIE 75050 + US + +8C-1F-64 (hex) Cleanwatts Digital, S.A. +0E6000-0E6FFF (base 16) Cleanwatts Digital, S.A. + Ladeira da Paula, 6 + Antanhol-Coimbra 3040-574 + PT + +8C-1F-64 (hex) FORSEE POWER +0AF000-0AFFFF (base 16) FORSEE POWER + 2 chemin du ruisseau + ECULLY 69130 + FR + +8C-1F-64 (hex) Sakura Seiki Co.,Ltd. +28C000-28CFFF (base 16) Sakura Seiki Co.,Ltd. + 75-5, Imojiya + Chikuma-city Nagano Prefecture 387-0015 + JP + +8C-1F-64 (hex) Indefac company +E64000-E64FFF (base 16) Indefac company + Ka-211, Whangmool ro 190, DongDaemoon Gu + Seoul 02622 + KR + +8C-1F-64 (hex) Jide Car Rastreamento e Monitoramento LTDA +22E000-22EFFF (base 16) Jide Car Rastreamento e Monitoramento LTDA + Rua Arcipreste Andrade 630 + São Paulo São Paulo 04268020 + BR + +8C-1F-64 (hex) Real Digital +3B2000-3B2FFF (base 16) Real Digital + 655 SW James Pl + Pullman WA 99163 + US + +8C-1F-64 (hex) Fingoti Limited +CD9000-CD9FFF (base 16) Fingoti Limited + Barnam Ham Farm + Bickleigh, Plymouth Devon PL6 7AL + GB + +8C-1F-64 (hex) Grace Design/Lunatec LLC +FB7000-FB7FFF (base 16) Grace Design/Lunatec LLC + 4689 Ute Highway + Longmont CO 80503 + US + +8C-1F-64 (hex) Farmobile LLC +672000-672FFF (base 16) Farmobile LLC + 4001 W. 114th, Suite 300 + Leawood KS 66251 + US + +8C-1F-64 (hex) Zhuhai Yunzhou Intelligence Technology Ltd. +254000-254FFF (base 16) Zhuhai Yunzhou Intelligence Technology Ltd. + Room 311,312A,Floor 3,Heung Shan TechPort,No.3888 Qinglv North Road,Tangjiawan Town + Zhuhai Guangdong 519000 + CN + +8C-1F-64 (hex) AIQuatro +AC0000-AC0FFF (base 16) AIQuatro + 143B + São Paulo São Paulo 02433-070 + BR + +8C-1F-64 (hex) Active Research Limited +0C0000-0C0FFF (base 16) Active Research Limited + 21 Harwell Road + Poole Dorset BH17 0GE + GB + +8C-1F-64 (hex) Beijing Redlink Information Technology Co., Ltd. +D9A000-D9AFFF (base 16) Beijing Redlink Information Technology Co., Ltd. + Room 5, 2nd floor, Deshi Building, Haidian District, Beijing, China + Beijing 100085 + CN + +8C-1F-64 (hex) Sphere Com Services Pvt Ltd +A6A000-A6AFFF (base 16) Sphere Com Services Pvt Ltd + Sphere Com Services Pvt Ltd, F-16-22, pankaj plaza, plot-no-7, Sector - 12, Dwarka, New Delhi - 110075, New Delhi + New Delhi Delhi 110075 + IN + +8C-1F-64 (hex) suzhou yuecrown Electronic Technology Co.,LTD +CCB000-CCBFFF (base 16) suzhou yuecrown Electronic Technology Co.,LTD + B6,no.1599,West Chengbei Road,Gusu District + suzhou jiangsu 215000 + CN + +8C-1F-64 (hex) Paragraf +01A000-01AFFF (base 16) Paragraf + 7-8 West Newlands + Somersham Cambridgeshire PE28 3EB + GB + 70-B3-D5 (hex) YUYAMA MFG Co.,Ltd BBB000-BBBFFF (base 16) YUYAMA MFG Co.,Ltd 3-3-1 @@ -19259,12 +19793,6 @@ E9B000-E9BFFF (base 16) NUMATA R&D Co.,Ltd Osaki city 989-6161 JP -70-B3-D5 (hex) Active Research Limited -6A0000-6A0FFF (base 16) Active Research Limited - Unit 5, Wessex Trade Centre, Ringwood Road - Poole Dorset BH12 3PF - GB - 70-B3-D5 (hex) Scame Sistemi srl 2F3000-2F3FFF (base 16) Scame Sistemi srl Via Lombardia 5 @@ -21287,12 +21815,6 @@ E73000-E73FFF (base 16) Zeus Control Systems Ltd Nuneaton CV13 0PE GB -70-B3-D5 (hex) Flextronics International Kft. -E2F000-E2FFFF (base 16) Flextronics International Kft. - Zrínyi Miklós str. 38. - Zalaegerszeg 8900 - HU - 70-B3-D5 (hex) Beijing Lihong Create Co., Ltd. ED3000-ED3FFF (base 16) Beijing Lihong Create Co., Ltd. Changping, Zhenxinglu. 46 @@ -22328,12 +22850,6 @@ CCB000-CCBFFF (base 16) RealD, Inc. Wakefield West Yorkshire WF1 2ED GB -8C-1F-64 (hex) Flextronics International Kft. -F5C000-F5CFFF (base 16) Flextronics International Kft. - Zrínyi Miklós str. 38. - Zalaegerszeg 8900 - HU - 8C-1F-64 (hex) Bulwark 6A8000-6A8FFF (base 16) Bulwark 2/3 Sahra Grove @@ -22484,12 +23000,174 @@ EFB000-EFBFFF (base 16) WARECUBE,INC Suwon-si 16648 KR +8C-1F-64 (hex) Missing Link Electronics, Inc. +47A000-47AFFF (base 16) Missing Link Electronics, Inc. + 2880 Zanker Road, Ste 203 + San Jose 95134 + US + 8C-1F-64 (hex) Gemini Electronics B.V. 81A000-81AFFF (base 16) Gemini Electronics B.V. Burg. van Meeuwenstraat 14 Beek Limburg 6191 ND NL +8C-1F-64 (hex) AixControl GmbH +08F000-08FFFF (base 16) AixControl GmbH + Sonnenweg 15 + Aachen NRW 52070 + DE + +8C-1F-64 (hex) Zilica Limited +A1B000-A1BFFF (base 16) Zilica Limited + 8 Hasting Close, Bray, Bray + Maidenhead Bray Berks SL6 2DA + GB + +8C-1F-64 (hex) REO AG +73C000-73CFFF (base 16) REO AG + Brühlerstr. 100 + Solingen 42657 + DE + +8C-1F-64 (hex) Borrell USA Corp +38B000-38BFFF (base 16) Borrell USA Corp + 240 RIGGS AV + MERCED 95341 + US + +8C-1F-64 (hex) NOVA Products GmbH +BD6000-BD6FFF (base 16) NOVA Products GmbH + Thierschstr. 11 + Munich 80538 + DE + +8C-1F-64 (hex) ECO-ADAPT +C38000-C38FFF (base 16) ECO-ADAPT + 39 Rue de Chateaudun + Paris Ile-de-France 75009 + FR + +8C-1F-64 (hex) Flextronics International Kft +F5C000-F5CFFF (base 16) Flextronics International Kft + Zrínyi Miklós str. 38. + Zalaegerszeg 8900 + HU + +70-B3-D5 (hex) Flextronics International Kft +E2F000-E2FFFF (base 16) Flextronics International Kft + Zrínyi Miklós str. 38. + Zalaegerszeg 8900 + HU + +8C-1F-64 (hex) ADETEC SAS +AE8000-AE8FFF (base 16) ADETEC SAS + 8 rue de l'Angoumois + ARGENTEUIL 95100 + FR + +8C-1F-64 (hex) AML +634000-634FFF (base 16) AML + 2190 Regal Parkway + Euless TX 76040 + US + +8C-1F-64 (hex) In-lite Design BV +557000-557FFF (base 16) In-lite Design BV + Stephensonweg 18 + Gorinchem Zuid-Holland 4207 HB + NL + +8C-1F-64 (hex) SMS group GmbH +FF4000-FF4FFF (base 16) SMS group GmbH + Hirtenwiese 4 + Elkenroth Rhineland-Palantine 57578 + DE + +8C-1F-64 (hex) CyberneX Co., Ltd +A6D000-A6DFFF (base 16) CyberneX Co., Ltd + Kamata, 5-26-8, Ardel Kamata #1107 + O-taku Tokyo-to 1440052 + JP + +8C-1F-64 (hex) Enestone Corporation +2FD000-2FDFFF (base 16) Enestone Corporation + 3-24-5 Shin yokohama Kohoku + YOKOHAMA Kanagawa 222-0033 + JP + +70-B3-D5 (hex) Active Research Limited +6A0000-6A0FFF (base 16) Active Research Limited + 21 Harwell Road + Poole Dorset BH17 0GE + GB + +8C-1F-64 (hex) SiEngine Technology Co., Ltd. +F7A000-F7AFFF (base 16) SiEngine Technology Co., Ltd. + 6th floor,Building 23,No.1999,Yi Shan Road + Shanghai Shanghai 201114 + CN + +8C-1F-64 (hex) Vesperix Corporation +8E9000-8E9FFF (base 16) Vesperix Corporation + 803 West Broad St Suite 520 + Falls Church VA 22046 + US + +8C-1F-64 (hex) SOCNOC AI Inc +7DE000-7DEFFF (base 16) SOCNOC AI Inc + 2800 Innovation Avenue, Innovation Industrial Park? + Hefei Anhui 230000 + CN + +8C-1F-64 (hex) Sontay Ltd. +697000-697FFF (base 16) Sontay Ltd. + Four Elms Road + Edenbridge TN86AB + GB + +8C-1F-64 (hex) Lumiplan Duhamel +C4C000-C4CFFF (base 16) Lumiplan Duhamel + 2 rue de l'industrie + Domène Isère 38420 + FR + +8C-1F-64 (hex) BRICKMAKERS GmbH +E5E000-E5EFFF (base 16) BRICKMAKERS GmbH + Am Plan 14-16 + Koblenz 56068 + DE + +8C-1F-64 (hex) AvMap srlu +84C000-84CFFF (base 16) AvMap srlu + Viale Zaccagna 6 + Carrara 54033 + IT + +8C-1F-64 (hex) Neurable +B92000-B92FFF (base 16) Neurable + 45 Bromfield St + Chicago IL 60641 + US + +8C-1F-64 (hex) ADiCo Corporation +D69000-D69FFF (base 16) ADiCo Corporation + 2045-32, Takaragi-honcho + Utsunomiya-shi Tochigi 320-0075 + JP + +8C-1F-64 (hex) Wittra Networks AB +DF8000-DF8FFF (base 16) Wittra Networks AB + Västra Järnvägsgatan 39th floor(Convendum) + Stockholm Stockholm 111 64 + SE + +8C-1F-64 (hex) METRONA-Union GmbH +9FA000-9FAFFF (base 16) METRONA-Union GmbH + Aidenbachstr. 40 + München 81379 + DE + 70-B3-D5 (hex) DISMUNTEL SAL 92C000-92CFFF (base 16) DISMUNTEL SAL Pol ind cotes @@ -22952,12 +23630,6 @@ E1F000-E1FFFF (base 16) THETA432 El Segundo CA 90245 US -70-B3-D5 (hex) Root Automation -6A2000-6A2FFF (base 16) Root Automation - 1916 Fort Jones Rd - Yreka CA 96097 - US - 70-B3-D5 (hex) DIEHL Connectivity Solutions 1F1000-1F1FFF (base 16) DIEHL Connectivity Solutions Stephanstraße 49 @@ -27934,3 +28606,225 @@ A9A000-A9AFFF (base 16) Signasystems Elektronik San. ve Tic. Ltd. Sti. 2683 151st Place NE Redmond WA 98052 US + +8C-1F-64 (hex) Pigs Can Fly Labs LLC +DC0000-DC0FFF (base 16) Pigs Can Fly Labs LLC + 9450 SW Gemini Dr, PMB 41687 + Beaverton OR 97008 + US + +8C-1F-64 (hex) EMBSYS SISTEMAS EMBARCADOS +FD4000-FD4FFF (base 16) EMBSYS SISTEMAS EMBARCADOS + AV. SIGISMUNDO NUNES DE OLIVEIRA,570 CASA 324 + MARILIA SAO PAULO 17512752 + BR + +8C-1F-64 (hex) Cardinal Scales Manufacturing Co +DD5000-DD5FFF (base 16) Cardinal Scales Manufacturing Co + 203 East Daugherty Street + Webb City MO 64870 + US + +8C-1F-64 (hex) United States Technologies Inc. +525000-525FFF (base 16) United States Technologies Inc. + 1701 Pollitt Drive + Fair Lawn NJ 07410 + US + +8C-1F-64 (hex) Rumble, Inc +837000-837FFF (base 16) Rumble, Inc + Bluebonnet Ln + Austin TX 78704 + US + +8C-1F-64 (hex) EA Elektroautomatik GmbH & Co. KG +504000-504FFF (base 16) EA Elektroautomatik GmbH & Co. KG + Helmholtzstraße 31-33 + Viersen NRW 41747 + DE + +8C-1F-64 (hex) PuS GmbH und Co. KG +4E0000-4E0FFF (base 16) PuS GmbH und Co. KG + Hainstr. 13 + Gera Germany 07545 + DE + +8C-1F-64 (hex) Cinetix Srl +89E000-89EFFF (base 16) Cinetix Srl + Via Armentera, 8 + Borgo Valsugana Trento 38051 + IT + +8C-1F-64 (hex) ATM SOLUTIONS +9BD000-9BDFFF (base 16) ATM SOLUTIONS + Office 10, Krishna Arcade, Plot 65, Sector 2A, Koparkharine + Navi Mumbai Maharashatra 400709 + IN + +8C-1F-64 (hex) Mitsubishi Electric India Pvt. Ltd. +D92000-D92FFF (base 16) Mitsubishi Electric India Pvt. Ltd. + EL3, J BLOCK, M.I.D.C. Bhosari + PUNE Maharastra 411027 + IN + +8C-1F-64 (hex) Integer.pl S.A. +A97000-A97FFF (base 16) Integer.pl S.A. + Wielicka 28 + Krakow 30-552 + PL + +8C-1F-64 (hex) Monnit Corporation +94E000-94EFFF (base 16) Monnit Corporation + 450 South Simmons STE 670 + Kaysville UT 84037 + US + +8C-1F-64 (hex) SASYS e.K. +1CB000-1CBFFF (base 16) SASYS e.K. + Spannstiftstr. 16 + Hagen 58119 + DE + +8C-1F-64 (hex) ADAMCZEWSKI elektronische Messtechnik GmbH +F4E000-F4EFFF (base 16) ADAMCZEWSKI elektronische Messtechnik GmbH + Felix-Wankel-Str. 13 + Zaberfeld Baden-Württemberg 74374 + DE + +8C-1F-64 (hex) XSENSOR Technology Corp. +7AA000-7AAFFF (base 16) XSENSOR Technology Corp. + 133 12 Ave SE + Calgary Alberta T2G 0Z9 + CA + +8C-1F-64 (hex) MG s.r.l. +67A000-67AFFF (base 16) MG s.r.l. + via Monte Bianco, 1 + Solbiate Olona VA 21058 + IT + +8C-1F-64 (hex) Beijing Wenrise Technology Co., Ltd. +A84000-A84FFF (base 16) Beijing Wenrise Technology Co., Ltd. + No.10 Shangdi Road, Haidian District + Beijing Beijing 100085 + CN + +8C-1F-64 (hex) AMF Medical SA +F52000-F52FFF (base 16) AMF Medical SA + Chemin de la Dent-d'Oche 1 A + Ecublens VD Vaud 1024 + CH + +8C-1F-64 (hex) SCIREQ Scientific Respiratory Equipment Inc +01E000-01EFFF (base 16) SCIREQ Scientific Respiratory Equipment Inc + 6600 rue St. Urbain, Suite 300 + Montreal Quebec H2S 3G8 + CA + +8C-1F-64 (hex) HUPI +489000-489FFF (base 16) HUPI + 45 allée théodore monod + Bidart Sélectionnez un département / état 64210 + FR + +8C-1F-64 (hex) Qualitrol LLC +905000-905FFF (base 16) Qualitrol LLC + 1385 Fairport Rd + Fairport NY 14450 + US + +8C-1F-64 (hex) WINTUS SYSTEM +9BA000-9BAFFF (base 16) WINTUS SYSTEM + E1102, 7 yeonmujang 5ga gil, seongdong-gu + SEOUL SEOUL 04782 + KR + +8C-1F-64 (hex) TTC TELEKOMUNIKACE, s.r.o. +E4C000-E4CFFF (base 16) TTC TELEKOMUNIKACE, s.r.o. + Trebohosticka 5 + Praha 10 Praha 10000 + CZ + +70-B3-D5 (hex) Root Automation +6A2000-6A2FFF (base 16) Root Automation + 112 4 H Way + Yreka CA 96097 + US + +8C-1F-64 (hex) Proterra, Inc +552000-552FFF (base 16) Proterra, Inc + 1 Whitlee Court + Greenville SC 29607 + US + +8C-1F-64 (hex) eumig industrie-TV GmbH. +5B3000-5B3FFF (base 16) eumig industrie-TV GmbH. + Gewerbeparkstrasse 9 + Anif Salzburg 5081 + AT + +8C-1F-64 (hex) TeraDiode / Panasonic +2C3000-2C3FFF (base 16) TeraDiode / Panasonic + 30 Upton Dr + Wilmington MA 01887 + US + +8C-1F-64 (hex) QUERCUS TECHNOLOGIES, S.L. +D7C000-D7CFFF (base 16) QUERCUS TECHNOLOGIES, S.L. + Av. Onze de Setembre 19 + Reus Tarragona 43203 + ES + +8C-1F-64 (hex) SPIT Technology, Inc +939000-939FFF (base 16) SPIT Technology, Inc + 2F, 91-1, Gyeongui-ro + Uijeongbu-si Gyonggi-do 11652 + KR + +8C-1F-64 (hex) VECOS Europe B.V. +C80000-C80FFF (base 16) VECOS Europe B.V. + ESP 237 + Eindhoven Noord-Brabant 5633 AD + NL + +8C-1F-64 (hex) EMIT GmbH +3D1000-3D1FFF (base 16) EMIT GmbH + Johannes-Mauthe-Straße 14 + Albstadt Baden Württemberg 72458 + DE + +8C-1F-64 (hex) M/S MILIND RAMACHANDRA RAJWADE +721000-721FFF (base 16) M/S MILIND RAMACHANDRA RAJWADE + 713, Sinhgad Road, P.cast S. No. 39, Manikbaug Industries Wadagaon Budru + Pune Maharashtra 411051 + IN + +8C-1F-64 (hex) JW Froehlich Maschinenfabrik GmbH +C8F000-C8FFFF (base 16) JW Froehlich Maschinenfabrik GmbH + Kohlhammerstrasse 18-24 + Leinfelden-Echterdingen 70771 + DE + +8C-1F-64 (hex) Power Electronics Espana, S.L. +D08000-D08FFF (base 16) Power Electronics Espana, S.L. + Poligono Industrial Carrases. Ronda del camp d Aviacio 4 + Lliria Valencia 46160 + ES + +8C-1F-64 (hex) Mediana +C6B000-C6BFFF (base 16) Mediana + Wonju Medical Industry Park, 1650-1 Donghwa-Ri, + Wonju-Si Gangwon-Do 220-801 + KR + +8C-1F-64 (hex) noah +B01000-B01FFF (base 16) noah + Augustusplatz 1-4 + Leipzig 04109 + DE + +8C-1F-64 (hex) Nokeval Oy +E0E000-E0EFFF (base 16) Nokeval Oy + Rounionkatu 107 + Nokia 37150 + FI diff --git a/hwdb.d/meson.build b/hwdb.d/meson.build index 4363d67cb..b4c519c8f 100644 --- a/hwdb.d/meson.build +++ b/hwdb.d/meson.build @@ -27,9 +27,11 @@ hwdb_files_test = files( '60-seat.hwdb', '60-sensor.hwdb', '70-analyzers.hwdb', + '70-av-production.hwdb', '70-cameras.hwdb', '70-joystick.hwdb', '70-mouse.hwdb', + '70-pda.hwdb', '70-pointingstick.hwdb', '70-touchpad.hwdb', '80-ieee1394-unit-function.hwdb') diff --git a/hwdb.d/parse_hwdb.py b/hwdb.d/parse_hwdb.py index 0268bf958..93179b675 100755 --- a/hwdb.d/parse_hwdb.py +++ b/hwdb.d/parse_hwdb.py @@ -121,7 +121,7 @@ def hwdb_grammar(): def property_grammar(): ParserElement.setDefaultWhitespaceChars(' ') - dpi_setting = Group(Optional('*')('DEFAULT') + INTEGER('DPI') + Suppress('@') + INTEGER('HZ'))('SETTINGS*') + dpi_setting = Group(Optional('*')('DEFAULT') + INTEGER('DPI') + Optional(Suppress('@') + INTEGER('HZ')))('SETTINGS*') mount_matrix_row = SIGNED_REAL + ',' + SIGNED_REAL + ',' + SIGNED_REAL mount_matrix = Group(mount_matrix_row + ';' + mount_matrix_row + ';' + mount_matrix_row)('MOUNT_MATRIX') xkb_setting = Optional(Word(alphanums + '+-/@._')) @@ -135,7 +135,9 @@ def property_grammar(): ('MOUSE_WHEEL_CLICK_COUNT', INTEGER), ('MOUSE_WHEEL_CLICK_COUNT_HORIZONTAL', INTEGER), ('ID_AUTOSUSPEND', Or((Literal('0'), Literal('1')))), + ('ID_AV_PRODUCTION_CONTROLLER', Or((Literal('0'), Literal('1')))), ('ID_PERSIST', Or((Literal('0'), Literal('1')))), + ('ID_PDA', Or((Literal('0'), Literal('1')))), ('ID_INPUT', Or((Literal('0'), Literal('1')))), ('ID_INPUT_ACCELEROMETER', Or((Literal('0'), Literal('1')))), ('ID_INPUT_JOYSTICK', Or((Literal('0'), Literal('1')))), diff --git a/hwdb.d/pci.ids b/hwdb.d/pci.ids index 4c99c149f..19832a58b 100644 --- a/hwdb.d/pci.ids +++ b/hwdb.d/pci.ids @@ -1,8 +1,8 @@ # # List of PCI ID's # -# Version: 2021.12.15 -# Date: 2021-12-15 03:15:02 +# Version: 2022.03.22 +# Date: 2022-03-22 03:15:02 # # Maintained by Albert Pool, Martin Mares, and other volunteers from # the PCI ID Project at https://pci-ids.ucw.cz/. @@ -105,14 +105,24 @@ 0731 7215 JM7200 9200 JM9200 920a JH920 + 0731 920a JH920 + 0731 920b JH920-I + 0731 920c JH920-M 920b JH920-I 920c JH920-M 9210 JM9210 + 0731 9210 JM9210 + 0731 9211 JM9210-I 9211 JM9210-I 9230 JM9230 + 0731 9230 JM9230 + 0731 9231 JM9230-I 9231 JM9231-I 9250 JM9250 + 0731 9250 JM9250 930a JH930-I + 0731 930a JH930-I + 0731 930b JH930-M 930b JH930-M 0777 Ubiquiti Networks, Inc. 0795 Wired Inc. @@ -821,14 +831,16 @@ 1028 200c HBA355i Front 1028 200d HBA355e Adapter 1028 200e HBA350i MX + 1028 2170 HBA350i MM 1028 2175 HBA350i Adapter + 1028 2197 HBA350i MM LP 1d49 0205 ThinkSystem 440-16i SAS/SATA PCIe Gen4 12Gb Internal HBA 1d49 0206 ThinkSystem 440-16e SAS/SATA PCIe Gen4 12Gb HBA 1d49 0207 ThinkSystem 440-8i SAS/SATA PCIe Gen4 12Gb HBA 1d49 0208 ThinkSystem 440-16i SAS/SATA PCIe Gen4 12Gb HBA 1d49 0209 ThinkSystem 440-8e SAS/SATA PCIe Gen4 12Gb HBA - 8086 4050 Storage Controller RS3P4QF160F - 8086 4070 Storage Controller RS3P4GF016F + 8086 4050 Storage Controller RS3P4QF160J + 8086 4070 Storage Controller RS3P4GF016J 00e7 Fusion-MPT 12GSAS/PCIe Unsupported SAS38xx # Tampered part 1028 200b HBA355i Adapter Tampered @@ -932,6 +944,8 @@ 10e2 MegaRAID 12GSAS/PCIe Secure SAS39xx # 9560 16 internal port RAID controller 1000 4000 MegaRAID 9560-16i +# 9561 16 internal port RAID controller + 1000 4002 MegaRAID 9561-16i # 9560 8 internal port RAID controller 1000 4010 MegaRAID 9560-8i # 9580 8 internal & 8 external port RAID controller @@ -965,6 +979,7 @@ 1028 2173 PERC H355 Front 1028 2174 PERC H350 Mini 1028 2177 PERC H350 Adapter + 1028 2199 PERC H350 Mini LP 1d49 0505 ThinkSystem RAID 540-8i PCIe Gen4 12Gb Adapter 1d49 0506 ThinkSystem RAID 540-16i PCIe Gen4 12Gb Adapter 10e7 MegaRAID 12GSAS/PCIe Unsupported SAS38xx @@ -986,6 +1001,7 @@ c010 PEX88048 50 lane, 50 port, PCI Express Gen 4.0 ExpressFabric Platform c012 PEX880xx PCIe Gen 4 Switch 1d49 0003 ThinkSystem 1611-8P PCIe Gen4 NVMe Switch Adapter + c030 PEX890xx PCIe Gen 5 Switch 1001 Kolter Electronic 0010 PCI 1616 Measurement card with 32 digital I/O lines 0011 OPTO-PCI Opto-Isolated digital I/O board @@ -1062,7 +1078,7 @@ 163f VanGogh 164c Lucienne 164d Rembrandt - 1681 Rembrandt + 1681 Rembrandt [Radeon 680M] 1714 BeaverCreek HDMI Audio [Radeon HD 6500D and 6400G-6600G series] 103c 168b ProBook 4535s 3150 RV380/M24 [Mobility Radeon X600] @@ -2032,9 +2048,10 @@ 174b a240 Radeon R7 240 OEM 174b d340 Radeon R7 340 OEM 1b0a 90d3 Radeon R7 240 OEM - 6613 Oland PRO [Radeon R7 240/340] + 6613 Oland PRO [Radeon R7 240/340 / Radeon 520] 148c 7340 Radeon R7 340 1682 7240 R7 240 2048 MB + 1dcf 3000 Oland PRO [Radeon R7 240/340 / Radeon 520] 6631 Oland 6640 Saturn XT [FirePro M6100] 106b 014b Tropo XT [Radeon R9 M380 Mac Edition] @@ -3742,23 +3759,29 @@ 73a2 Navi 21 Pro-XTA [Radeon Pro W6900X] 73a3 Navi 21 GL-XL [Radeon PRO W6800] 73a4 Navi 21 USB + 73a5 Navi 21 [Radeon RX 6950 XT] 73ab Navi 21 Pro-XLA [Radeon Pro W6800X/Radeon Pro W6800X Duo] 73af Navi 21 [Radeon RX 6900 XT] + 148c 2414 Navi 21 XTXH [PowerColor Red Devil RX 6900 XT Ultimate] 73bf Navi 21 [Radeon RX 6800/6800 XT / 6900 XT] 1002 0e3a Radeon RX 6900 XT + 148c 2408 Red Devil AMD Radeon RX 6900 XT 1eae 6701 XFX Speedster MERC 319 AMD Radeon RX 6800 XT Black 73c3 Navi 22 73c4 Navi 22 USB - 73df Navi 22 [Radeon RX 6700/6700 XT / 6800M] + 73df Navi 22 [Radeon RX 6700/6700 XT/6750 XT / 6800M] 73e0 Navi 23 73e1 Navi 23 WKS-XM [Radeon PRO W6600M] 73e3 Navi 23 WKS-XL [Radeon PRO W6600] 73e4 Navi 23 USB + 73ef Navi 23 [Radeon RX 6650 XT] 73ff Navi 23 [Radeon RX 6600/6600 XT/6600M] 148c 2412 PowerColor Red Devil RX 6600 XT 7408 Aldebaran 740c Aldebaran 740f Aldebaran + 743f Navi 24 [Radeon RX 6400 / 6500 XT] + 1da2 e457 PULSE AMD Radeon RX 6500 XT 7833 RS350 Host Bridge 7834 RS350 [Radeon 9100 PRO/XT IGP] 7835 RS350M [Mobility Radeon 9000 IGP] @@ -4161,7 +4184,7 @@ ab10 Lexa HDMI Audio ab18 Vega 12 HDMI Audio ab20 Vega 20 HDMI Audio [Radeon VII] - ab28 Navi 21 HDMI Audio [Radeon RX 6800/6800 XT / 6900 XT] + ab28 Navi 21/23 HDMI/DP Audio Controller ab38 Navi 10 HDMI Audio ac00 Theater 506 World-Wide Analog Decoder ac01 Theater 506 World-Wide Analog Decoder @@ -4956,14 +4979,14 @@ 1043 876b PRIME Motherboard 17aa 5124 ThinkPad E595 ea50 ce19 mCOM10-L1900 - 15e2 Raven/Raven2/FireFlight/Renoir Audio Processor + 15e2 ACP/ACP3X/ACP6x Audio Coprocessor 17aa 5124 ThinkPad E595 ea50 ce19 mCOM10-L1900 - 15e3 Family 17h (Models 10h-1fh) HD Audio Controller + 15e3 Family 17h/19h HD Audio Controller 103c 8615 Pavilion Laptop 15-cw1xxx 1043 86c7 PRIME B450M-A Motherboard 17aa 5124 ThinkPad E595 - 15e4 Raven/Raven2/Renoir Sensor Fusion Hub + 15e4 Sensor Fusion Hub 15e5 Raven2 USB 3.1 ea50 ce19 mCOM10-L1900 15e6 Raven/Raven2/Renoir Non-Sensor Fusion Hub KMDF driver @@ -5139,6 +5162,13 @@ 43c7 400 Series Chipset PCIe Port 43c8 400 Series Chipset SATA Controller 43d5 400 Series Chipset USB 3.1 XHCI Controller + 43e9 500 Series Chipset Switch Upstream Port + 43eb 500 Series Chipset SATA Controller +# or ASM106X Serial ATA Controller + 1b21 1062 ASM1062 Serial ATA Controller + 43ee 500 Series Chipset USB 3.1 XHCI Controller +# maybe + 1b21 1142 ASM1042A USB 3.0 Host Controller 57a3 Matisse PCIe GPP Bridge 57a4 Matisse PCIe GPP Bridge 57ad Matisse Switch Upstream @@ -11492,6 +11522,7 @@ 10ef GP102 HDMI Audio Controller 10f0 GP104 High Definition Audio Controller 10f1 GP106 High Definition Audio Controller + 1043 85b6 DUAL-GTX1060-O6G [GeForce GTX 1060 6GB Dual] 10f7 TU102 High Definition Audio Controller 10f8 TU104 HD Audio Controller 10f9 TU106 High Definition Audio Controller @@ -12189,6 +12220,7 @@ 1c01 GP106 1c02 GP106 [GeForce GTX 1060 3GB] 1c03 GP106 [GeForce GTX 1060 6GB] + 1043 85b6 DUAL-GTX1060-O6G [GeForce GTX 1060 6GB Dual] 1c04 GP106 [GeForce GTX 1060 5GB] 1c06 GP106 [GeForce GTX 1060 6GB Rev. 2] 1c07 GP106 [P106-100] @@ -12321,6 +12353,7 @@ 1ef5 TU104GLM [Quadro RTX 5000 Mobile Refresh] 1f02 TU106 [GeForce RTX 2070] 1043 8673 TURBO RTX 2070 + 1f03 TU106 [GeForce RTX 2060 12GB] 1f04 TU106 1f06 TU106 [GeForce RTX 2060 SUPER] 1f07 TU106 [GeForce RTX 2070 Rev. A] @@ -12354,21 +12387,32 @@ 1f99 TU117M 1f9c TU117M [GeForce MX450] 1f9d TU117M [GeForce GTX 1650 Mobile / Max-Q] +# via Lenovo 496.90 + 1f9f TU117M [GeForce MX550] + 1fa0 TU117M [GeForce MX550] 1fae TU117GL 1fb0 TU117GLM [Quadro T1000 Mobile] 1fb1 TU117GL [T600] 1fb2 TU117GLM [Quadro T400 Mobile] + 1fb6 TU117GLM [T600 Laptop GPU] + 1fb7 TU117GLM [T550 Laptop GPU] 1fb8 TU117GLM [Quadro T2000 Mobile / Max-Q] 1fb9 TU117GLM [Quadro T1000 Mobile] 1fba TU117GLM [T600 Mobile] 1fbb TU117GLM [Quadro T500 Mobile] + 1fbc TU117GLM [T1200 Laptop GPU] 1fbf TU117GL 1fd9 TU117BM [GeForce GTX 1650 Mobile Refresh] 1fdd TU117BM [GeForce GTX 1650 Mobile Refresh] + 1ff0 TU117GL [T1000 8GB] + 1ff2 TU117GL [T400 4GB] 1ff9 TU117GLM [Quadro T1000 Mobile] + 2082 GA100 [CMP 170HX] 20b0 GA100 [A100 SXM4 40GB] 20b1 GA100 [A100 PCIe 40GB] 20b2 GA100 [A100 SXM4 80GB] +# 20B3 14A7 10DE PG506-242 / 20B3 14A8 10DE PG506-243 + 20b3 GA100 [PG506-242/243] 20b5 GA100 [A100 PCIe 80GB] 20b6 GA100GL [PG506-232] 20b7 GA100GL [A30 PCIe] @@ -12376,7 +12420,9 @@ 20be GA100 [GRID A100A] 20bf GA100 [GRID A100B] 20c2 GA100 [CMP 170HX] + 20f0 GA100 [A100-PG506-207] 20f1 GA100 [A100 PCIe 40GB] + 20f2 GA100 [A100-PG506-217] 2182 TU116 [GeForce GTX 1660 Ti] 2183 TU116 2184 TU116 [GeForce GTX 1660] @@ -12399,6 +12445,7 @@ 10de 146d GA102 [GeForce RTX 3080 20GB] 1462 3892 RTX 3080 10GB GAMING X TRIO 2208 GA102 [GeForce RTX 3080 Ti] + 220a GA102 [GeForce RTX 3080 12GB] 220d GA102 [CMP 90HX] 2216 GA102 [GeForce RTX 3080 Lite Hash Rate] 222b GA102 [GeForce RTX 3090 Engineering Sample] @@ -12406,15 +12453,20 @@ 2230 GA102GL [RTX A6000] 2231 GA102GL [RTX A5000] 2232 GA102GL [RTX A4500] + 2233 GA102GL [RTX A5500] 2235 GA102GL [A40] 2236 GA102GL [A10] 2237 GA102GL [A10G] + 2238 GA102GL [A10M] 223f GA102GL 228b GA104 High Definition Audio Controller 2296 Tegra PCIe Endpoint Virtual Network 2302 GA103 2321 GA103 + 2414 GA103 [GeForce RTX 3060 Ti] 2420 GA103M [GeForce RTX 3080 Ti Mobile] + 2438 GA103GLM [RTX A5500 Laptop GPU] + 2460 GA103M [GeForce RTX 3080 Ti Laptop GPU] 2482 GA104 [GeForce RTX 3070 Ti] 2483 GA104 2484 GA104 [GeForce RTX 3070] @@ -12436,29 +12488,46 @@ 24b6 GA104GLM [RTX A5000 Mobile] 24b7 GA104GLM [RTX A4000 Mobile] 24b8 GA104GLM [RTX A3000 Mobile] + 24b9 GA104GLM [RTX A3000 12GB Laptop GPU] + 24ba GA104GLM [RTX A4500 Laptop GPU] + 24bb GA104GLM [RTX A3000 Laptop GPU] 24bf GA104 [GeForce RTX 3070 Engineering Sample] 24dc GA104M [GeForce RTX 3080 Mobile / Max-Q 8GB/16GB] 24dd GA104M [GeForce RTX 3070 Mobile / Max-Q] + 24e0 GA104M [Geforce RTX 3070 Ti Laptop GPU] + 24fa GA104 [RTX A4500 Embedded GPU ] 2501 GA106 [GeForce RTX 3060] 2503 GA106 [GeForce RTX 3060] 2504 GA106 [GeForce RTX 3060 Lite Hash Rate] 2505 GA106 + 2507 GA106 [Geforce RTX 3050] 2520 GA106M [GeForce RTX 3060 Mobile / Max-Q] 2523 GA106M [GeForce RTX 3050 Ti Mobile / Max-Q] 252f GA106 [GeForce RTX 3060 Engineering Sample] + 2531 GA106 [RTX A2000] 2560 GA106M [GeForce RTX 3060 Mobile / Max-Q] 2563 GA106M [GeForce RTX 3050 Ti Mobile / Max-Q] + 2571 GA106 [RTX A2000 12GB] 2583 GA107 [GeForce RTX 3050] 25a0 GA107M [GeForce RTX 3050 Ti Mobile] 25a2 GA107M [GeForce RTX 3050 Mobile] 25a4 GA107 25a5 GA107M [GeForce RTX 3050 Mobile] + 25a6 GA107M [GeForce MX570] + 25a7 GA107M [GeForce MX570] + 25a9 GA107M [GeForce RTX 2050] 25af GA107 [GeForce RTX 3050 Engineering Sample] 25b5 GA107GLM [RTX A4 Mobile] +# A16 - 25B6 10DE 14A9 / A2 - 25B6 10DE 157E + 25b6 GA107GL [A2 / A16] 25b8 GA107GLM [RTX A2000 Mobile] + 25b9 GA107GLM [RTX A1000 Laptop GPU] + 25ba GA107GLM [RTX A2000 8GB Laptop GPU] 25e0 GA107BM [GeForce RTX 3050 Ti Mobile] 25e2 GA107BM [GeForce RTX 3050 Mobile] 25e5 GA107BM [GeForce RTX 3050 Mobile] + 25f9 GA107 [RTX A1000 Embedded GPU ] + 25fa GA107 [RTX A2000 Embedded GPU] 10df Emulex Corporation 0720 OneConnect NIC (Skyhawk) 103c 1934 FlexFabric 20Gb 2-port 650M Adapter @@ -12689,6 +12758,7 @@ 1028 09be Latitude 7410 17aa 224f ThinkPad X1 Carbon 5th Gen 5260 RTS5260 PCI Express Card Reader + 5261 RTS5261 PCI Express Card Reader 5286 RTS5286 PCI Express Card Reader 5287 RTL8411B PCI Express Card Reader 1025 1094 Acer Aspire E5-575G @@ -12790,6 +12860,8 @@ 103c 2a6f Asus IPIBL-LB Motherboard 103c 825b OMEN-17-w001nv 103c 8615 Pavilion Laptop 15-cw1xxx +# Rev 29, uses r8169 Driver on Linux + 103c 8882 HP ProDesk 405 G8 Desktop Mini PC 1043 11f5 Notebook motherboard (one of many models) 1043 16d5 U6V/U31J laptop 1043 81aa P5B @@ -13699,6 +13771,8 @@ 9082 Standard AHCI 1.0 SATA Controller 9140 HDMI Audio Device 9201 USB3.0 Controller +# Centaur CNS Coprocessor + 9380 Ncore Coprocessor 9530 VX800/820/900 Series Secure Digital Memory Card Controller 95d0 VX800/820/900 Series SDIO Host Controller a208 PT890 PCI to PCI Bridge Controller @@ -16795,6 +16869,7 @@ # PI7C9X20508GP 5Port-8Lane PCI Express Switch GreenPacket Family 0508 PI7C9X20508GP PCI Express Switch 5Port-8Lane 2304 PI7C9X2G304 EL/SL PCIe2 3-Port/4-Lane Packet Switch + 2308 PI7C9X2G308GP 8-lane PCI Express 2.0 Switch with 3 PCI Express ports 2404 PI7C9X2G404 EL/SL PCIe2 4-Port/4-Lane Packet Switch 2608 PI7C9X2G608GP PCIe2 6-Port/8-Lane Packet Switch ea50 cc10 RXi2-BP @@ -17454,6 +17529,8 @@ 13a3 0036 DX1730 Acceleration Card 0037 8204 Acceleration Processor 13a3 0036 DX1740 Acceleration Card + 9240 XR9240 Compression and Security Coprocessor [Panther II] + 13a3 9200 DX2040 Compression and Security Acceleration Card [Panther II] 13a4 Rascom Inc 13a5 Audio Digital Imaging Inc 13a6 Videonics Inc @@ -19077,11 +19154,15 @@ 103c 1240 Myrinet M2L-PCI64/2-3.0 LANai 7.4 (HP OEM) 14c2 DTK Computer 14c3 MEDIATEK Corp. + 0608 RZ608 Wi-Fi 6E 80MHz + 0616 MT7922 802.11ax PCI Express Wireless Network Adapter 7612 MT7612E 802.11acbgn PCI Express Wireless Network Adapter + 7615 MT7615E 802.11ac PCI Express Wireless Network Adapter 7630 MT7630e 802.11bgn Wireless Network Adapter # MT7612E too? 7662 MT7662E 802.11ac PCI Express Wireless Network Adapter 7915 MT7915E 802.11ax PCI Express Wireless Network Adapter + 7961 MT7921 802.11ax PCI Express Wireless Network Adapter 14c4 IWASAKI Information Systems Co Ltd 14c5 Automation Products AB 14c6 Data Race Inc @@ -19424,6 +19505,10 @@ 1259 2708 AT-2712 FX # The Broadcom 57800 device has two 1Gig ports and two 10Gig ports. The subsystem information can be used to differentiate. 168a NetXtreme II BCM57800 1/10 Gigabit Ethernet +# SFP+ ports + 1014 0493 PCIe2 LP 4-Port (10Gb+1GbE) SR+RJ45 Adapter (FC EN0T; CCIN 2CC3) +# RJ-45 ports + 1014 0494 PCIe2 LP 4-Port (10Gb+1GbE) SR+RJ45 Adapter (FC EN0T; CCIN 2CC3) 1028 1f5c BCM57800 10-Gigabit Ethernet 1028 1f5d BCM57800 10-Gigabit Ethernet 1028 1f67 BCM57800 1-Gigabit Ethernet @@ -20635,6 +20720,7 @@ 07b0 VMXNET3 Ethernet Controller 07c0 PVSCSI SCSI Controller 07e0 SATA AHCI controller + 07f0 NVMe SSD Controller 0801 Virtual Machine Interface 15ad 0800 Hypervisor ROM Interface 0820 Paravirtual RDMA controller @@ -20668,6 +20754,8 @@ 021d MT43244 Family [BlueField-3 Secure Flash Recovery] 021e CX8 Family [ConnectX-8 Flash Recovery] 021f CX8 Family [ConnectX-8 Secure Flash Recovery] + 0220 BF4 Family Flash Recovery [BlueField-4 SoC Flash Recovery] + 0221 BF4 Family Secure Flash Recovery [BlueField-4 Secure Flash Recovery] 024e MT53100 [Spectrum-2, Flash recovery mode] 024f MT53100 [Spectrum-2, Secure Flash recovery mode] 0250 Spectrum-3, Flash recovery mode @@ -20679,6 +20767,7 @@ 0256 Abir GearBox 0257 Quantum-2 in Flash Recovery Mode 0258 Quantum-2 RMA + 0259 Abir Chiplet 0262 MT27710 [ConnectX-4 Lx Programmable] EN 0263 MT27710 [ConnectX-4 Lx Programmable Virtual Function] EN 0264 Innova-2 Flex Burn image @@ -20686,6 +20775,7 @@ 0271 Spectrum-4L, RMA 0274 Spectrum-4C, Flash recovery mode 0275 Spectrum-4C RMA + 0277 Spectrum-4TOR RMA 0281 NPS-600 Flash Recovery 1002 MT25400 Family [ConnectX-2 Virtual Function] 1003 MT27500 Family [ConnectX-3] @@ -20857,10 +20947,14 @@ a2da MT43244 BlueField-3 SoC Crypto enabled a2db MT43244 BlueField-3 SoC Crypto disabled a2dc MT43244 BlueField-3 integrated ConnectX-7 network controller + a2dd BF4 Family Crypto enabled [BlueField-4 SoC Crypto enabled] + a2de BF4 Family Crypto disabled [BlueField-4 SoC Crypto disabled] + a2df BF4 Family integrated network controller [BlueField-4 integrated network controller] c2d2 MT416842 BlueField SoC management interfac c2d3 MT42822 BlueField-2 SoC Management Interface c2d4 MT43162 BlueField-3 Lx SoC Management Interface c2d5 MT43244 BlueField-3 SoC Management Interface + c2d6 BF4 Family Management Interface [BlueField-4 SoC Management Interface] # SwitchX-2, 40GbE switch c738 MT51136 c739 MT51136 GW @@ -20956,8 +21050,9 @@ 15cc Hotrail Inc 15cd Dreamtech Co Ltd 15ce Genrad Inc -15cf Hilscher GmbH - 0000 CIFX 50E-DP(M/S) +# https://www.hilscher.com/imprint/ +15cf Hilscher Gesellschaft für Systemautomation mbH + 0000 CIFX PCI/PCIe 15d1 Infineon Technologies AG 15d2 FIC (First International Computer Inc) 15d3 NDS Technologies Israel Ltd @@ -21819,6 +21914,32 @@ a036 ThunderX RAD (RAID acceleration engine) virtual function a037 THUNDERX ZIP virtual function a040 THUNDERX CPT Cryptographic Accelerator +# MAC found on OcteonTx2 series of silicons + a059 Octeon TX2 CGX (MAC) +# MAC found on Octeon 10 series of silicons + a060 Octeon 10 RPM (MAC) +# Octeon Tx2 Loopback Interface block + a061 Octeon Tx2 Loopback Interface (LBK) +# Octeon Tx2 Resource Virtualization Unit Physical Function + a063 Octeon Tx2 RVU Physical Function +# Octeon Tx2 Resource Virtualization Unit Virtual Function + a064 Octeon Tx2 RVU Virtual Function +# Octeon Tx2 Resource Virtualization Unit Admin Function + a065 Octeon Tx2 RVU Admin Function +# PTP Timestamping unit on Octeon 10 silicon series + a09e Octeon 10 PTP controller +# Cryptographic Accelerator found on Octeon 10 series of silicons + a0f2 Octeon 10 CPT Cryptographic Accelerator, Physical function + a0f3 Octeon 10 CPT Cryptographic Accelerator, Virtual function +# Octeon Tx2 System DPI Interface (SDP) Physical Function + a0f6 Octeon Tx2 SDP Physical Function +# Octeon Tx2 System DPI Interface (SDP) Virtual Function + a0f7 Octeon Tx2 SDP Virtual Function + a0f8 Octeon Tx2 Loopback Interface Virtual Function (LBKVF) +# Cryptographic Accelerator found on OcteonTx2 series of silicons + a0fd Octeon Tx2 CPT Cryptographic Accelerator, Physical function +# Cryptographic Accelerator found on OcteonTx2 series of silicons + a0fe Octeon Tx2 CPT Cryptographic Accelerator, Virtual function a100 THUNDERX CN88XX 48 core SoC a200 OCTEON TX CN81XX/CN80XX a300 OCTEON TX CN83XX @@ -21933,6 +22054,8 @@ 0401 Datacenter Technologies QDF2400 PCI Express Root Port 1000 QCS405 PCIe Root Complex 1101 QCA6390 Wireless Network Adapter [AX500-DBS (2x2)] + 1103 Atheros QCNFA765 + 1104 QCN6024/9024/9074 Wireless Network Adapter 17cc NetChip Technology, Inc 2280 USB 2.0 17cd Cadence Design Systems, Inc. @@ -22806,6 +22929,10 @@ 0011 FlexCard PMC-II Ethernet 0018 FlexCard PXIe3 0019 FlexCard PCIe3 +# IO card for std ethernet and automotive ethernet (ieee 1000Base-T1) + 001a FlexCard PXIe Ethernet +# IO card for std ethernet and automotive ethernet (ieee 1000Base-T1) + 001b FlexCard PCIe Ethernet 1976 TRENDnet 1977 Parsec 197b JMicron Technology Corp. @@ -23137,6 +23264,11 @@ 1aa8 Ciprico, Inc. 0009 RAIDCore Controller 000a RAIDCore Controller +1aa9 Schweitzer Engineering Laboratories + 000d SEL-3390S8 Serial Adapter + 000e SEL-3390E4 Ethernet Adapter + 0014 SEL-3390T Time and Ethernet Adapter + 0018 SEL-3390E4 Ethernet Adapter 1aae Global Velocity, Inc. 1ab4 Distributed Management Task Force, Inc. (DMTF) 1ab6 CalDigit, Inc. @@ -23207,6 +23339,8 @@ 0a58 microEnable 5 VD8-CL # CameraLink frame grabber 0a5a microEnable 5 AD8-CL +# CoaXpress frame grabber + 0a64 imaWorx CXP-12 Quad # OEM product 0b52 mE5 Abacus 4G Base # OEM product @@ -23311,12 +23445,15 @@ 1080 ASM1083/1085 PCIe to PCI Bridge 1849 1080 Motherboard 1142 ASM1042A USB 3.0 Host Controller + 1166 ASM1166 Serial ATA Controller 1182 ASM1182e 2-Port PCIe x1 Gen2 Packet Switch 1b21 118f ASM1182e 2-Port PCIe x1 Gen2 Packet Switch 1184 ASM1184e 4-Port PCIe x1 Gen2 Packet Switch 1849 1184 ASM1184e 4-Port PCIe x1 Gen2 Packet Switch + 1187 ASM1187e 7-Port PCIe x1 Gen2 Packet Switch 1242 ASM1142 USB 3.1 Host Controller 1343 ASM1143 USB 3.1 Host Controller + 1812 ASM1812 6-Port PCIe x4 Gen2 Packet Switch 2142 ASM2142 USB 3.1 Host Controller 1462 7a72 H270 PC MATE 2824 ASM2824 PCIe Gen3 Packet Switch @@ -23392,6 +23529,8 @@ 2241 88NR2241 Non-Volatile memory controller 1028 2112 BOSS-N1 Monolithic 1028 2113 BOSS-N1 Modular + 1028 2151 BOSS-N1 Modular ET + 1028 2196 ROR-N100 1d49 0306 ThinkSystem M.2 NVMe 2-Bay RAID Enablement Kit 1d49 0307 ThinkSystem 7mm NVMe 2-Bay Rear RAID Enablement Kit 9120 88SE9120 SATA 6Gb/s Controller @@ -23401,6 +23540,7 @@ 9128 88SE9128 PCIe SATA 6 Gb/s RAID controller 9130 88SE9128 PCIe SATA 6 Gb/s RAID controller with HyperDuo 1043 8438 P8P67 Deluxe Motherboard + 9170 88SE9170 PCIe 2.0 x1 2-port SATA 6 Gb/s Controller 9172 88SE9172 SATA 6Gb/s Controller 9178 88SE9170 PCIe SATA 6Gb/s Controller 917a 88SE9172 SATA III 6Gb/s RAID Controller @@ -23532,9 +23672,22 @@ 1bb1 0151 Nytro 5520 # Kersey 2.5" TCG 1bb1 0152 Nytro 5520 TCG +# Nytro 5050H (Ebonhawk - High Performance) + 1bb1 0153 Nytro 5050H +# Nytro 5050H TCG (Ebonhawk High Performance) + 1bb1 0154 Nytro 5050H TCG +# Nytro 5050M (Ebonhawk Mainstream Performance) + 1bb1 0155 Nytro 5050M +# Nytro 5050M TCG (Ebonhawk Mainstream Performance) + 1bb1 0156 Nytro 5050M TCG +# Nytro 5050M (Ebonhawk Mainstream Performance) - 7mm + 1bb1 0157 Nytro 5050M 7mm +# Nytro 5050M (Ebonhawk Mainstream Performance) TCG - 7mm + 1bb1 0158 Nytro 5050M TCG 7mm 1bb1 01a1 Nytro XP7102 5012 FireCuda 510 SSD 5016 FireCuda 520 SSD + 5018 FireCuda 530 SSD 1bb3 Bluecherry 4304 BC-04120A MPEG4 4 port video encoder / decoder 4309 BC-08240A MPEG4 4 port video encoder / decoder @@ -23688,8 +23841,22 @@ 1028 2149 DC NVMe SED PE8010 RI U.2 7.68TB 1028 214a DC NVMe PE8010 RI U.2 7.68TB 1c5c 0100 PE8000 Series NVMe Solid State Drive + 2849 PE81x0 U.2/3 NVMe Solid State Drive 1c5f Beijing Memblaze Technology Co. Ltd. 000d PBlaze5 520/526 + 000e PBlaze6 6530 + 1c5f 0b20 NVMe SSD PBlaze6 6530 1920G AIC + 1c5f 0b21 NVMe SSD PBlaze6 6530 1920G 2.5" U.2 + 1c5f 0b30 NVMe SSD PBlaze6 6530 3840G AIC + 1c5f 0b31 NVMe SSD PBlaze6 6530 3840G 2.5" U.2 + 1c5f 0b40 NVMe SSD PBlaze6 6530 7680G AIC + 1c5f 0b41 NVMe SSD PBlaze6 6530 7680G 2.5" U.2 + 1c5f 4b20 NVMe SSD PBlaze6 6530 1600G AIC + 1c5f 4b21 NVMe SSD PBlaze6 6530 1600G 2.5" U.2 + 1c5f 4b30 NVMe SSD PBlaze6 6530 3200G AIC + 1c5f 4b31 NVMe SSD PBlaze6 6530 3200G 2.5" U.2 + 1c5f 4b40 NVMe SSD PBlaze6 6530 6400G AIC + 1c5f 4b41 NVMe SSD PBlaze6 6530 6400G 2.5" U.2 003d PBlaze5 920/926 003e PBlaze6 6920 1c5f 0a31 NVMe SSD PBlaze6 6920 3840GB 2.5" U.2 @@ -23738,6 +23905,8 @@ 0002 Clarett 1cb8 Dawning Information Industry Co., Ltd. 1cc1 ADATA Technology Co., Ltd. +# 256GB NVMe SSD + 5766 ADATA XPG GAMMIXS1 1L Media 8201 XPG SX8200 Pro PCIe Gen3x4 M.2 2280 Solid State Drive 1cc4 Union Memory (Shenzhen) 1203 NVMe SSD Controller UHXXXa series @@ -23751,6 +23920,7 @@ 1cc4 a213 NVMe SSD UHXXXa series U.2 3200GB 1cc4 a214 NVMe SSD UHXXXa series U.2 6400GB 17ab NVMe 256G SSD device + 6303 AM630 PCIe 4.0 x4 NVMe SSD Controller 1cc5 Embedded Intelligence, Inc. 0100 CAN-PCIe-02 1cc7 Radian Memory Systems Inc. @@ -23946,6 +24116,7 @@ 101c AR-ARK-SRIOV-VF [Arkville Virtual Function] 101d AR-ARK-NIC [Arkville ArkNIC Kernel Path Device] 101e AR-ARKA-FX1 [Arkville 64B DPDK Data Mover for Agilex R-Tile] + 101f AR-TK242 [2x100GbE Packet Capture Device] 4200 A5PL-E1-10GETI [10 GbE Ethernet Traffic Instrument] 1d72 Xiaomi 1d78 DERA Storage @@ -24157,6 +24328,9 @@ 2000 NoLoad Hardware Development Kit 3000 eBPF-based PCIe Accelerator 1ded Alibaba (China) Co., Ltd. +# A RDMA (iWarp) device provided by Alibaba Cloud used in ECS environment + 107f Elastic RDMA Adapter + 5007 Elastic RDMA Adapter 8000 M1 Root Port 8001 ACC-RCiEP 8002 RCiEP VF @@ -24264,9 +24438,20 @@ 1028 210e Dell Ent NVMe FIPS CM6 MU 1.6TB 1028 210f Dell Ent NVMe FIPS CM6 MU 3.2TB 1028 2110 Dell Ent NVMe FIPS CM6 MU 6.4TB - 1e0f 0001 Generic NVMe CM6 RI 3.84TB + 1e0f 0001 Generic NVMe CM6 0009 NVMe SSD 1e0f 0001 Toshiba RC500 NVMe SSD 500GB + 0011 NVMe SSD Controller CD7 + 1028 2189 DC NVMe SED CD7 RI 960GB + 1028 218a DC NVMe CD7 RI 960GB + 1028 218b DC NVMe SED CD7 RI 1.92TB + 1028 218c DC NVMe CD7 RI 1.92TB + 1028 218d DC NVMe SED CD7 RI 3.84TB + 1028 218e DC NVMe CD7 RI 3.84TB + 1028 218f DC NVMe SED CD7 RI 7.68TB + 1028 2190 DC NVMe CD7 RI 7.68TB + 1028 2191 DC NVMe SED CD7 RI 15.36TB + 1028 2192 DC NVMe CD7 RI 15.36TB 1e17 Arnold & Richter Cine Technik GmbH & Co. Betriebs KG 1e24 Squirrels Research Labs 0101 Acorn CLE-101 @@ -24296,7 +24481,7 @@ 0102 Xplorer X1600 # https://www.medion.com/ 1e39 MEDION AG -1e3b Shenzhen DAPU Microelectronics Co., Ltd +1e3b DapuStor Corporation 0600 NVMe SSD Controller DPU600 1e3b 0030 Enterprise NVMe SSD U.2 3.84TB (J5100) 1e3b 0031 Enterprise NVMe SSD U.2 7.68TB (J5100) @@ -24359,6 +24544,7 @@ 1e3b 0091 Enterprise NVMe SSD HHHL 0.75TB (H3900) 1e3d Burlywood, Inc 1e49 Yangtze Memory Technologies Co.,Ltd + 0041 ZHITAI TiPro7000 # YMTC PCIe/NVMe SSD 1013 PC210 1e4b MAXIO Technology (Hangzhou) Ltd. @@ -24377,6 +24563,9 @@ 0000 0100 PY8800 64GB Accelerator 1e59 Oxford Nanopore Technologies 0001 MinION Mk1C +1e5d ASR Microelectronics + 7000 AI controller A7000 + 7010 AI controller A7010 1e60 Hailo Technologies Ltd. 2864 Hailo-8 AI Processor 1e68 Jiangsu Xinsheng Intelligent Technology Co., Ltd @@ -24414,14 +24603,22 @@ 1eab Hefei DATANG Storage Technology Co.,LTD. 300a NVMe SSD Controller 300A 300b NVMe SSD Controller 300B +1eac Quectel Wireless Solutions Co., Ltd. + 1001 EM120R-GL LTE Modem + 1002 EM160R-GL LTE Modem 1eae XFX Limited 1eb1 VeriSilicon Inc 1001 Video Accelerator +1eb4 Quantum Nebula Microelectronics Technology Co.,Ltd. + 3401 SSD Contoller 1ebd EMERGETECH Company Ltd. 0101 Seirios 2063 Video Codec 1ed2 FuriosaAI, Inc. 0000 Warboy 1ed3 Yeston +1ed5 Moore Threads Technology Co.,Ltd + 0100 MTT S10 + 0101 MTT S30 1ed8 Digiteq Automotive 0101 FG4 PCIe Frame Grabber 1ed9 Myrtle.ai @@ -24439,6 +24636,13 @@ 5220 IG5220-Based NVMe SSD 5236 IG5236-Based NVMe SSD 5636 IG5636-Based NVMe SSD +1f2f China Mobile (Hangzhou) Information Technology Co.Ltd. + 1513 DERA MENG NVMe Controller + 1f2f 6113 KM660 U.2 1.6TB NVMe SSD + 1f2f 6114 KM560 U.2 1.92TB NVMe SSD + 1f2f 6115 KM660 U.2 3.2TB NVMe SSD + 1f2f 6116 KM560 U.2 3.84TB NVMe SSD + 1f2f 6118 KM560 U.2 7.68TB NVMe SSD 1fab Unifabrix Ltd. 0000 Nexus Alpha IVPU # nee Tumsan Oy @@ -25182,6 +25386,12 @@ 1400 CooVOX TDM GSM Module 1600 CooVOX TDM E1/T1 Module 1800 CooVOX TDM BRI Module +6766 Glenfly Tech Co., Ltd. + 3d00 Arise-GT-10C0 + 3d02 Arise 1020 + 3d40 Arise-GT-10C0 High Definition Audio Controller + 3d41 Arise 1020 High Definition Audio Controller +6899 ZT Systems # nee Qumranet 6900 Red Hat, Inc. 7063 pcHDTV @@ -25194,6 +25404,9 @@ 7401 EndRun Technologies e100 PTP3100 PCIe PTP Slave Clock 7470 TP-LINK Technologies Co., Ltd. +7526 HongQin (Beijing) Technology Co., Ltd. + 0082 HQ SSD 1TB + 0083 HQ SSD 2TB M.2 NVMe 7604 O.N. Electronic Co Ltd. 7bde MIDAC Corporation 7fed PowerTV @@ -26006,6 +26219,12 @@ 1028 2102 NVMe RI U.2 1.92TB (P5500) 1028 2103 NVMe RI U.2 3.84TB (P5500) 1028 2104 NVMe RI U.2 7.68TB (P5500) + 1028 219a NVMe P5316 RI 15.36TB + 1028 219b NVMe P5316 RI 30.72TB + 1028 219c NVMe SED P5316 RI 15.36 + 1028 219d NVMe SED P5316 RI 30.72 + 1028 219e NVMe FIPS P5316 RI 15.36TB + 1028 219f NVMe FIPS P5316 RI 30.72 8086 8008 NVMe Datacenter SSD [3DNAND] SE 2.5" U.2 (P5510) 8086 8d08 NVMe Datacenter SSD [3DNAND] VE 2.5" U.2 (P5316) 8086 8d1d NVMe Datacenter SSD [3DNAND] VE E1.L 9.5/18mm (P5316) @@ -26114,8 +26333,15 @@ 8086 0001 Ethernet Controller XXV710 Intel(R) FPGA Programmable Acceleration Card N3000 for Networking 0d9f Ethernet Controller (2) I225-IT 0dd2 Ethernet Network Adapter I710 + 1137 0000 I710T4LG 4x1 GbE RJ45 PCIe NIC + 1137 02e3 I710T4LG 4x1 GbE RJ45 PCIe NIC + 8086 0000 Ethernet Network Adapter I710-T4L 8086 000d Ethernet Network Adapter I710-T4L 8086 0010 Ethernet Network Adapter I710-T4L for OCP 3.0 + 8086 401a Ethernet Network Adapter I710-T4L + 8086 401b Ethernet Network Adapter I710-T4L for OCP 3.0 + 0dda Ethernet Connection X722 for 10GbE SFP+ + 1bd4 0076 Ethernet Connection X722 for 10GbE SFP+ 0e00 Xeon E7 v2/Xeon E5 v2/Core i7 DMI2 1028 04f7 Xeon E5 v2 on PowerEdge R320 server 15d9 066b X9SRL-F @@ -27466,6 +27692,7 @@ 8086 000c Ethernet 100G 2P E810-C OCP 8086 000d Ethernet Network Adapter E810-L-Q2 for OCP 3.0 8086 000e Ethernet Network Adapter E810-2C-Q2 + 8086 0010 Ethernet 100G 2P E810-C-stg Adapter 1593 Ethernet Controller E810-C for SFP 1137 02c3 E810XXVDA4 4x25/10 GbE SFP28 PCIe NIC 8086 0002 Ethernet Network Adapter E810-L-2 @@ -27478,7 +27705,10 @@ 8086 000c Ethernet Network Adapter E810-XXV-4 for OCP 3.0 8086 000d Ethernet 25G 4P E810-XXV OCP 8086 000e Ethernet Network Adapter E810-XXV-4T + 8086 000f Ethernet 25G 4P E810-XXV-stg Adapter + 8086 0010 Ethernet 25G 4P E810-XXV-st Adapter 1599 Ethernet Controller E810-XXV for backplane + 8086 0001 Ethernet 25G 2P E810-XXV-k Mezz 159a Ethernet Controller E810-XXV for QSFP 159b Ethernet Controller E810-XXV for SFP 1137 02be E810XXVDA2 2x25/10 GbE SFP28 PCIe NIC @@ -31187,6 +31417,7 @@ 3432 5520/5500/X58 Chipset QuickData Technology Device 3433 5520/5500/X58 Chipset QuickData Technology Device 3438 7500/5520/5500/X58 I/O Hub Throttle Registers + 347e Ice Lake Xeon Non-Transparent Bridge 3482 Ice Lake-LP LPC Controller 34a3 Ice Lake-LP SMBus Controller 34a4 Ice Lake-LP SPI Controller @@ -31997,8 +32228,10 @@ 8086 02a4 Wireless-AC 9462 444e Turbo Memory Controller 460d 12th Gen Core Processor PCI Express x16 Controller #1 + 461d Alder Lake Innovation Platform Framework Processor Participant 461e Alder Lake-P Thunderbolt 4 USB Controller 461f Alder Lake-P Thunderbolt 4 PCI Express Root Port #3 + 4626 Alder Lake-P Integrated Graphics Controller 4629 12th Gen Core Processor Host Bridge/DRAM Registers 462f Alder Lake-P Thunderbolt 4 PCI Express Root Port #2 463d 12th Gen Core Processor PCI Express x4 Controller #2 @@ -32007,6 +32240,7 @@ 4641 12th Gen Core Processor Host Bridge/DRAM Registers 464d 12th Gen Core Processor PCI Express x4 Controller #0 464f 12th Gen Core Processor Gaussian & Neural Accelerator + 4660 12th Gen Core Processor Host Bridge/DRAM Registers 466d Alder Lake-P Thunderbolt 4 NHI #1 466e Alder Lake-P Thunderbolt 4 PCI Express Root Port #0 467d Platform Monitoring Technology @@ -32015,6 +32249,7 @@ 46a0 AlderLake-P GT2 46a1 UHD Graphics 46a3 Alder Lake-P GT1 [UHD Graphics] + 46a6 Alder Lake-P Integrated Graphics Controller 46c0 AlderLake-M GT1 4905 DG1 [Iris Xe MAX Graphics] 4906 DG1 [Iris Xe Pod] @@ -32026,8 +32261,9 @@ 4c8b RocketLake-S GT1 [UHD Graphics 730] 4c90 RocketLake-S GT1 [UHD Graphics P750] 4c9a RocketLake-S [UHD Graphics] - 4da3 JaserLake SMBus - 4da4 JaserLake SPI (flash) Controller + 4da3 Jasper Lake SMBus + 4da4 Jasper Lake SPI Controller + 4dc8 Jasper Lake HD Audio 4de0 Management Engine Interface 4de8 Serial IO I2C Host Controller 4de9 Serial IO I2C Host Controller @@ -32088,8 +32324,11 @@ 504b EP80579 Reserved 504c EP80579 Integrated Processor with QuickAssist TDM 5181 Alder Lake PCH-P LPC/eSPI Controller + 5182 Alder Lake PCH eSPI Controller 51a3 Alder Lake PCH-P SMBus Host Controller 51a4 Alder Lake-P PCH SPI Controller + 51a8 Alder Lake PCH UART #0 + 51a9 Alder Lake PCH UART #1 51bf Alder Lake PCH-P PCI Express Root Port #9 51c5 Alder Lake-P Serial IO I2C Controller #0 51c6 Alder Lake-P Serial IO I2C Controller #1 @@ -32471,6 +32710,20 @@ 10b4 202f Lightspeed 740 8086 0000 Terminator 2x/i 8086 0100 Intel740 Graphics Accelerator +# Unlike other PCH components. The eSPI controller is specific to each chipset model + 7a84 Z690 Chipset LPC/eSPI Controller + 7aa3 Alder Lake-S PCH SMBus Controller + 7aa4 Alder Lake-S PCH SPI Controller + 7aa7 Alder Lake-S PCH Shared SRAM + 7ab4 Alder Lake-S PCH PCI Express Root Port #13 + 7abd Alder Lake-S PCH PCI Express Root Port #6 + 7acc Alder Lake-S PCH I2C Controller #0 + 7ad0 Alder Lake-S HD Audio Controller + 7ae0 Alder Lake-S PCH USB 3.2 Gen 2x2 XHCI Controller + 7ae2 Alder Lake-S PCH SATA Controller [AHCI Mode] + 7ae8 Alder Lake-S PCH HECI Controller #1 + 7af0 Alder Lake-S PCH CNVi WiFi + 8086 0094 Wi-Fi 6 AX201 160MHz 8002 Trusted Execution Technology Registers 8003 Trusted Execution Technology Registers 8100 US15W/US15X SCH [Poulsbo] Host Bridge @@ -32840,6 +33093,7 @@ 1028 09be Latitude 7410 9b63 10th Gen Core Processor Host Bridge/DRAM Registers 9b64 10th Gen Core Processor Host Bridge/DRAM Registers + 9ba8 CometLake-S GT1 [UHD Graphics 610] 9bc4 CometLake-H GT2 [UHD Graphics] 9bc5 CometLake-S GT2 [UHD Graphics 630] 9bc8 CometLake-S GT2 [UHD Graphics 630] @@ -33040,6 +33294,7 @@ 9d3d Sunrise Point-LP Active Management Technology - SOL 103c 8079 EliteBook 840 G3 17aa 2247 ThinkPad T570 + 9d3e iTouch Controller 9d43 Sunrise Point-LP LPC Controller 17aa 382a B51-80 Laptop 9d46 LPC/eSPI Controller @@ -33094,6 +33349,7 @@ 9da4 Cannon Point-LP SPI Controller 9da8 Cannon Point-LP Serial IO UART Controller #2 9daa Cannon Point-LP Serial IO SPI Controller + 9dab Cannon Point-LP Serial IO SPI Controller 9db0 Cannon Point-LP PCI Express Root Port #9 9db1 Cannon Point-LP PCI Express Root Port #10 9db2 Cannon Point-LP PCI Express Root Port #1 @@ -33104,6 +33360,7 @@ 9dbc Cannon Point-LP PCI Express Root Port #5 9dbe Cannon Point-LP PCI Express Root Port #7 9dbf Cannon Point PCI Express Root Port #8 + 9dc4 Cannon Point-LP SD Host Controller 9dc5 Cannon Point-LP Serial IO I2C Host Controller 9dc8 Cannon Point-LP High Definition Audio Controller 1028 089e Inspiron 5482 @@ -33467,7 +33724,7 @@ a397 Comet Lake PCI Express Root Port #08 a398 Comet Lake PCI Express Root Port 9 a39a Comet Lake PCI Express Root Port 11 - a3a1 Memory controller + a3a1 Cannon Lake PCH Power Management Controller a3a3 Comet Lake PCH-V SMBus Host Controller a3af Comet Lake PCH-V USB Controller a3b1 Comet Lake PCH-V Thermal Subsystem @@ -33958,6 +34215,8 @@ 1bd4 0070 RS0800M5E24i 1bd4 0071 RS0800M5H16i 1bd4 0072 RS0800M5E16i + 1cc4 0101 Ramaxel FBGF-RAD PM8204 + 1cc4 0201 Ramaxel FBGF-RAD PM8222 1d49 0220 ThinkSystem 4350-8i SAS/SATA 12Gb HBA 1d49 0221 ThinkSystem 4350-16i SAS/SATA 12Gb HBA 1d49 0520 ThinkSystem RAID 5350-8i PCIe 12Gb Adapter @@ -34244,6 +34503,8 @@ cddd Tyzx, Inc. 0101 DeepSea 1 High Speed Stereo Vision Frame Grabber 0200 DeepSea 2 High Speed Stereo Vision Frame Grabber ceba KEBA AG +cf86 Spectrum-4TOR + 0276 Spectrum-4TOR in Flash Recovery Mode d161 Digium, Inc. 0120 Wildcard TE120P single-span T1/E1/J1 card 0205 Wildcard TE205P/TE207P dual-span T1/E1/J1 card 5.0V @@ -34728,8 +34989,7 @@ C 11 Signal processing controller 80 Signal processing controller C 12 Processing accelerators 00 Processing accelerators -# For the class of PCI attached devices which perform a function of Deep Learning Neural Network inference acceleration - 01 AI Inference Accelerator + 01 SNIA Smart Data Accelerator Interface (SDXI) controller C 13 Non-Essential Instrumentation C 40 Coprocessor C ff Unassigned class diff --git a/hwdb.d/pnp_id_registry.html b/hwdb.d/pnp_id_registry.html index 9b98e18c6..a1d6f63bc 100644 --- a/hwdb.d/pnp_id_registry.html +++ b/hwdb.d/pnp_id_registry.html @@ -2499,6 +2499,9 @@ Kopin CorporationKOP10/01/2021 Anker Innovations LimitedAKR12/10/2021 SAMPO CORPORATIONSPO12/10/2021 + Shiftall Inc.SFL12/31/2021 + AudioControlAUD12/31/2021 + Schneider Consumer GroupSCA02/08/2022 diff --git a/hwdb.d/usb.ids b/hwdb.d/usb.ids index 1b9f9f82b..d02e59f5e 100644 --- a/hwdb.d/usb.ids +++ b/hwdb.d/usb.ids @@ -9,8 +9,8 @@ # The latest version can be obtained from # http://www.linux-usb.org/usb.ids # -# Version: 2021.10.24 -# Date: 2021-10-24 20:34:08 +# Version: 2022.03.18 +# Date: 2022-03-18 20:34:09 # # Vendors, devices and interfaces. Please keep sorted. @@ -2712,6 +2712,8 @@ 0845 ConferenceCam CC3000e Camera 0846 ConferenceCam CC3000e Speakerphone 084b ConferenceCam Connect Video + 084c ConferenceCam Connect Audio + 084e ConferenceCam Connect 0850 QuickCam Web 0857 Logi Group Speakerphone 085c C922 Pro Stream Webcam @@ -2764,7 +2766,7 @@ 08d7 QuickCam Communicate STX 08d8 QuickCam for Notebook Deluxe 08d9 QuickCam IM/Connect - 08da QuickCam Messanger + 08da QuickCam Messenger 08dd QuickCam for Notebooks 08e0 QuickCam Express 08e1 Labtec Webcam @@ -2903,7 +2905,7 @@ c06a USB Optical Mouse c06b G700 Wireless Gaming Mouse c06c Optical Mouse - c077 M105 Optical Mouse + c077 Mouse c07c M-R0017 [G700s Rechargeable Gaming Mouse] c07d G502 Mouse c07e G402 Gaming Mouse @@ -2911,7 +2913,7 @@ c083 G403 Prodigy Gaming Mouse c084 G203 Gaming Mouse c08b G502 SE HERO Gaming Mouse - c092 G203 LIGHTSYNC Gaming Mouse + c092 G102/G203 LIGHTSYNC Gaming Mouse c101 UltraX Media Remote c110 Harmony 785/880/885 Remote c111 Harmony 525 Remote @@ -3060,6 +3062,7 @@ c532 Unifying Receiver c534 Unifying Receiver c537 Cordless Mouse Receiver + c539 Cordless Mouse Receiver c53a PowerPlay Wireless Charging System c53d G631 Keyboard c603 3Dconnexion Spacemouse Plus XT @@ -19572,10 +19575,11 @@ 5512 CH341 in EPP/MEM/I2C mode, EPP/I2C adapter 5523 CH341 in serial mode, usb to serial port converter 5584 CH341 in parallel mode, usb to printer port converter + 7522 CH340 serial converter 7523 CH340 serial converter 752d CH345 MIDI adapter 7584 CH340S - e008 HID-based serial adapater + e008 HID-based serial adapter 1a89 Dynalith Systems Co., Ltd. 1a8b SGS Taiwan Ltd. 1a8d BandRich, Inc. @@ -20338,7 +20342,7 @@ 6052 APB Team Robotic Development Board 6053 Darkgame Controller 6054 Satlab/AAUSAT3 BlueBox - 6055 RADiuS ER900TRS-02 transciever with SMA Connector + 6055 RADiuS ER900TRS-02 transceiver with SMA Connector 6056 The Glitch 6057 OpenPipe MIDI Shield 6058 Novena OTG port @@ -20475,10 +20479,10 @@ 60ec Duet 2 WiFi or Duet 2 Ethernet 3D printer control electronics 60ed Duet 2 Maestro 3D printer control electronics 60ee Duet 3 motion control electronics - 60f0 UDAD-T1 data aquisition device (boot) - 60f1 UDAD-T1 data aquisition device - 60f2 UDAD-T2 data aquisition device (boot) - 60f3 UDAD-T2 data aquisition device + 60f0 UDAD-T1 data acquisition device (boot) + 60f1 UDAD-T1 data acquisition device + 60f2 UDAD-T2 data acquisition device (boot) + 60f3 UDAD-T2 data acquisition device 60f4 Uniti ARC motor controller 60f5 EightByEight Blinky Badge (DFU) 60f6 EightByEight Blinky Badge @@ -20494,6 +20498,7 @@ 6118 Thomson MO5 keyboard 6122 Ultimate Hacking Keyboard 614c dwtk In-Circuit Emulator + 614d Generic Display 8085 Box0 (box0-v5) cc15 rad1o badge for CCC summer camp 2015 1d57 Xenta @@ -20511,7 +20516,7 @@ ad03 [T3] 2.4GHz and IR Air Mouse Remote Control af01 AUVIO Universal Remote Receiver for PlayStation 3 af03 Wireless Receiver - fa20 2.4GHz Wireless Reciever (Mini Keyboard & Mouse) + fa20 2.4GHz Wireless Receiver (Mini Keyboard & Mouse) 1d5b Smartronix, Inc. 1d5c Fresco Logic 2000 FL2000/FL2000DX VGA/DVI/HDMI Adapter @@ -20819,7 +20824,12 @@ 0001 Wi-Fi Body Scale (WBS01) 1fba DERMALOG Identification Systems GmbH 1fbd Delphin Technology AG - 0001 Expert Key - Data aquisition system + 0001 Expert Key - Data acquisition system + 0004 MetiOS Device (RNDIS) + 0005 Loggito + 0006 LoggitoLab 8 AI-RTD + 0007 LoggitoLab 8 TC + 0008 LoggitoLab 4 AI-RTD 4 TC 1fc9 NXP Semiconductors 0003 LPC1343 000c LPC4330FET180 [ARM Cortex M4 + M0] (device firmware upgrade mode) @@ -22644,6 +22654,14 @@ f190 MSO-19 f280 MSO-28 f281 MSO-28 +3197 Katusha + 1001 M151 + 1002 M250 + 1003 P130 + 1004 M130 + 1101 P247 + 1102 M247 + 1103 M348 31c9 BeiJing LanXum Computer Technology Co., Ltd. 1001 Printer 1301 Black and White Laser Printer @@ -23238,7 +23256,12 @@ 0780 CS780 Microphone Input 07d3 BLOB boot loader firmware 07dc Bluetooth 4.0* Smart Ready (low energy) + 0a66 RealSense 3D Camera (Front F200) + 0aa5 RealSense SR300 + 0ad2 RealSense D410 + 0ad3 RealSense D415 0b07 RealSense D435 + 0b64 RealSense L515 0dad Cherry MiniatureCard Keyboard 1010 AnyPoint(TM) Home Network 10 Mbps Phoneline Adapter 110a Bluetooth Controller from (Ericsson P4A) @@ -23264,6 +23287,7 @@ 9303 8x930Hx Hub 9500 CE 9500 DVB-T 9890 82930 Test Board + a36d Host Controller beef SCM Miniature Card Reader/Writer c013 Wireless HID Station dead Galileo @@ -23278,7 +23302,6 @@ 0032 AX210 Bluetooth 0716 Modem Flashloader 07da Centrino Bluetooth Wireless Transceiver - 8087 07da Centrino Advanced-N 6235 07db Atom C2000 Root Hub 07dc Bluetooth wireless interface 07eb Oaktrail tablet diff --git a/man/bootctl.xml b/man/bootctl.xml index 16c498579..8d5b1d3d2 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -92,7 +92,10 @@ url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification, as well as any other entries discovered or automatically generated by a boot loader implementing the Boot Loader - Interface. + Interface. + + JSON output may be requested with . + @@ -258,26 +261,64 @@ - - Control creation and deletion of the top-level machine ID directory on the file - system containing boot loader entries (i.e. beneath the file system returned by the - option, see above) during and - , respectively. Defaults to no. See + + Controls creation and deletion of the Boot Loader Specification Type #1 entry + directory on the file system containing resources such as kernel images and initial RAM disk images + during and , respectively. The directory is named + after the entry token, as specified with parameter described below, + and is placed immediately below the $BOOT root directory (i.e. beneath the file + system returned by the option, see above). Defaults to + no. + + + + + + Controls how to name and identify boot loader entries for this OS + installation. Accepted during , and takes one of auto, + machine-id, os-id, os-image-id or an + arbitrary string prefixed by literal: as argument. + + If set to the entries are named after the machine ID of the running + system (e.g. b0e793a9baf14b5fa13ecbe84ff637ac). See machine-id5 for details about the machine ID concept and file. - Overriding this may be desirable to hide the machine ID from the (unencrypted) ESP, configure a - kernel-install8 - script, or, conversely, commit a transient machine ID. + If set to the entries are named after the OS ID of the running system, + i.e. the ID= field of + os-release5 + (e.g. fedora). Similar, if set to the entries are + named after the OS image ID of the running system, i.e. the IMAGE_ID= field of + os-release (e.g. vendorx-cashier-system). - The top-level machine ID directory is useful to allow smooth multi-boot installations: each - installed OS instance will have a different machine ID and thus a separate directory to place its - boot-time resources in. If this feature is turned off with this option, care needs to be taken that - multiple OS instances do not place conflicting files on the shared ESP and Extended Boot Loader - Partitions, or that multiple OS instances are not possible. + If set to (the default), the /etc/kernel/entry-token + file will be read if it exists, and the stored value used. Otherwise if the local machine ID is + initialized it is used. Otherwise IMAGE_ID= from os-release + will be used, if set. Otherwise, ID= from os-release will be + used, if set. + + Unless set to machine-id, or when + is used the selected token string is written to a file + /etc/kernel/entry-token, to ensure it will be used for future entries. This file + is also read by + kernel-install8, + in order to identify under which name to generate boot loader entries for newly installed kernels, or + to determine the entry names for removing old ones. + + Using the machine ID for naming the entries is generally preferable, however there are cases + where using the other identifiers is a good option. Specifically: if the identification data that the + machine ID entails shall not be stored on the (unencrypted) $BOOT partition, or if + the ID shall be generated on first boot and is not known when the entries are prepared. Note that + using the machine ID has the benefit that multiple parallel installations of the same OS can coexist + on the same medium, and they can update their boot loader entries independently. When using another + identifier (such as the OS ID or the OS image ID), parallel installations of the same OS would try to + use the same entry name. To support parallel installations, the installer must use a different entry + token when adding a second installation. + diff --git a/man/bootup.xml b/man/bootup.xml index 431d19a48..b29e494ee 100644 --- a/man/bootup.xml +++ b/man/bootup.xml @@ -106,21 +106,21 @@ | swap.target | | v v | | | v | remote-cryptsetup.target | | | (various low-level (various mounts and | remote-veritysetup.target | - | | services: udevd, fsck services...) | | remote-fs.target - | | tmpfiles, random | | | / - | | seed, sysctl, ...) v | | / - | | | local-fs.target | | / - | | | | | | / - \____|______|_______________ ______|___________/ | / - \ / | / - v | / - sysinit.target | / - | | / - ______________________/|\_____________________ | / - / | | | \ | / - | | | | | | / - v v | v | | / - (various (various | (various | |/ + | | services: udevd, fsck services...) | | | + | | tmpfiles, random | | | remote-fs.target + | | seed, sysctl, ...) v | | | + | | | local-fs.target | | _____________/ + | | | | | |/ + \____|______|_______________ ______|___________/ | + \ / | + v | + sysinit.target | + | | + ______________________/|\_____________________ | + / | | | \ | + | | | | | | + v v | v | | + (various (various | (various | | timers...) paths...) | sockets...) | | | | | | | | v v | v | | diff --git a/man/busctl.xml b/man/busctl.xml index cc5a6508e..bcd3f4805 100644 --- a/man/busctl.xml +++ b/man/busctl.xml @@ -78,10 +78,10 @@ capture SERVICE Similar to monitor but - writes the output in pcap format (for details, see the Libpcap - File Format description). Make sure to redirect - standard output to a file. Tools like + writes the output in pcapng format (for details, see + + PCAP Next Generation (pcapng) Capture File Format). + Make sure to redirect standard output to a file or pipe. Tools like wireshark1 may be used to dissect and view the resulting files. diff --git a/man/common-variables.xml b/man/common-variables.xml index 2f478cb45..0e220b3f9 100644 --- a/man/common-variables.xml +++ b/man/common-variables.xml @@ -90,7 +90,10 @@ less1 and more1, until one is found. If no pager implementation is discovered no pager is invoked. Setting this environment variable to an empty string - or the value cat is equivalent to passing . + or the value cat is equivalent to passing . + + Note: if $SYSTEMD_PAGERSECURE is not set, $SYSTEMD_PAGER + (as well as $PAGER) will be silently ignored. diff --git a/man/coredump.conf.xml b/man/coredump.conf.xml index 639227054..7cb71f80a 100644 --- a/man/coredump.conf.xml +++ b/man/coredump.conf.xml @@ -85,7 +85,8 @@ The maximum size in bytes of a core which will be processed. Core dumps exceeding this size may be stored, but the backtrace will not be generated. Like other sizes in this same - config file, the usual suffixes to the base of 1024 are allowed (B, K, M, G, T, P, and E). + config file, the usual suffixes to the base of 1024 are allowed (B, K, M, G, T, P, and E). Defaults + to 1G on 32bit systems, 32G on 64bit systems. Setting Storage=none and ProcessSizeMax=0 disables all coredump handling except for a log entry. @@ -96,8 +97,9 @@ ExternalSizeMax= JournalSizeMax= - The maximum (compressed or uncompressed) size in bytes of a core to be saved. Unit - suffixes are allowed just as in . + The maximum (compressed or uncompressed) size in bytes of a core to be saved in + separate files on disk (default: 1G on 32bit, 32G on 64bit systems) or in the journal (default: + 10M). Unit suffixes are allowed just as in . ExternalSizeMax=infinity sets the core size to unlimited. diff --git a/man/crypttab.xml b/man/crypttab.xml index ac5c6ef66..22411166a 100644 --- a/man/crypttab.xml +++ b/man/crypttab.xml @@ -677,6 +677,14 @@ of the current PCR state. + + + + Takes a boolean argument, defaults to false. Controls whether + TPM2 volume unlocking is bound to a PIN in addition to PCRs. Similarly, this option is only useful + when TPM2 enrollment metadata is not available. + + diff --git a/man/event-quick-child.c b/man/event-quick-child.c new file mode 100644 index 000000000..16cc6cf3a --- /dev/null +++ b/man/event-quick-child.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: CC0-1.0 */ + +#include +#include +#include +#include + +int main(int argc, char **argv) { + pid_t pid = fork(); + assert(pid >= 0); + + /* SIGCHLD signal must be blocked for sd_event_add_child to work */ + sigset_t ss; + sigemptyset(&ss); + sigaddset(&ss, SIGCHLD); + sigprocmask(SIG_BLOCK, &ss, NULL); + + if (pid == 0) /* child */ + sleep(1); + + else { /* parent */ + sd_event *e = NULL; + int r; + + /* Create the default event loop */ + sd_event_default(&e); + assert(e); + + /* We create a floating child event source (attached to 'e'). + * The default handler will be called with 666 as userdata, which + * will become the exit value of the loop. */ + r = sd_event_add_child(e, NULL, pid, WEXITED, NULL, (void*) 666); + assert(r >= 0); + + r = sd_event_loop(e); + assert(r == 666); + + sd_event_unref(e); + } + + return 0; +} diff --git a/man/halt.xml b/man/halt.xml index 4b3beb80b..7ab21c148 100644 --- a/man/halt.xml +++ b/man/halt.xml @@ -84,13 +84,8 @@ - Force immediate halt, power-off, or reboot. When - specified once, this results in an immediate but clean shutdown - by the system manager. When specified twice, this results in an - immediate shutdown without contacting the system manager. See the - description of in - systemctl1 - for more details. + Force immediate halt, power-off, reboot. Do + not contact the init system. diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index d6123491f..79bd90b34 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -428,6 +428,28 @@ + + systemd.watchdog_pre_sec= + + + Overrides the watchdog pre-timeout settings otherwise configured with + RuntimeWatchdogPreSec=. Takes a time value (if no unit is specified, seconds is the + implicitly assumed time unit) or the special strings off or + default. For details, see + systemd-system.conf5. + + + + + systemd.watchdog_pretimeout_governor= + + + Overrides the watchdog pre-timeout settings otherwise configured with + RuntimeWatchdogPreGovernor=. Takes a string value. For details, see + systemd-system.conf5. + + + systemd.cpu_affinity= diff --git a/man/kernel-install.xml b/man/kernel-install.xml index 83255bb93..d6a5e4303 100644 --- a/man/kernel-install.xml +++ b/man/kernel-install.xml @@ -39,21 +39,21 @@ /boot/, /efi/, or /boot/efi/, see below. - kernel-install will execute the files - located in the directory /usr/lib/kernel/install.d/ - and the local administration directory /etc/kernel/install.d/. - All files are collectively sorted and executed in lexical order, regardless of the directory in - which they live. However, files with identical filenames replace each other. - Files in /etc/kernel/install.d/ take precedence over files with the same name - in /usr/lib/kernel/install.d/. This can be used to override a system-supplied - executables with a local file if needed; a symbolic link in /etc/kernel/install.d/ - with the same name as an executable in /usr/lib/kernel/install.d/, - pointing to /dev/null, disables the executable entirely. Executables must have the - extension .install; other extensions are ignored. + kernel-install will run the executable files ("plugins") located in the + directory /usr/lib/kernel/install.d/ and the local administration directory + /etc/kernel/install.d/. All files are collectively sorted and executed in lexical + order, regardless of the directory in which they live. However, files with identical filenames replace + each other. Files in /etc/kernel/install.d/ take precedence over files with the + same name in /usr/lib/kernel/install.d/. This can be used to override a + system-supplied executables with a local file if needed; a symbolic link in + /etc/kernel/install.d/ with the same name as an executable in + /usr/lib/kernel/install.d/, pointing to /dev/null, disables the + executable entirely. Executables must have the extension .install; other extensions + are ignored. - An executable should return 0 on success. It may also - return 77 to cause the whole operation to terminate - (executables later in lexical order will be skipped). + An executable placed in these directories should return 0 on success. It may + also return 77 to cause the whole operation to terminate (executables later in + lexical order will be skipped). @@ -64,37 +64,45 @@ add KERNEL-VERSION KERNEL-IMAGE [INITRD-FILE ...] This command expects a kernel version string and a path to a kernel image file as - arguments. kernel-install calls the executables from - /usr/lib/kernel/install.d/*.install and - /etc/kernel/install.d/*.install with the following arguments: + arguments. Optionally, one or more initial RAM disk images may be specified as well (note that + plugins might generate additional ones). kernel-install calls the executable + files from /usr/lib/kernel/install.d/*.install and + /etc/kernel/install.d/*.install (i.e. the plugins) with the following + arguments: - add KERNEL-VERSION $BOOT/MACHINE-ID/KERNEL-VERSION/ KERNEL-IMAGE [INITRD-FILE ...] - + add KERNEL-VERSION $BOOT/ENTRY-TOKEN/KERNEL-VERSION/ KERNEL-IMAGE [INITRD-FILE ...] + + The third argument directly refers to the path where to place kernel images, initial RAM disk + images and other resources for Boot + Loader Specification Type #1 entries (the "entry directory"). If other boot loader schemes + are used the parameter may be ignored. The ENTRY-TOKEN string is + typically the machine ID and is supposed to identify the local installation on the system. For + details see below. Two default plugins execute the following operations in this case: kernel-install creates - $BOOT/MACHINE-ID/KERNEL-VERSION, - if enabled (see $KERNEL_INSTALL_LAYOUT=). + $BOOT/ENTRY-TOKEN/KERNEL-VERSION, + if enabled (see $KERNEL_INSTALL_LAYOUT). 50-depmod.install runs depmod8 for the KERNEL-VERSION. - 90-loaderentry.install - copies KERNEL-IMAGE to - $BOOT/MACHINE-ID/KERNEL-VERSION/linux. + 90-loaderentry.install copies + KERNEL-IMAGE to + $BOOT/ENTRY-TOKEN/KERNEL-VERSION/linux. If INITRD-FILEs are provided, it also copies them to - $BOOT/MACHINE-ID/KERNEL_VERSION/INITRD-FILE. + $BOOT/ENTRY-TOKEN/KERNEL_VERSION/INITRD-FILE. It also creates a boot loader entry according to the Boot Loader Specification in - $BOOT/loader/entries/MACHINE-ID-KERNEL-VERSION.conf. + url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification (Type #1) in + $BOOT/loader/entries/ENTRY-TOKEN-KERNEL-VERSION.conf. The title of the entry is the PRETTY_NAME parameter specified in - /etc/os-release or /usr/lib/os-release (if the former is - missing), or "Linux KERNEL-VERSION", if unset. + /etc/os-release or /usr/lib/os-release (if the former + is missing), or "Linux KERNEL-VERSION", if unset. - If $KERNEL_INSTALL_LAYOUT= is not "bls", this plugin does nothing. + If $KERNEL_INSTALL_LAYOUT is not "bls", this plugin does nothing. @@ -104,13 +112,13 @@ This command expects a kernel version string as single argument. This calls executables from /usr/lib/kernel/install.d/*.install and /etc/kernel/install.d/*.install with the following arguments: - - remove KERNEL-VERSION $BOOT/MACHINE-ID/KERNEL-VERSION/ - Afterwards, kernel-install removes the directory - $BOOT/MACHINE-ID/KERNEL-VERSION/ - and its contents. + remove KERNEL-VERSION $BOOT/ENTRY-TOKEN/KERNEL-VERSION/ + + Afterwards, kernel-install removes the entry directory + $BOOT/ENTRY-TOKEN/KERNEL-VERSION/ + and its contents, if it exists. Two default plugins execute the following operations in this case: @@ -118,14 +126,18 @@ 50-depmod.install removes the files generated by depmod for this kernel again. 90-loaderentry.install removes the file - $BOOT/loader/entries/MACHINE-ID-KERNEL-VERSION.conf. - - kernel-install removes - $BOOT/MACHINE-ID/KERNEL-VERSION, - if enabled (see $KERNEL_INSTALL_LAYOUT=). + $BOOT/loader/entries/ENTRY-TOKEN-KERNEL-VERSION.conf. + + inspect + + Shows the various paths and parameters configured or auto-detected. In particular shows the + values of the various $KERNEL_INSTALL_* environment variables listed + below. + + @@ -135,9 +147,9 @@ The partition where the kernels and Boot Loader Specification snippets are located is called $BOOT. kernel-install determines the location of this partition by checking - /efi/, /boot/, and /boot/efi/ - in turn. The first location where $BOOT/loader/entries/ or - $BOOT/$MACHINE_ID/ exists is used. + /efi/, /boot/, and /boot/efi/ in turn. The + first location where $BOOT/loader/entries/ or + $BOOT/ENTRY-TOKEN/ exists is used. @@ -163,28 +175,57 @@ If is used, $KERNEL_INSTALL_VERBOSE=1 will be set for the plugins. They may output additional logs in this case. - If MACHINE_ID= is set and not empty, it will be used as MACHINE-ID, - overriding any automatic detection attempts. The value must be a valid machine ID (32 hexadecimal characters). + If $MACHINE_ID is set and not empty when kernel-install is + invoked, it will be used as MACHINE-ID, overriding any automatic detection + attempts. The value must be a valid machine ID (32 hexadecimal characters). - KERNEL_INSTALL_MACHINE_ID= is set for the plugins to the desired MACHINE-ID – - either 32 hexadecimal characters or the special value Default. - KERNEL_INSTALL_BOOT_ROOT= is set for the plugins to the root directory (mount point, usually) of the hierarchy - where boot-loader entries, kernel images, and associated resources should be placed. Can be overridden by setting BOOT_ROOT=. + $KERNEL_INSTALL_MACHINE_ID is set for the plugins to the desired + MACHINE-ID to use. It's always a 128bit ID, and typically the ID from + /etc/machine-id or the one passed in via $MACHINE_ID. (If no + machine ID was specified via these methods it might be generated randomly by + kernel-install, in which case it only applies to this invocation.) - KERNEL_INSTALL_LAYOUT=bls|other|... specifies the installation layout. - Defaults to if $BOOT/MACHINE-ID exists, or otherwise. - Additional layout names may be defined by convention. If a plugin uses a special layout, - it's encouraged to declare its own layout name and configure layout= in install.conf upon initial installation. + $KERNEL_INSTALL_ENTRY_TOKEN is set for the plugins to the desired entry "token" + to use. It's an identifier that shall be used to identify the local installation, and is often the + machine ID, i.e. same as $KERNEL_INSTALL_MACHINE_ID, but might also be a different + type of identifier, for example a fixed string or the ID=, + IMAGE_ID= values from /etc/os-release. The string passed here + will be used to name Boot Loader Specification entries, or the directories the kernel image and initial + RAM disk images are placed into. Note that while oftentimes + $KERNEL_INSTALL_ENTRY_TOKEN and $KERNEL_INSTALL_MACHINE_ID are set + to the same value, the latter is guaranteed to be a valid 32 character ID in lowercase hexadecimals while + the former can be any short string. The entry token to use is read from + /etc/kernel/entry-token, if it exists. Otherwise a few possible candidates below the + $BOOT are searched for Boot Loader Specification Type 1 entry directories, and if + found the entry token is derived from that. If that is not successful the machine ID is used as + fallback. + + $KERNEL_INSTALL_BOOT_ROOT is set for the plugins to the absolute path of the + root directory (mount point, usually) of the hierarchy where boot loader entries, kernel images, and + associated resources should be placed. This usually is the path where the XBOOTLDR partition or the ESP + (EFI System Partition) are mounted, and also conceptually referred to as $BOOT. Can be + overridden by setting $BOOT_ROOT. + + $KERNEL_INSTALL_LAYOUT=bls|other|... is set for the plugins to specify the + installation layout. Defaults to if + $BOOT/ENTRY-TOKEN exists, or + otherwise. Additional layout names may be defined by convention. If a plugin uses a special layout, it's + encouraged to declare its own layout name and configure layout= in + install.conf upon initial installation. The following values are currently + understood: bls - Standard Boot Loader Specification layout, - compatible with systemd-boot7: entries in - $BOOT/loader/entries/MACHINE-ID-KERNEL-VERSION[+TRIES].conf, - kernel and initrds under $BOOT/MACHINE-ID/KERNEL-VERSION/ - Provided by 90-loaderentry.install. + Standard Boot Loader + Specification Type #1 layout, compatible with + systemd-boot7: + entries in + $BOOT/loader/entries/ENTRY-TOKEN-KERNEL-VERSION[+TRIES].conf, + kernel and initrds under + $BOOT/ENTRY-TOKEN/KERNEL-VERSION/ + Implemented by 90-loaderentry.install. @@ -194,6 +235,14 @@ + + $KERNEL_INSTALL_INITRD_GENERATOR is set for plugins to select the initrd + generator. This may be configured as initrd_generator= in + install.conf. See below. + + $KERNEL_INSTALL_STAGING_AREA is set for plugins to a path to a directory. + Plugins may drop files in that directory, and they will be installed as part of the loader entry, based + on the file name and extension. @@ -241,25 +290,21 @@ - /etc/machine-id + /etc/kernel/entry-token - The content of this file specifies the machine identification - MACHINE-ID. If /etc/machine-id - cannot be read or is temporary (backed by a file on tmpfs), - kernel-install will use Default instead. + If this file exists it is read and used as "entry token" for this system, i.e. is used for + naming Boot Loader Specification entries, see + $KERNEL_INSTALL_ENTRY_TOKEN above for details. - /etc/machine-info + /etc/machine-id - If this file contains the KERNEL_INSTALL_MACHINE_ID variable, - kernel-install will use it as MACHINE-ID instead of - the contents of /etc/machine-id. If the variable is not found in - /etc/machine-info, kernel-install will try to save the - machine ID it uses to install to $BOOT to this file. + The content of this file specifies the machine identification + MACHINE-ID. @@ -267,9 +312,9 @@ /etc/os-release /usr/lib/os-release - + Read by 90-loaderentry.install. - If available, PRETTY_NAME is read from these files and used as the title of the boot menu entry. + If available, PRETTY_NAME= is read from these files and used as the title of the boot menu entry. Otherwise, Linux KERNEL-VERSION will be used. @@ -279,10 +324,10 @@ /etc/kernel/install.conf - Configuration options for kernel-install, - as a series of KEY=VALUE assignments, - compatible with shell syntax. - See the Environment variables section for supported keys. + Configuration options for kernel-install, as a series of + KEY=VALUE assignments, compatible with shell + syntax. This currently supports two keys: layout= and + initrd_generator=, for details see the Environment variables section above. diff --git a/man/loader.conf.xml b/man/loader.conf.xml index 9fdd1e78d..d5abb1c04 100644 --- a/man/loader.conf.xml +++ b/man/loader.conf.xml @@ -23,6 +23,7 @@ ESP/loader/loader.conf, ESP/loader/entries/*.conf + XBOOTLDR/loader/entries/*.conf @@ -30,15 +31,19 @@ Description - systemd-boot7 - will read ESP/loader/loader.conf and any files with the + systemd-boot7 will + read ESP/loader/loader.conf, and any files with the .conf extension under - ESP/loader/entries/ on the EFI system partition (ESP). + ESP/loader/entries/ on the EFI system partition (ESP), + and XBOOTLDR/loader/entries/ on the extended boot loader + partition (XBOOTLDR) as defined by Boot Loader + Specification. - Each configuration file must consist of an option name, followed by - whitespace, and the option value. # may be used to start - a comment line. Empty and comment lines are ignored. + Each of these configuration files must consist of series of newline (i.e. ASCII code 10) separated + lines, each consisting of an option name, followed by whitespace, and the option + value. # may be used to start a comment line. Empty and comment lines are ignored. The + files use UTF-8 encoding. Boolean arguments may be written as yes/y/true/t/on/1 or @@ -49,7 +54,14 @@ Options - The following configuration options in loader.conf are understood: + The configuration options supported by + ESP/loader/entries/*.conf and + XBOOTLDR/loader/entries/*.conf files are defined as part + of the Boot Loader + Specification. + + The following configuration are supported by the loader.conf configuration + file: @@ -196,6 +208,38 @@ by using the f key. + + beep + + Beep n times when the n-th entry in the boot menu is shown (default disabled). + Currently, only x86 is supported, where it uses the PC speaker. + + + + reboot-for-bitlocker + + Caveat: This feature is experimental, and is likely to be changed (or removed in its + current form) in a future version of systemd. + + Work around BitLocker requiring a recovery key when the boot loader was + updated (disabled by default). + + Try to detect BitLocker encrypted drives along with an active TPM. If both are found + and Windows Boot Manager is selected in the boot menu, set the BootNext + EFI variable and restart the system. The firmware will then start Windows Boot Manager + directly, leaving the TPM PCRs in expected states so that Windows can unseal the encryption + key. This allows systemd-boot to be updated without having to provide the recovery key for + BitLocker drive unlocking. + + Note that the PCRs that Windows uses can be configured with the + Configure TPM platform validation profile for native UEFI firmware configurations + group policy under Computer Configuration\Administrative Templates\Windows Components\BitLocker Drive Encryption. + When secure boot is enabled, changing this to PCRs 0,2,7,11 should be safe. + The TPM key protector needs to be removed and then added back for the PCRs on an already + encrypted drive to change. If PCR 4 is not measured, this setting can be disabled to speed + up booting into Windows. + + random-seed-mode diff --git a/man/locale.conf.xml b/man/locale.conf.xml index b24ad9cf2..24be105d4 100644 --- a/man/locale.conf.xml +++ b/man/locale.conf.xml @@ -30,13 +30,13 @@ system-wide locale settings. It is read at early boot by systemd1. - The basic file format of locale.conf is - a newline-separated list of environment-like shell-compatible - variable assignments. It is possible to source the configuration - from shell scripts, however, beyond mere variable assignments, no - shell features are supported, allowing applications to read the - file without implementing a shell compatible execution - engine. + The format of locale.conf is a newline-separated list of environment-like + shell-compatible variable assignments, ignoring comments and empty lines. It is possible to source the + configuration from shell scripts, however, beyond mere variable assignments, no shell features are + supported, allowing applications to read the file without implementing a shell compatible execution + engine. See + os-release5 for a + detailed description of the format. Note that the kernel command line options locale.LANG=, @@ -58,20 +58,20 @@ The locale settings configured in /etc/locale.conf are system-wide and are inherited by every service or user, unless overridden or unset by - individual programs or individual users. + individual programs or users. Depending on the operating system, other configuration files might be checked for locale configuration as well, however only as fallback. - /etc/locale.conf is usually created and updated + /etc/locale.conf can be updated using systemd-localed.service8. localectl1 may be used to alter the settings in this file during runtime from the command line. Use systemd-firstboot1 - to initialize them on mounted (but not booted) system images. + to customize them on mounted (but not booted) system images. @@ -107,7 +107,9 @@ /etc/locale.conf: - LANG=de_DE.UTF-8 + # Custom settings + +LANG=de_DE.UTF-8 LC_MESSAGES=en_US.UTF-8 @@ -123,5 +125,4 @@ LC_MESSAGES=en_US.UTF-8 systemd-firstboot1 - diff --git a/man/machine-id.xml b/man/machine-id.xml index d5d3a1a29..9bd49582f 100644 --- a/man/machine-id.xml +++ b/man/machine-id.xml @@ -80,13 +80,13 @@ /etc/machine-id may also be written using any other means. - For operating system images which are created once and used on multiple - machines, for example for containers or in the cloud, - /etc/machine-id should be either missing or an empty file in the generic file - system image (the difference between the two options is described under "First Boot Semantics" below). An - ID will be generated during boot and saved to this file if possible. Having an empty file in place is - useful because it allows a temporary file to be bind-mounted over the real file, in case the image is - used read-only. + For operating system images which are created once and used on multiple machines, for example for + containers or in the cloud, /etc/machine-id should be either missing or an empty + file in the generic file system image (the difference between the two options is described under "First + Boot Semantics" below). An ID will be generated during boot and saved to this file if possible. Having an + empty file in place is useful because it allows a temporary file to be bind-mounted over the real file, + in case the image is used read-only. Also see Safely + Building Images. systemd-firstboot1 may be used to initialize /etc/machine-id on mounted (but not diff --git a/man/machine-info.xml b/man/machine-info.xml index d324856d0..4e6b72cea 100644 --- a/man/machine-info.xml +++ b/man/machine-info.xml @@ -29,13 +29,13 @@ The /etc/machine-info file contains machine metadata. - The basic file format of machine-info - is a newline-separated list of environment-like shell-compatible - variable assignments. It is possible to source the configuration - from shell scripts, however, beyond mere variable assignments no - shell features are supported, allowing applications to read the - file without implementing a shell compatible execution - engine. + The format of machine-info is a newline-separated list of environment-like + shell-compatible variable assignments, ignoring comments and empty lines. It is possible to source the + configuration from shell scripts, however, beyond mere variable assignments no shell features are + supported, allowing applications to read the file without implementing a shell compatible execution + engine. See + os-release5 for a + detailed description of the format. /etc/machine-info contains metadata about the machine that is set by the user or administrator. The settings configured here have the highest precedence. When not set, appropriate @@ -130,14 +130,19 @@ - KERNEL_INSTALL_MACHINE_ID= + HARDWARE_VENDOR= - Specifies the installation-specific installation directory - kernel-install should use. The value must be a valid machine ID (32 hexadecimal - characters). This would generally be the original machine-id that was used when the boot loader - entries for this installation were first added. When not set, the current value of - machine-id5 - will be used. + Specifies the hardware vendor. If unspecified, the hardware vendor set in DMI or + hwdb7 will be + used. + + + + HARDWARE_MODEL= + + Specifies the hardware model. If unspecified, the hardware model set in DMI or + hwdb7 will be + used. diff --git a/man/meson.build b/man/meson.build index a06a60176..83b368115 100644 --- a/man/meson.build +++ b/man/meson.build @@ -105,7 +105,7 @@ endforeach ############################################################ -have_lxml = run_command(xml_helper_py).returncode() == 0 +have_lxml = run_command(xml_helper_py, check: false).returncode() == 0 if not have_lxml message('python-lxml not available, not making man page indices') endif @@ -226,14 +226,14 @@ update_dbus_docs = custom_target( if conf.get('BUILD_MODE_DEVELOPER') == 1 test('dbus-docs-fresh', update_dbus_docs_py, - args : ['--build-dir', project_build_root, '--test', dbus_docs]) + args : ['--build-dir', project_build_root, '--test', dbus_docs], + depends : dbus_programs) endif update_man_rules = custom_target( 'update-man-rules', output : 'update-man-rules', - command : [sh, '-c', - 'cd @0@ && '.format(project_build_root) + - 'python3 @0@/tools/update-man-rules.py $(find @0@ -wholename "*/man/*.xml") >t && '.format(project_source_root) + - 'mv t @0@/rules/meson.build'.format(meson.current_source_dir())], + command : [update_man_rules_py, + '@0@/man/*.xml'.format(project_source_root), + '@0@/rules/meson.build'.format(meson.current_source_dir())], depends : custom_entities_ent) diff --git a/man/org.freedesktop.hostname1.xml b/man/org.freedesktop.hostname1.xml index 4f51cd5e8..94004002e 100644 --- a/man/org.freedesktop.hostname1.xml +++ b/man/org.freedesktop.hostname1.xml @@ -58,6 +58,7 @@ node /org/freedesktop/hostname1 { in b interactive); GetProductUUID(in b interactive, out ay uuid); + GetHardwareSerial(out s serial); Describe(out s json); properties: readonly s Hostname = '...'; @@ -93,6 +94,8 @@ node /org/freedesktop/hostname1 { }; + + @@ -119,6 +122,8 @@ node /org/freedesktop/hostname1 { + + diff --git a/man/org.freedesktop.portable1.xml b/man/org.freedesktop.portable1.xml index 8f960cc28..10f7c2136 100644 --- a/man/org.freedesktop.portable1.xml +++ b/man/org.freedesktop.portable1.xml @@ -58,6 +58,10 @@ node /org/freedesktop/portable1 { out a{say} units); GetImageState(in s image, out s state); + GetImageStateWithExtensions(in s image, + in as extensions, + in t flags, + out s state); AttachImage(in s image, in as matches, in s profile, @@ -133,6 +137,8 @@ node /org/freedesktop/portable1 { + + @@ -209,6 +215,12 @@ node /org/freedesktop/portable1 { running-runtime + GetImageStateWithExtensions() is a superset of + GetImageState(), with additional support for a list of extensions + as input parameters, which is necessary to query the state in case the image was attached + in that particular way. The flag parameter is currently unused and + reserved for future purposes. + AttachImage() attaches a portable image to the system. This method takes an image path or name, a list of strings that will be used to search for unit files inside the image (partial or complete matches), a string indicating which @@ -337,6 +349,9 @@ node /org/freedesktop/portable1 { out a{say} extensions, out a{say} units); GetState(out s state); + GetStateWithExtensions(in as extensions, + in t flags, + out s state); Attach(in as matches, in s profile, in b runtime, @@ -405,6 +420,8 @@ node /org/freedesktop/portable1 { + + @@ -437,6 +454,8 @@ node /org/freedesktop/portable1 { + + diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 8211e4219..d47d916b7 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -149,7 +149,6 @@ node /org/freedesktop/systemd1 { CancelJob(in u id); ClearJobs(); ResetFailed(); - @org.freedesktop.systemd1.Privileged("true") SetShowStatus(in s mode); ListUnits(out a(ssssssouso) units); ListUnitsFiltered(in as states, @@ -398,6 +397,12 @@ node /org/freedesktop/systemd1 { readwrite t RuntimeWatchdogUSec = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") @org.freedesktop.systemd1.Privileged("true") + readwrite t RuntimeWatchdogPreUSec = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + @org.freedesktop.systemd1.Privileged("true") + readwrite s RuntimeWatchdogPreGovernor = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + @org.freedesktop.systemd1.Privileged("true") readwrite t RebootWatchdogUSec = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") @org.freedesktop.systemd1.Privileged("true") @@ -646,6 +651,10 @@ node /org/freedesktop/systemd1 { + + + + @@ -1048,6 +1057,10 @@ node /org/freedesktop/systemd1 { + + + + @@ -2445,6 +2458,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly s ControlGroup = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t ControlGroupId = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryCurrent = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryAvailable = ...; @@ -2675,6 +2690,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s RootVerity = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ExtensionDirectories = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly a(sba(ss)) ExtensionImages = [...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly a(ssba(ss)) MountImages = [...]; @@ -3014,6 +3031,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -3590,6 +3609,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -3820,6 +3841,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + @@ -4178,6 +4201,7 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { RootHashSignature MountImages ExtensionImages + ExtensionDirectories see systemd.exec(5) for their meaning. MemoryAvailable indicates how much unused memory is available to the unit before @@ -4322,6 +4346,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly s ControlGroup = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t ControlGroupId = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryCurrent = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryAvailable = ...; @@ -4552,6 +4578,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s RootVerity = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ExtensionDirectories = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly a(sba(ss)) ExtensionImages = [...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly a(ssba(ss)) MountImages = [...]; @@ -4915,6 +4943,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -5485,6 +5515,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -5715,6 +5747,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + @@ -6107,6 +6141,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly s ControlGroup = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t ControlGroupId = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryCurrent = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryAvailable = ...; @@ -6337,6 +6373,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s RootVerity = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ExtensionDirectories = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly a(sba(ss)) ExtensionImages = [...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly a(ssba(ss)) MountImages = [...]; @@ -6628,6 +6666,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + @@ -7116,6 +7156,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + @@ -7346,6 +7388,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + @@ -7865,6 +7909,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly s ControlGroup = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t ControlGroupId = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryCurrent = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryAvailable = ...; @@ -8095,6 +8141,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly s RootVerity = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as ExtensionDirectories = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly a(sba(ss)) ExtensionImages = [...]; @org.freedesktop.DBus.Property.EmitsChangedSignal("const") readonly a(ssba(ss)) MountImages = [...]; @@ -8372,6 +8420,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + @@ -8846,6 +8896,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + @@ -9076,6 +9128,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + @@ -9454,6 +9508,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice { @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly s ControlGroup = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t ControlGroupId = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryCurrent = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryAvailable = ...; @@ -9605,6 +9661,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice { + + @@ -9759,6 +9817,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice { + + @@ -9937,6 +9997,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope { @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly s ControlGroup = '...'; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly t ControlGroupId = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryCurrent = ...; @org.freedesktop.DBus.Property.EmitsChangedSignal("false") readonly t MemoryAvailable = ...; @@ -10106,6 +10168,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope { + + @@ -10288,6 +10352,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope { + + diff --git a/man/os-release.xml b/man/os-release.xml index bdd7bbb64..7e3e9a73a 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -35,7 +35,7 @@ /usr/lib/os-release files contain operating system identification data. - The basic file format of os-release is a newline-separated list of + The format of os-release is a newline-separated list of environment-like shell-compatible variable assignments. It is possible to source the configuration from Bourne shell scripts, however, beyond mere variable assignments, no shell features are supported (this means variable expansion is explicitly not supported), allowing applications to read the file without @@ -44,8 +44,8 @@ 0–9. (Assignments that do not include these special characters may be enclosed in quotes too, but this is optional.) Shell special characters ("$", quotes, backslash, backtick) must be escaped with backslashes, following shell style. All strings should be in UTF-8 encoding, and non-printable characters should not - be used. It is not supported to concatenate multiple individually quoted strings. Lines beginning with - "#" shall be ignored as comments. Blank lines are permitted and ignored. + be used. Concatenation of multiple individually quoted strings is not supported. Lines beginning with "#" + are treated as comments. Blank lines are permitted and ignored. The file /etc/os-release takes precedence over /usr/lib/os-release. @@ -75,6 +75,10 @@ from earliest boot on, and hence must be located on the root file system. + os-release must not contain repeating keys. Nevertheless, readers should pick + the entries later in the file in case of repeats, similarly to how a shell sourcing the file would. A + reader may warn about repeating entries. + For a longer rationale for os-release please refer to the Announcement of /etc/os-release. diff --git a/man/portablectl.xml b/man/portablectl.xml index c5404db0b..719ea81ae 100644 --- a/man/portablectl.xml +++ b/man/portablectl.xml @@ -47,7 +47,8 @@ Portable service images are an efficient way to bundle multiple related services and other units together, and transfer them as a whole between systems. When these images are attached the local system the contained units may run in most ways like regular system-provided units, either with full privileges or inside strict sandboxing, - depending on the selected configuration. + depending on the selected configuration. For more details, see + Portable Services. Specifically portable service images may be of the following kind: @@ -359,10 +360,14 @@ top of IMAGE when attaching/detaching. This argument can be specified multiple times, in which case the order in which images are laid down follows the rules specified in systemd.exec5 - for the ExtensionImages= directive. The image(s) must contain an - extension-release file with metadata that matches what is defined in the - os-release of IMAGE. See: + for the ExtensionImages= directive and for the + systemd-sysext8 tool. + The image(s) must contain an extension-release file with metadata that matches + what is defined in the os-release of IMAGE. See: os-release5. + Images can be block images, btrfs subvolumes or directories. For more information on portable + services with extensions, see the Extension Images paragraph on + Portable Services. Note that the same extensions have to be specified, in the same order, when attaching @@ -451,6 +456,7 @@ See Also systemd1, + systemd-sysext8, org.freedesktop.portable15, systemd-portabled.service8 diff --git a/man/rules/meson.build b/man/rules/meson.build index 26ac25dbd..aaa69d303 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -642,7 +642,12 @@ manpages = [ ['sd_id128_randomize', '3', [], ''], ['sd_id128_to_string', '3', - ['SD_ID128_STRING_MAX', 'SD_ID128_TO_STRING', 'sd_id128_from_string'], + ['SD_ID128_STRING_MAX', + 'SD_ID128_TO_STRING', + 'SD_ID128_TO_UUID_STRING', + 'SD_ID128_UUID_STRING_MAX', + 'sd_id128_from_string', + 'sd_id128_to_uuid_string'], ''], ['sd_is_fifo', '3', @@ -933,7 +938,7 @@ manpages = [ ['systemd-network-generator.service', '8', ['systemd-network-generator'], ''], ['systemd-networkd-wait-online.service', '8', - ['systemd-networkd-wait-online'], + ['systemd-networkd-wait-online', 'systemd-networkd-wait-online@.service'], 'ENABLE_NETWORKD'], ['systemd-networkd.service', '8', ['systemd-networkd'], 'ENABLE_NETWORKD'], ['systemd-notify', '1', [], ''], @@ -966,7 +971,7 @@ manpages = [ ['systemd-stdio-bridge', '1', [], ''], ['systemd-stub', '7', - ['linuxaa64.efi.stub', 'linuxia32.efi.stub', 'linuxx64.efi.stub'], + ['linuxaa64.efi.stub', 'linuxia32.efi.stub', 'linuxx64.efi.stub', 'sd-stub'], 'HAVE_GNU_EFI'], ['systemd-suspend.service', '8', @@ -982,6 +987,13 @@ manpages = [ '5', ['system.conf.d', 'systemd-user.conf', 'user.conf.d'], ''], + ['systemd-sysupdate', + '8', + ['systemd-sysupdate-reboot.service', + 'systemd-sysupdate-reboot.timer', + 'systemd-sysupdate.service', + 'systemd-sysupdate.timer'], + 'ENABLE_SYSUPDATE'], ['systemd-sysusers', '8', ['systemd-sysusers.service'], ''], ['systemd-sysv-generator', '8', [], 'HAVE_SYSV_COMPAT'], ['systemd-time-wait-sync.service', @@ -1053,6 +1065,7 @@ manpages = [ ['systemd.time', '7', [], ''], ['systemd.timer', '5', [], ''], ['systemd.unit', '5', [], ''], + ['sysupdate.d', '5', [], 'ENABLE_SYSUPDATE'], ['sysusers.d', '5', [], 'ENABLE_SYSUSERS'], ['telinit', '8', [], 'HAVE_SYSV_COMPAT'], ['timedatectl', '1', [], 'ENABLE_TIMEDATECTL'], diff --git a/man/sd_bus_add_object.xml b/man/sd_bus_add_object.xml index 3249b7f98..991f3a806 100644 --- a/man/sd_bus_add_object.xml +++ b/man/sd_bus_add_object.xml @@ -561,7 +561,7 @@ SD_BUS_VTABLE_CAPABILITY(capability) - Access to this vtable entry will be allowed if the calling proccess has the + Access to this vtable entry will be allowed if the calling process has the capability capability, as described in sd_bus_query_sender_privilege3. If used for SD_BUS_VTABLE_START(), provides a default for all entries in the diff --git a/man/sd_event_add_child.xml b/man/sd_event_add_child.xml index 2961b3ee5..fec754d2d 100644 --- a/man/sd_event_add_child.xml +++ b/man/sd_event_add_child.xml @@ -114,25 +114,29 @@ event loop. The event loop object is specified in the event parameter, the event source object is returned in the source parameter. The pid parameter specifies the PID of the process to watch, which must be a direct child process of the invoking - process. The handler must reference a function to call when the process changes - state. The handler function will be passed the userdata pointer, which may be - chosen freely by the caller. The handler also receives a pointer to a siginfo_t - structure containing information about the child process event. The options - parameter determines which state changes will be watched for. It must contain an OR-ed mask of - WEXITED (watch for the child process terminating), WSTOPPED - (watch for the child process being stopped by a signal), and WCONTINUED (watch for - the child process being resumed by a signal). See waitid2 for - further information. + process. The options parameter determines which state changes will be watched for. + It must contain an OR-ed mask of WEXITED (watch for the child process terminating), + WSTOPPED (watch for the child process being stopped by a signal), and + WCONTINUED (watch for the child process being resumed by a signal). See + waitid2 + for further information. - Only a single handler may be installed for a specific - child process. The handler is enabled for a single event - (SD_EVENT_ONESHOT), but this may be changed - with + The handler must be a function to call when the process changes state or + NULL. The handler function will be passed the userdata + pointer, which may be chosen freely by the caller. The handler also receives a pointer to a + siginfo_t structure containing information about the child process event. The + handler may return negative to signal an error (see below), other return values are ignored. If + handler is NULL, a default handler that calls + sd_event_exit3 will be + used. + + Only a single handler may be installed for a specific child process. The handler is enabled for a + single event (SD_EVENT_ONESHOT), but this may be changed with sd_event_source_set_enabled3. - If the handler function returns a negative error code, it will be - disabled after the invocation, even if the - SD_EVENT_ON mode was requested before. + If the handler function returns a negative error code, it will either be disabled after the invocation, + even if the SD_EVENT_ON mode was requested before, or it will cause the loop to + terminate, see + sd_event_source_set_exit_on_failure3. To destroy an event source object use @@ -307,6 +311,16 @@ + + Example + + + Exit loop when the child terminates + + + + + See Also diff --git a/man/sd_event_add_defer.xml b/man/sd_event_add_defer.xml index 54e882300..e56e16a03 100644 --- a/man/sd_event_add_defer.xml +++ b/man/sd_event_add_defer.xml @@ -66,14 +66,17 @@ Description - These three functions add new static event sources to an - event loop. The event loop object is specified in the - event parameter, the event source object is - returned in the source parameter. The event - sources are enabled statically and will "fire" when the event loop - is run and the conditions described below are met. The handler - function will be passed the userdata - pointer, which may be chosen freely by the caller. + These three functions add new static event sources to an event loop. The event loop object is + specified in the event parameter, the event source object is returned in the + source parameter. The event sources are enabled statically and will "fire" when + the event loop is run and the conditions described below are met. + + The handler is a function to call or NULL. The handler + function will be passed the userdata pointer, which may be chosen freely by the + caller. The handler may return negative to signal an error (see below), other return values are + ignored. If handler is NULL, a default handler that calls + sd_event_exit3 will be + used. sd_event_add_defer() adds a new event source that will be dispatched instantly, before the event loop @@ -103,9 +106,11 @@ (SD_EVENT_ON) or to make it fire just once (SD_EVENT_ONESHOT). - If the handler function returns a negative error code, it - will be disabled after the invocation, even if the - SD_EVENT_ON mode was requested before. + If the handler function returns a negative error code, it will either be disabled after the + invocation, even if the SD_EVENT_ON mode was requested before, or it will cause the + loop to terminate, see + sd_event_source_set_exit_on_failure3. + To destroy an event source object use sd_event_source_unref3, diff --git a/man/sd_event_add_inotify.xml b/man/sd_event_add_inotify.xml index d632bf728..c7af7bdcd 100644 --- a/man/sd_event_add_inotify.xml +++ b/man/sd_event_add_inotify.xml @@ -70,33 +70,40 @@ Description sd_event_add_inotify() adds a new inotify7 file system inode - event source to an event loop. The event loop object is specified in the event parameter, - the event source object is returned in the source parameter. The path - parameter specifies the path of the file system inode to watch. The handler must reference a - function to call when the inode changes. The handler function will be passed the userdata - pointer, which may be chosen freely by the caller. The handler also receives a pointer to a struct - inotify_event structure containing information about the inode event. The mask - parameter specifies which types of inode events to watch specifically. It must contain an OR-ed combination of - IN_ACCESS, IN_ATTRIB, IN_CLOSE_WRITE, … flags. See - inotify7 for + project='man-pages'>inotify7 file + system inode event source to an event loop. The event loop object is specified in the + event parameter, the event source object is returned in the + source parameter. The path parameter specifies the path of + the file system inode to watch. The mask parameter specifies which types of inode + events to watch specifically. It must contain an OR-ed combination of IN_ACCESS, + IN_ATTRIB, IN_CLOSE_WRITE, … flags. See inotify7 for further information. + The handler must reference a function to call when the inode changes or + NULL. The handler function will be passed the userdata pointer, + which may be chosen freely by the caller. The handler also receives a pointer to a struct + inotify_event structure containing information about the inode event. The handler may return + negative to signal an error (see below), other return values are ignored. If + handler is NULL, a default handler that calls + sd_event_exit3 will be + used. + sd_event_add_inotify_fd() is identical to sd_event_add_inotify(), except that it takes a file descriptor to an inode (possibly - an O_PATH one, but any other will do too) instead of a path in the file - system. + an O_PATH one, but any other will do too) instead of a path in the file system. + If multiple event sources are installed for the same inode the backing inotify watch descriptor is automatically shared. The mask parameter may contain any flag defined by the inotify API, with the exception of IN_MASK_ADD. The handler is enabled continuously (SD_EVENT_ON), but this may be changed with - sd_event_source_set_enabled3. Alternatively, - the IN_ONESHOT mask flag may be used to request SD_EVENT_ONESHOT mode. - If the handler function returns a negative error code, it will be disabled after the invocation, even if the - SD_EVENT_ON mode was requested before. - + sd_event_source_set_enabled3. + Alternatively, the IN_ONESHOT mask flag may be used to request + SD_EVENT_ONESHOT mode. If the handler function returns a negative error code, it + will be disabled after the invocation, even if the SD_EVENT_ON mode was requested + before. As a special limitation the priority of inotify event sources may only be altered (see sd_event_source_set_priority3) diff --git a/man/sd_event_add_io.xml b/man/sd_event_add_io.xml index 323e57c79..383a58a07 100644 --- a/man/sd_event_add_io.xml +++ b/man/sd_event_add_io.xml @@ -115,27 +115,27 @@ EPOLLRDHUP, EPOLLPRI, and EPOLLET, see epoll_ctl2 - for details. The handler shall reference a - function to call when the event source is triggered. The - userdata pointer will be passed to the - handler function, and may be chosen freely by the caller. The - handler will also be passed the file descriptor the event was seen - on, as well as the actual event flags. It's generally a subset of - the events watched, however may additionally include - EPOLLERR and EPOLLHUP. - + for details. - By default, an event source will stay enabled - continuously (SD_EVENT_ON), but this may be - changed with + The handler is a function to call when the event source is triggered or + NULL. The userdata pointer will be passed to the handler + function, and may be chosen freely by the caller. The handler will also be passed the file descriptor the + event was seen on, as well as the actual event flags. It's generally a subset of the events watched, + however may additionally include EPOLLERR and EPOLLHUP. The + handler may return negative to signal an error (see below), other return values are ignored. If + handler is NULL, a default handler that calls + sd_event_exit3 will be + used. + + By default, an event source will stay enabled continuously (SD_EVENT_ON), but + this may be changed with sd_event_source_set_enabled3. - If the handler function returns a negative error code, it will be - disabled after the invocation, even if the - SD_EVENT_ON mode was requested before. Note - that an event source set to SD_EVENT_ON will - fire continuously unless data is read from or written to the file - descriptor to reset the mask of events seen. - + If the handler function returns a negative error code, it will either be disabled after the invocation, + even if the SD_EVENT_ON mode was requested before, or it will cause the loop to + terminate, see + sd_event_source_set_exit_on_failure3. + Note that an event source set to SD_EVENT_ON will fire continuously unless data is + read from or written to the file descriptor to reset the mask of events seen. Setting the I/O event mask to watch for to 0 does not mean that the event source won't be triggered anymore, as diff --git a/man/sd_event_add_signal.xml b/man/sd_event_add_signal.xml index 1f0854f6c..b2aaff87c 100644 --- a/man/sd_event_add_signal.xml +++ b/man/sd_event_add_signal.xml @@ -64,16 +64,18 @@ source parameter. The signal parameter specifies the numeric signal to be handled (see signal7). - The handler parameter must reference a - function to call when the signal is received or be - NULL. The handler function will be passed - the userdata pointer, which may be chosen - freely by the caller. The handler also receives a pointer to a - signalfd_siginfo structure containing - information about the received signal. See signalfd2 - for further information. + project='man-pages'>signal7). + + The handler parameter is a function to call when the signal is received or + NULL. The handler function will be passed the userdata + pointer, which may be chosen freely by the caller. The handler also receives a pointer to a + signalfd_siginfo structure containing information about the received signal. See + signalfd2 + for further information. The handler may return negative to signal an error (see below), other return + values are ignored. If handler is NULL, a default handler + that calls + sd_event_exit3 will be + used. Only a single handler may be installed for a specific signal. The signal must be blocked in all threads before this function is called (using By default, the event source is enabled permanently (SD_EVENT_ON), but this may be changed with sd_event_source_set_enabled3. - If the handler function returns a negative error code, it will be - disabled after the invocation, even if the - SD_EVENT_ON mode was requested before. + If the handler function returns a negative error code, it will either be disabled after the + invocation, even if the SD_EVENT_ON mode was requested before, or it will cause the + loop to terminate, see + sd_event_source_set_exit_on_failure3. To destroy an event source object use diff --git a/man/sd_event_add_time.xml b/man/sd_event_add_time.xml index 3e8927f96..19f112b0f 100644 --- a/man/sd_event_add_time.xml +++ b/man/sd_event_add_time.xml @@ -122,25 +122,30 @@ timer event may be delayed. Use 0 to select the default accuracy (250ms). Use 1µs for maximum accuracy. Consider specifying 60000000µs (1min) or larger for long-running events that may be delayed substantially. Picking higher accuracy values allows the system to coalesce timer events more aggressively, - improving power efficiency. The handler parameter shall reference a function to call when - the timer elapses. The handler function will be passed the userdata pointer, which may be - chosen freely by the caller. The handler is also passed the configured trigger time, even if it is actually called - slightly later, subject to the specified accuracy value, the kernel timer slack (see - prctl2), and additional - scheduling latencies. To query the actual time the handler was called use - sd_event_now3. + improving power efficiency. - By default, the timer will elapse once - (SD_EVENT_ONESHOT), but this may be changed - with + The handler is a function to call when the timer elapses or + NULL. The userdata pointer will be passed to the handler + function, and may be chosen freely by the caller. The configured trigger time is also passed to the + handler, even if the call actually happens slightly later, subject to the specified accuracy value, the + kernel timer slack (see + prctl2), and + additional scheduling latencies. To query the actual time the handler was called use + sd_event_now3. The + handler may return negative to signal an error (see below), other return values are ignored. If + handler is NULL, a default handler that calls + sd_event_exit3 will be + used. + + By default, the timer will elapse once (SD_EVENT_ONESHOT), but this may be + changed with sd_event_source_set_enabled3. - If the handler function returns a negative error code, it will be - disabled after the invocation, even if the - SD_EVENT_ON mode was requested before. Note - that a timer event set to SD_EVENT_ON will - fire continuously unless its configured time is updated using - sd_event_source_set_time(). - + If the handler function returns a negative error code, it will either be disabled after the invocation, + even if the SD_EVENT_ON mode was requested before, or it will cause the loop to + terminate, see + sd_event_source_set_exit_on_failure3. + Note that a timer event set to SD_EVENT_ON will fire continuously unless its + configured time is updated using sd_event_source_set_time(). sd_event_add_time_relative() is like sd_event_add_time(), but takes a relative time specification. It's relative to the current time of the event loop iteration, diff --git a/man/sd_id128_to_string.xml b/man/sd_id128_to_string.xml index db64bc018..e0338bae8 100644 --- a/man/sd_id128_to_string.xml +++ b/man/sd_id128_to_string.xml @@ -18,8 +18,11 @@ sd_id128_to_string SD_ID128_TO_STRING - sd_id128_from_string SD_ID128_STRING_MAX + sd_id128_to_uuid_string + SD_ID128_TO_UUID_STRING + SD_ID128_UUID_STRING_MAX + sd_id128_from_string Format or parse 128-bit IDs as strings @@ -29,13 +32,22 @@ #define SD_ID128_STRING_MAX 33U + #define SD_ID128_UUID_STRING_MAX 37U + #define SD_ID128_TO_STRING(id) … + #define SD_ID128_TO_UUID_STRING(id) … + char *sd_id128_to_string sd_id128_t id, char s[static SD_ID128_STRING_MAX] + + char *sd_id128_uuid_string + sd_id128_t id, char s[static SD_ID128_UUID_STRING_MAX] + + int sd_id128_from_string const char *s, sd_id128_t *ret @@ -58,6 +70,10 @@ which remains valid until the end of the current code block. This is usually the simplest way to acquire a string representation of a 128-bit ID in a buffer that is valid in the current code block. + sd_id128_to_uuid_string() and SD_ID128_TO_UUID_STRING() + are similar to these two functions/macros, but format the 128bit values as RFC4122 UUIDs, i.e. a series + of 36 lowercase hexadeciaml digits and dashes, terminated by a NUL byte. + sd_id128_from_string() implements the reverse operation: it takes a 33 character string with 32 hexadecimal digits (either lowercase or uppercase, terminated by NUL) and parses them back into a 128-bit ID returned in @@ -65,7 +81,7 @@ ID formatted as RFC UUID. If ret is passed as NULL the function will validate the passed ID string, but not actually return it in parsed form. - Note that when parsing 37 character UUIDs this is done strictly in Big Endian byte order, + Note that when formatting and parsing 36 character UUIDs this is done strictly in Big Endian byte order, i.e. according to RFC4122 Variant 1 rules, even if the UUID encodes a different variant. This matches behaviour in various other Linux userspace tools. It's probably wise to avoid UUIDs of other variant types. diff --git a/man/sd_journal_print.xml b/man/sd_journal_print.xml index f7a70e9b0..503bf9ec3 100644 --- a/man/sd_journal_print.xml +++ b/man/sd_journal_print.xml @@ -68,10 +68,10 @@ int sd_journal_print_with_location + int priority const char *file const char *line const char *func - int priority const char *format diff --git a/man/standard-specifiers.xml b/man/standard-specifiers.xml index f1666365b..abbd47f77 100644 --- a/man/standard-specifiers.xml +++ b/man/standard-specifiers.xml @@ -34,6 +34,11 @@ Short host name The hostname of the running system, truncated at the first dot to remove any domain component. + + %R + Pretty host name + The pretty hostname of the running system, as read from the PRETTY_HOSTNAME= field of /etc/machine-info. If not set, resolves to the short hostname. See machine-info5 for more information. + %m Machine ID diff --git a/man/system-or-user-ns.xml b/man/system-or-user-ns.xml new file mode 100644 index 000000000..01d1dd022 --- /dev/null +++ b/man/system-or-user-ns.xml @@ -0,0 +1,16 @@ + + + + + + + +This option is only available for system services, or for services running in per-user + instances of the service manager when unprivileged user namespaces are available. + +These options are only available for system services, or for services running in per-user + instances of the service manager when unprivileged user namespaces are available. + + diff --git a/man/systemctl.xml b/man/systemctl.xml index 1c1490952..d87c52685 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1145,7 +1145,7 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err Dump the systemd manager environment block. This is the environment block that is passed to all processes the manager spawns. The environment - block will be dumped in straight-forward form suitable for sourcing into + block will be dumped in straightforward form suitable for sourcing into most shells. If no special characters or whitespace is present in the variable values, no escaping is performed, and the assignments have the form VARIABLE=value. If whitespace or characters which have @@ -2305,6 +2305,13 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err + + + + @seconds-since-the-epoch + + + diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index ceea368ef..393c0312f 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -30,32 +30,31 @@ systemd-boot loads boot entry information from the EFI system partition (ESP), usually mounted at /efi/, /boot/, or - /boot/efi/ during OS runtime, as well as from the Extended Boot Loader partition if - it exists (usually mounted to /boot/). Configuration file fragments, kernels, - initrds and other EFI images to boot generally need to reside on the ESP or the Extended Boot Loader - partition. Linux kernels must be built with to be able to be directly - executed as an EFI image. During boot systemd-boot automatically assembles a list of - boot entries from the following sources: + /boot/efi/ during OS runtime, as well as from the Extended Boot Loader partition + (XBOOTLDR) if it exists (usually mounted to /boot/). Configuration file fragments, + kernels, initrds and other EFI images to boot generally need to reside on the ESP or the Extended Boot + Loader partition. Linux kernels must be built with to be able to be + directly executed as an EFI image. During boot systemd-boot automatically assembles a + list of boot entries from the following sources: Boot entries defined with Boot Loader Specification description files - located in /loader/entries/ on the ESP and the Extended Boot Loader - Partition. These usually describe Linux kernel images with associated initrd images, but alternatively - may also describe arbitrary other EFI executables. + url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification Type #1 + description files located in /loader/entries/ on the ESP and the Extended Boot + Loader Partition. These usually describe Linux kernel images with associated initrd images, but + alternatively may also describe other arbitrary EFI executables. - Unified kernel images following the Boot Loader Specification, as executable EFI - binaries in /EFI/Linux/ on the ESP and the Extended Boot Loader Partition. - + Unified kernel images, Boot + Loader Specification Type #2, which are executable EFI binaries in + /EFI/Linux/ on the ESP and the Extended Boot Loader Partition. - The Microsoft Windows EFI boot manager, if installed + The Microsoft Windows EFI boot manager, if installed. - The Apple macOS boot manager, if installed + The Apple macOS boot manager, if installed. - The EFI Shell binary, if installed + The EFI Shell binary, if installed. - A reboot into the UEFI firmware setup option, if supported by the firmware + A reboot into the UEFI firmware setup option, if supported by the firmware. systemd-boot supports the following features: diff --git a/man/systemd-creds.xml b/man/systemd-creds.xml index 73999f425..d3c0295d6 100644 --- a/man/systemd-creds.xml +++ b/man/systemd-creds.xml @@ -240,7 +240,7 @@ When specified with the encrypt command controls the timestamp to embed into the encrypted credential. Defaults to the current time. Takes a timestamp specification in the format described in - systemd.time5. + systemd.time7. When specified with the decrypt command controls the timestamp to use to validate the "not-after" timestamp that was configured with during @@ -255,7 +255,7 @@ credential. During decryption the timestamp is checked against the current system clock, and if the timestamp is in the past the decryption will fail. By default no such timestamp is set. Takes a timestamp specification in the format described in - systemd.time5. + systemd.time7. @@ -275,7 +275,7 @@ media, encryption will fail. The switch is a shortcut for . Similar, - is a shortcut for . + is a shortcut for . When encrypting credentials that shall be used in the initial RAM disk (initrd) where /var/lib/systemd/ is typically not available make sure to use diff --git a/man/systemd-cryptenroll.xml b/man/systemd-cryptenroll.xml index d5fdb54cd..02f932edf 100644 --- a/man/systemd-cryptenroll.xml +++ b/man/systemd-cryptenroll.xml @@ -214,6 +214,8 @@ + + @@ -267,14 +269,14 @@ Secure boot state; changes when UEFI SecureBoot mode is enabled/disabled, or firmware certificates (PK, KEK, db, dbx, …) changes. The shim project will measure most of its (non-MOK) certificates and SBAT data into this PCR. - - 8 - sd-boot7 measures the kernel command line into this PCR. - - - + + + 12 + sd-boot7 measures the kernel command line into this PCR. + + 10 The IMA project measures its runtime state into this PCR. @@ -299,6 +301,24 @@ signatures likely will validate against pre-existing certificates. + + BOOL + + When enrolling a TPM2 device, controls whether to require the user to enter a PIN + when unlocking the volume in addition to PCR binding, based on TPM2 policy authentication. Defaults + to no. Despite being called PIN, any character can be used, not just numbers. + + + Note that incorrect PIN entry when unlocking increments the + TPM dictionary attack lockout mechanism, and may lock out users for a prolonged time, depending on + its configuration. The lockout mechanism is a global property of the TPM, + systemd-cryptenroll does not control or configure the lockout mechanism. You may + use tpm2-tss tools to inspect or configure the dictionary attack lockout, with + tpm2_getcap1 and + tpm2_dictionarylockout1 + commands, respectively. + + SLOT diff --git a/man/systemd-debug-generator.xml b/man/systemd-debug-generator.xml index 531209bec..81d1efb44 100644 --- a/man/systemd-debug-generator.xml +++ b/man/systemd-debug-generator.xml @@ -37,7 +37,7 @@ If the or option is specified and followed by a unit name, this unit is - masked for the runtime, similar to the effect of + masked for the runtime (i.e. for this session - from boot to shutdown), similar to the effect of systemctl1's mask command. This is useful to boot with certain units removed from the initial boot transaction for diff --git a/man/systemd-detect-virt.xml b/man/systemd-detect-virt.xml index 14bfd19b6..8ac4de404 100644 --- a/man/systemd-detect-virt.xml +++ b/man/systemd-detect-virt.xml @@ -62,7 +62,7 @@ - VM + VM qemu QEMU software virtualization, without KVM diff --git a/man/systemd-journal-upload.service.xml b/man/systemd-journal-upload.service.xml index e2b39bf96..ce9bbdf15 100644 --- a/man/systemd-journal-upload.service.xml +++ b/man/systemd-journal-upload.service.xml @@ -45,7 +45,10 @@ Unless limited by one of the options specified below, all journal entries accessible to the user the program is running as will be uploaded, and then the program will wait and send new entries as they become available. - + + systemd-journal-upload transfers the raw content of journal file and + uses HTTP as a transport protocol. + systemd-journal-upload.service is a system service that uses systemd-journal-upload to upload journal entries to a server. It uses the configuration in diff --git a/man/systemd-networkd-wait-online.service.xml b/man/systemd-networkd-wait-online.service.xml index 9c6b02ac1..a3a70db20 100644 --- a/man/systemd-networkd-wait-online.service.xml +++ b/man/systemd-networkd-wait-online.service.xml @@ -18,12 +18,14 @@ systemd-networkd-wait-online.service + systemd-networkd-wait-online@.service systemd-networkd-wait-online Wait for network to come online systemd-networkd-wait-online.service + systemd-networkd-wait-online@.service /usr/lib/systemd/systemd-networkd-wait-online @@ -38,6 +40,17 @@ to be fully configured or failed, and for at least one link to be online. Here, online means that the link's operational state is equal or higher than degraded. The threshold can be configured by option. + + The service systemd-networkd-wait-online.service invokes + systemd-networkd-wait-online without any options. Thus, it waits for all managed + interfaces to be configured or failed, and for at least one to be online. + + The service systemd-networkd-wait-online@.service takes an interface + name, and invokes systemd-networkd-wait-online with and the + specified interface name. Thus, wait for the specified interface to be configured and online. For + example, systemd-networkd-wait-online@eth0.service waits for + eth0 to be configured by systemd-networkd and online. + diff --git a/man/systemd-random-seed.service.xml b/man/systemd-random-seed.service.xml index 3137ed0c8..0a50b51e4 100644 --- a/man/systemd-random-seed.service.xml +++ b/man/systemd-random-seed.service.xml @@ -57,7 +57,8 @@ thus — if otherwise entropy-starved — generate the same or at least guessable random seed streams. As a safety precaution crediting entropy is thus disabled by default. It is recommended to remove the random seed from OS images intended for replication on multiple systems, in which case it is safe to enable - entropy crediting, see below. + entropy crediting, see below. Also see Safely Building + Images. See Random Seeds for further information. diff --git a/man/systemd-stdio-bridge.xml b/man/systemd-stdio-bridge.xml index 002a91b12..0dce5659d 100644 --- a/man/systemd-stdio-bridge.xml +++ b/man/systemd-stdio-bridge.xml @@ -31,12 +31,11 @@ Description - systemd-stdio-bridge implements a proxy for a D-Bus endpoint. It expects to - receive an open connection to a bus when started, and will also connect to a (different) bus as a - client. It will then act as a server on the first connection, and forward messages between the two - busses. This program is suitable for socket activation: the first connection may be a pipe or a socket - and must be passed as either standard input, or as an open file descriptor according to the protocol - described in + systemd-stdio-bridge implements a proxy between STDIN/STDOUT and a D-Bus bus. It + expects to receive an open connection via STDIN/STDOUT when started, and will create a new connection to + the specified bus. It will then forward messages between the two connections. This program is suitable + for socket activation: the first connection may be a pipe or a socket and must be passed as either + standard input, or as an open file descriptor according to the protocol described in sd_listen_fds3. The second connection will be made by default to the local system bus, but this can be influenced by the , , , and diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml index 28c6ba938..f2d3b91e2 100644 --- a/man/systemd-stub.xml +++ b/man/systemd-stub.xml @@ -17,6 +17,7 @@ systemd-stub + sd-stub linuxx64.efi.stub linuxia32.efi.stub linuxaa64.efi.stub diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml index 3805a010e..b104044cc 100644 --- a/man/systemd-system.conf.xml +++ b/man/systemd-system.conf.xml @@ -177,6 +177,50 @@ These settings have no effect if a hardware watchdog is not available. + + RuntimeWatchdogPreSec= + + Configure the hardware watchdog device pre-timeout value. + Takes a timeout value in seconds (or in other time units similar to + RuntimeWatchdogSec=). A watchdog pre-timeout is a + notification generated by the watchdog before the watchdog reset might + occur in the event the watchdog has not been serviced. This notification + is handled by the kernel and can be configured to take an action (i.e. + generate a kernel panic) using RuntimeWatchdogPreGovernor=. + Not all watchdog hardware or drivers support generating a pre-timeout and + depending on the state of the system, the kernel may be unable to take the + configured action before the watchdog reboot. The watchdog will be configured + to generate the pre-timeout event at the amount of time specified by + RuntimeWatchdogPreSec= before the runtime watchdog timeout + (set by RuntimeWatchdogSec=). For example, if the we have + RuntimeWatchdogSec=30 and + RuntimeWatchdogPreSec=10, then the pre-timeout event + will occur if the watchdog has not pinged for 20s (10s before the + watchdog would fire). By default, RuntimeWatchdogPreSec= + defaults to 0 (off). The value set for RuntimeWatchdogPreSec= + must be smaller than the timeout value for RuntimeWatchdogSec=. + This setting has no effect if a hardware watchdog is not available or the + hardware watchdog does not support a pre-timeout and will be ignored by the + kernel if the setting is greater than the actual watchdog timeout. + + + + RuntimeWatchdogPreGovernor= + + Configure the action taken by the hardware watchdog device + when the pre-timeout expires. The default action for the pre-timeout event + depends on the kernel configuration, but it is usually to log a kernel + message. For a list of valid actions available for a given watchdog device, + check the content of the + /sys/class/watchdog/watchdogX/pretimeout_available_governors + file. Typically, available governor types are noop and panic. + Availability, names and functionality might vary depending on the specific device driver + in use. If the pretimeout_available_governors sysfs file is empty, + the governor might be built as a kernel module and might need to be manually loaded + (e.g. pretimeout_noop.ko), or the watchdog device might not support + pre-timeouts. + + WatchdogDevice= @@ -441,17 +485,19 @@ Most of these settings are unset, which means the resource limits are inherited from the kernel or, if invoked in a container, from the container manager. However, the following have defaults: - DefaultLimitNOFILE= defaults to 1024:&HIGH_RLIMIT_NOFILE;. + DefaultLimitNOFILE= defaults to 1024:&HIGH_RLIMIT_NOFILE;. + DefaultLimitMEMLOCK= defaults to 8M. + DefaultLimitCORE= does not have a default but it is worth mentioning that RLIMIT_CORE is set to infinity by PID 1 which is inherited by its children. - - Note that the service manager internally increases RLIMIT_MEMLOCK for - itself, however the limit is reverted to the original value for child processes forked off. + Note that the service manager internally in PID 1 bumps RLIMIT_NOFILE and + RLIMIT_MEMLOCK to higher values, however the limit is reverted to the mentioned + defaults for all child processes forked off. @@ -459,7 +505,7 @@ DefaultOOMPolicy= Configure the default policy for reacting to processes being killed by the Linux - Out-Of-Memory (OOM) killer. This may be used to pick a global default for the per-unit + Out-Of-Memory (OOM) killer or systemd-oomd. This may be used to pick a global default for the per-unit OOMPolicy= setting. See systemd.service5 for details. Note that this default is not used for services that have Delegate= diff --git a/man/systemd-sysupdate.xml b/man/systemd-sysupdate.xml new file mode 100644 index 000000000..77c1635b9 --- /dev/null +++ b/man/systemd-sysupdate.xml @@ -0,0 +1,287 @@ + + + + + + + + systemd-sysupdate + systemd + + + + systemd-sysupdate + 8 + + + + systemd-sysupdate + systemd-sysupdate.service + systemd-sysupdate.timer + systemd-sysupdate-reboot.service + systemd-sysupdate-reboot.timer + Automatically Update OS or Other Resources + + + + + systemd-sysupdate + OPTIONS + + + systemd-sysupdate.service + + + + Description + + systemd-sysupdate atomically updates the host OS, container images, portable + service images or other sources, based on the transfer configuration files described in + sysupdate.d5. + + This tool implements file, directory, or partition based update schemes, supporting multiple + parallel installed versions of specific resources in an A/B (or even: A/B/C, A/B/C/D/, …) style. A/B + updating means that when one version of a resource is currently being used, the next version can be + downloaded, unpacked, and prepared in an entirely separate location, independently of the first, and — once + complete — be activated, swapping the roles so that it becomes the used one and the previously used one + becomes the one that is replaced by the next update, and so on. The resources to update are defined + in transfer files, one for each resource to be updated. For example, resources that may be updated with + this tool could be: a root file system partition, a matching Verity partition plus one kernel image. The + combination of the three would be considered a complete OS update. + + The tool updates partitions, files or directory trees always in whole, and operates with at least + two versions of each of these resources: the current version, plus the + next version: the one that is being updated to, and which is initially incomplete as + the downloaded data is written to it; plus optionally more versions. Once the download of a newer version + is complete it becomes the current version, releasing the version previously considered current for + deletion/replacement/updating. + + When installing new versions the tool will directly download, decompress, unpack and write the new + version into the destination. This is done in a robust fashion so that an incomplete download can be + recognized on next invocation, and flushed out before a new attempt is initiated. + + Note that when writing updates to a partition, the partition has to exist already, as + systemd-sysupdate will not automatically create new partitions. Use a tool such as + systemd-repart8 to + automatically create additional partitions to be used with systemd-sysupdate on + boot. + + The tool can both be used on the running OS, to update the OS in "online" state from within itself, + and on "offline" disk images, to update them from the outside based on transfer files + embedded in the disk images. For the latter, see below. The latter is + particularly interesting to update container images or portable service images. + + The systemd-sysupdate.service system service will automatically update the + host OS based on the installed transfer files. It is triggered in regular intervals via + systemd-sysupdate.timer. The systemd-sysupdate-reboot.service + will automatically reboot the system after a new version is installed. It is triggered via + systemd-sysupdate-reboot.timer. The two services are separate from each other as it + is typically advisable to download updates regularly while the system is up, but delay reboots until the + appropriate time (i.e. typically at night). The two sets of service/timer units may be enabled + separately. + + For details about transfer files and examples see + sysupdate.d5. + + + + Command + + The following commands are understood: + + + + VERSION + + If invoked without an argument, enumerates downloadable and installed versions, and + shows a summarizing table with the discovered versions and their properties, including whether + there's a newer candidate version to update to. If a version argument is specified, shows details + about the specific version, including the individual files that need to be transferred to acquire the + version. + + If no command is explicitly specified this command is implied. + + + + + + Checks if there's a new version available. This internally enumerates downloadable and + installed versions and returns exit status 0 if there's a new version to update to, non-zero + otherwise. If there is a new version to update to, its version identifier is written to standard + output. + + + + VERSION + + Installs (updates to) the specified version, or if none is specified to the newest + version available. If the version is already installed or no newer version available, no operation is + executed. + + If a new version to install/update to is found, old installed versions are deleted until at + least one new version can be installed, as configured via InstanceMax= in + sysupdate.d5, or + via the available partition slots of the right type. This implicit operation can also be invoked + explicitly via the vacuum command described below. + + + + + + Deletes old installed versions until the limits configured via + InstanceMax= in + sysupdate.d5 are + met again. Normally, it should not be necessary to invoke this command explicitly, since it is + implicitly invoked whenever a new update is initiated. + + + + + + Checks whether a newer version of the OS is installed than the one currently + running. Returns zero if so, non-zero otherwise. This compares the newest installed version's + identifier with the OS image version as reported by the IMAGE_VERSION= field in + /etc/os-release. If the former is newer than the latter, an update was + apparently completed but not activated (i.e. rebooted into) yet. + + + + + + Similar to the command but immediately reboots in case a + newer version of the OS has been installed than the one currently running. This operation can be done + implicitly together with the update command, after a completed update via the + switch, see below. This command will execute no operation (and return + success) if no update has been installed, and thus the system was not rebooted. + + + + + + Lists components that can be updated. This enumerates the + /etc/sysupdate.*.d/, /run/sysupdate.*.d/ and + /usr/lib/sysupdate.*.d/ directories that contain transfer files. This command is + useful to list possible parameters for (see below). + + + + + + + + + Options + + The following options are understood: + + + + + + + + Selects the component to update. Takes a component name as argument. This has the + effect of slightly altering the search logic for transfer files. If this switch is not used, the + transfer files are loaded from /etc/sysupdate.d/*.conf, + /run/sysupdate.d/*.conf and /usr/lib/sysupdate.d/*.conf. If + this switch is used, the specified component name is used to alter the directories to look in to be + /etc/sysupdate.component.d/*.conf, + /run/sysupdate.component.d/*.conf and + /usr/lib/sysupdate.component.d/*.conf, each time with + the component string replaced with the specified + component name. + + Use the components command to list available components to update. This enumerates + the directories matching this naming rule. + + Components may be used to define a separate set of transfer files for different components of + the OS that shall be updated separately. Do not use this concept for resources that shall always be + updated together in a synchronous fashion. Simply define multiple transfer files within the same + sysupdate.d/ directory for these cases. + + This option may not be combined with . + + + + + + A path to a directory. If specified, the transfer *.conf files + are read from this directory instead of /usr/lib/sysupdate.d/*.conf, + /etc/sysupdate.d/*.conf, and /run/sysupdate.d/*.conf. + + This option may not be combined with . + + + + + + Takes a path to a directory to use as root file system when searching for + sysupdate.d/*.conf files. + + + + + + Takes a path to a disk image file or device to mount and use in a similar fashion to + , see above. If this is used and partition resources are updated this is done + inside the specified disk image. + + + + + + + Takes a decimal integer greater than or equal to 2. Controls how many versions to + keep at any time. This option may also be configured inside the transfer files, via the + InstancesMax= setting, see + sysupdate.d5 for + details. + + + + + + Takes a boolean argument, defaults to yes. This may be used to specify whether the + newly updated resource versions shall be synchronized to disk when appropriate (i.e. after the + download is complete, before it is finalized, and again after finalization). This should not be + turned off, except to improve runtime performance in testing environments. + + + + + + Takes a boolean argument, defaults to yes. Controls whether to cryptographically + verify downloads. Do not turn this off, except in testing environments. + + + + + + When used in combination with the update command and a new version is + installed, automatically reboots the system immediately afterwards. + + + + + + + + + + Exit status + + On success, 0 is returned, a non-zero failure code otherwise. + + + + See Also + + systemd1, + sysupdate.d5, + systemd-repart8 + + + + diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 77eb2d9be..5bb1679ae 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -145,7 +145,7 @@ BindReadOnlyPaths=/dev/log /run/systemd/journal/socket /run/systemd/journal/stdout - + @@ -276,9 +276,7 @@ In order to allow propagating mounts at runtime in a safe manner, /run/systemd/propagate on the host will be used to set up new mounts, and /run/host/incoming/ in the private namespace - will be used as an intermediate step to store them before being moved to the final mount point. - - + will be used as an intermediate step to store them before being moved to the final mount point. @@ -364,9 +362,7 @@ InaccessiblePaths=, or under /home/ and other protected directories if ProtectHome=yes is specified. TemporaryFileSystem= with :ro or - ProtectHome=tmpfs should be used instead. - - + ProtectHome=tmpfs should be used instead. @@ -459,6 +455,37 @@ + + + ExtensionDirectories= + + This setting is similar to BindReadOnlyPaths= in that it mounts a file + system hierarchy from a directory, but instead of providing a destination path, an overlay will be set + up. This option expects a whitespace separated list of source directories. + + A read-only OverlayFS will be set up on top of /usr/ and + /opt/ hierarchies. The order in which the directories are listed will determine + the order in which the overlay is laid down: directories specified first to last will result in overlayfs + layers bottom to top. + + Each directory listed in ExtensionDirectories= may be prefixed with -, + in which case it will be ignored when its source path does not exist. Any mounts created with this option are + specific to the unit, and are not visible in the host's mount table. + + These settings may be used more than once, each usage appends to the unit's list of directories + paths. If the empty string is assigned, the entire list of mount paths defined prior to this is + reset. + + Each directory must contain a /usr/lib/extension-release.d/extension-release.IMAGE + file, with the appropriate metadata which matches RootImage=/RootDirectory= + or the host. See: + os-release5. + + Note that usage from user units requires overlayfs support in unprivileged user namespaces, + which was first introduced in kernel v5.11. + + + @@ -602,7 +629,7 @@ Capabilities - + @@ -1012,7 +1039,7 @@ CapabilityBoundingSet=~CAP_B CAP_C normally at 0. Use the OOMPolicy= setting of service units to configure how the service - manager shall react to the kernel OOM killer terminating a process of the service. See + manager shall react to the kernel OOM killer or systemd-oomd terminating a process of the service. See systemd.service5 for details. @@ -1231,7 +1258,7 @@ CapabilityBoundingSet=~CAP_B CAP_C DynamicUser= is set. This setting cannot ensure protection in all cases. In general it has the same limitations as ReadOnlyPaths=, see below. - + @@ -1485,7 +1512,7 @@ NoExecPaths=/ ExecPaths=/usr/sbin/my_daemon /usr/lib /usr/lib64 - + @@ -1510,7 +1537,7 @@ BindReadOnlyPaths=/var/lib/systemd then the invoked processes by the unit cannot see any files or directories under /var/ except for /var/lib/systemd or its contents. - + @@ -1538,7 +1565,7 @@ BindReadOnlyPaths=/var/lib/systemd available), and the unit should be written in a way that does not solely rely on this setting for security. - + @@ -1572,7 +1599,7 @@ BindReadOnlyPaths=/var/lib/systemd namespaces are not available), and the unit should be written in a way that does not solely rely on this setting for security. - + When access to some but not all devices must be possible, the DeviceAllow= setting might be used instead. See @@ -1606,7 +1633,7 @@ BindReadOnlyPaths=/var/lib/systemd JoinsNamespaceOf= to listen on sockets inside of network namespaces of other services. - + @@ -1625,7 +1652,7 @@ BindReadOnlyPaths=/var/lib/systemd When this option is used on a socket unit any sockets bound on behalf of this unit will be bound within the specified network namespace. - + @@ -1656,7 +1683,7 @@ BindReadOnlyPaths=/var/lib/systemd not available), and the unit should be written in a way that does not solely rely on this setting for security. - + @@ -1672,7 +1699,7 @@ BindReadOnlyPaths=/var/lib/systemd IPCNamespacePath= configured, as otherwise the network namespace of those units is reused. - + @@ -1726,7 +1753,7 @@ BindReadOnlyPaths=/var/lib/systemd capability (e.g. services for which User= is set), NoNewPrivileges=yes is implied. - + @@ -1743,7 +1770,7 @@ BindReadOnlyPaths=/var/lib/systemd doesn't have the CAP_SYS_ADMIN capability (e.g. services for which User= is set), NoNewPrivileges=yes is implied. - + @@ -1767,7 +1794,7 @@ BindReadOnlyPaths=/var/lib/systemd inaccessible. If ProtectKernelTunables= is set, MountAPIVFS=yes is implied. - + @@ -1788,7 +1815,7 @@ BindReadOnlyPaths=/var/lib/systemd but the unit doesn't have the CAP_SYS_ADMIN capability (e.g. services for which User= is set), NoNewPrivileges=yes is implied. - + @@ -1807,7 +1834,7 @@ BindReadOnlyPaths=/var/lib/systemd capability (e.g. services for which User= is set), NoNewPrivileges=yes is implied. - + @@ -2111,7 +2138,7 @@ RestrictNamespaces=~cgroup net option. Hence it is primarily useful to explicitly request this behaviour if none of the other settings are used. - + @@ -2141,7 +2168,7 @@ RestrictNamespaces=~cgroup net Usually, it is best to leave this setting unmodified, and use higher level file system namespacing options instead, in particular PrivateMounts=, see above. - + @@ -2483,18 +2510,39 @@ SystemCallErrorNumber=EPERM EnvironmentFile= - Similar to Environment= but reads the environment variables from a text - file. The text file should contain new-line-separated variable assignments. Empty lines, lines without an - = separator, or lines starting with ; or # will be ignored, which may be used for - commenting. A line ending with a backslash will be concatenated with the following one, allowing multiline - variable definitions. The parser strips leading and trailing whitespace from the values of assignments, unless - you use double quotes ("). + Similar to Environment= but reads the environment variables from a text file. + The text file should contain newline-separated variable assignments. Empty lines, lines without an + = separator, or lines starting with ; or # will be + ignored, which may be used for commenting. The file must be UTF-8 encoded. Valid characters are unicode scalar values other than noncharacters, U+0000 NUL, and U+FEFF byte order mark. Control codes other than NUL + are allowed. - C escapes - are supported, but not - most control characters. - \t and \n can be used to insert tabs and newlines within - EnvironmentFile=. + In the file, an unquoted value after the = is parsed with the same backslash-escape + rules as unquoted + text in a POSIX shell, but unlike in a shell, interior whitespace is preserved and quotes after the + first non-whitespace character are preserved. Leading and trailing whitespace (space, tab, carriage return) is + discarded, but interior whitespace within the line is preserved verbatim. A line ending with a backslash will be + continued to the following one, with the newline itself discarded. A backslash + \ followed by any character other than newline will preserve the following character, so that + \\ will become the value \. + + In the file, a '-quoted value after the = can span multiple lines + and contain any character verbatim other than single quote, like single-quoted + text in a POSIX shell. No backslash-escape sequences are recognized. Leading and trailing whitespace + outside of the single quotes is discarded. + + In the file, a "-quoted value after the = can span multiple lines, + and the same escape sequences are recognized as in double-quoted + text of a POSIX shell. Backslash (\) followed by any of "\`$ will + preserve that character. A backslash followed by newline is a line continuation, and the newline itself is + discarded. A backslash followed by any other character is ignored; both the backslash and the following + character are preserved verbatim. Leading and trailing whitespace outside of the double quotes is + discarded. The argument passed should be an absolute filename or wildcard expression, optionally prefixed with -, which indicates that if the file does not exist, it will not be read and no error or @@ -2527,12 +2575,6 @@ SystemCallErrorNumber=EPERM Variables set for invoked processes due to this setting are subject to being overridden by those configured with Environment= or EnvironmentFile=. - C escapes - are supported, but not - most control characters. - \t and \n can be used to insert tabs and newlines within - EnvironmentFile=. - Example: PassEnvironment=VAR1 VAR2 VAR3 passes three variables VAR1, @@ -2676,8 +2718,8 @@ SystemCallErrorNumber=EPERM StandardInput=, see above. If path refers to a regular file on the filesystem, it is opened (created if it doesn't exist yet) for writing at the beginning of the file, but without truncating it. - If standard input and output are directed to the same file path, it is opened only once, for reading as well - as writing and duplicated. This is particularly useful when the specified path refers to an + If standard input and output are directed to the same file path, it is opened only once — for reading as well + as writing — and duplicated. This is particularly useful when the specified path refers to an AF_UNIX socket in the file system, as in that case only a single stream connection is created for both input and output. @@ -2703,16 +2745,17 @@ SystemCallErrorNumber=EPERM connects standard output to a socket acquired via socket activation. The semantics are similar to the same option of StandardInput=, see above. - The option connects standard output to a specific, - named file descriptor provided by a socket unit. A name may be specified as part of this option, following a - : character (e.g. fd:foobar). If no name is specified, the name + The option connects standard output to a + specific, named file descriptor provided by a socket unit. A name may be specified as part of this + option, following a : character + (e.g. fd:foobar). If no name is specified, the name stdout is implied (i.e. fd is equivalent to - fd:stdout). At least one socket unit defining the specified name must be provided via the - Sockets= option, and the file descriptor name may differ from the name of its containing - socket unit. If multiple matches are found, the first one will be used. See + fd:stdout). At least one socket unit defining the specified name must be provided + via the Sockets= option, and the file descriptor name may differ from the name of + its containing socket unit. If multiple matches are found, the first one will be used. See FileDescriptorName= in - systemd.socket5 for more - details about named descriptors and their ordering. + systemd.socket5 + for more details about named descriptors and their ordering. If the standard output (or error output, see below) of a unit is connected to the journal or the kernel log buffer, the unit will implicitly gain a dependency of type After= @@ -3005,7 +3048,10 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy newline characters and NUL bytes. If the file system path is omitted it is chosen identical to the credential name, i.e. this is a terse way do declare credentials to inherit from the service manager into a service. This option may be used multiple times, each time defining - an additional credential to pass to the unit. + an additional credential to pass to the unit. Alternatively, if the path is a directory, every file + in that directory will be loaded as a separate credential. The ID for each credential will be the + provided ID suffixed with _$FILENAME (e.g., Key_file1). When + loading from a directory, symlinks will be ignored. The LoadCredentialEncrypted= setting is identical to LoadCredential=, except that the credential data is decrypted before being passed @@ -3364,7 +3410,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy $SERVICE_RESULT - Only defined for the service unit type, this environment variable is passed to all + Only used for the service unit type. This environment variable is passed to all ExecStop= and ExecStopPost= processes, and encodes the service "result". Currently, the following values are defined: @@ -3432,7 +3478,7 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy $EXIT_CODE $EXIT_STATUS - Only defined for the service unit type, these environment variables are passed to all + Only defined for the service unit type. These environment variables are passed to all ExecStop=, ExecStopPost= processes and contain exit status/code information of the main process of the service. For the precise definition of the exit code and status, see wait2. $EXIT_CODE @@ -3544,6 +3590,31 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy + + $MONITOR_SERVICE_RESULT + $MONITOR_EXIT_CODE + $MONITOR_EXIT_STATUS + $MONITOR_INVOCATION_ID + $MONITOR_UNIT + + Only defined for the service unit type. Those environment variable are passed to + all ExecStart= and ExecStartPre= processes which run in + services triggered by OnFailure= or OnSuccess= dependencies. + + + Variables $MONITOR_SERVICE_RESULT, $MONITOR_EXIT_CODE + and $MONITOR_EXIT_STATUS take the same values as for + ExecStop= and ExecStopPost= processes. Variables + $MONITOR_INVOCATION_ID and $MONITOR_UNIT are set to the + invocaton id and unit name of the service which triggered the dependency. + + Note that when multiple services trigger the same unit, those variables will be + not be passed. Consider using a template handler unit for that case instead: + OnFailure=handler@%n.service for non-templated units, + or OnFailure=handler@%p-%i.service for templated + units. + + $PIDFILE @@ -3981,6 +4052,75 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy + + Examples + + + <varname>$MONITOR_<replaceable>*</replaceable></varname> usage + + A service myfailer.service which can trigger an + OnFailure= dependency. + + +[Unit] +Description=Service which can trigger an OnFailure= dependency +OnFailure=myhandler.service + +[Service] +ExecStart=/bin/myprogram + + + A service mysuccess.service which can trigger an + OnSuccess= dependency. + + +[Unit] +Description=Service which can trigger an OnSuccess= dependency +OnSuccess=myhandler.service + +[Service] +ExecStart=/bin/mysecondprogram + + + A service myhandler.service which can be triggered + by any of the above services. + + +[Unit] +Description=Acts on service failing or succeeding + +[Service] +ExecStart=/bin/bash -c "echo $MONITOR_SERVICE_RESULT $MONITOR_EXIT_CODE $MONITOR_EXIT_STATUS $MONITOR_INVOCATION_ID $MONITOR_UNIT" + + + If myfailer.service were to run and exit in failure, + then myhandler.service would be triggered and the + monitor variables would be set as follows: + + +MONITOR_SERVICE_RESULT=exit-code +MONITOR_EXIT_CODE=exited +MONITOR_EXIT_STATUS=1 +MONITOR_INVOCATION_ID=cc8fdc149b2b4ca698d4f259f4054236 +MONITOR_UNIT=myfailer.service + + + If mysuccess.service were to run and exit in success, + then myhandler.service would be triggered and the + monitor variables would be set as follows: + + +MONITOR_SERVICE_RESULT=success +MONITOR_EXIT_CODE=exited +MONITOR_EXIT_STATUS=0 +MONITOR_INVOCATION_ID=6ab9af147b8c4a3ebe36e7a5f8611697 +MONITOR_UNIT=mysuccess.service + + + + + + See Also diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 933fe8df4..5629057bf 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -178,6 +178,20 @@ + + Kind= + + A whitespace-separated list of shell-style globs matching the device kind, as exposed by + networkctl status INTERFACE or + ip -d link show INTERFACE. If the list is + prefixed with a "!", the test is inverted. Some valid values are bond, + bridge, gre, tun, + veth. Valid kinds are given by netlink's IFLA_INFO_KIND + attribute, so this is not comprehensive. + + + + Property= @@ -946,6 +960,119 @@ + + MDI= + + Specifies the medium dependent interface (MDI) mode for the interface. A MDI describes + the interface from a physical layer implementation to the physical medium used to carry the + transmission. Takes one of the following words: straight (or equivalently: + mdi), crossover (or equivalently: + mdi-x, mdix), and auto. When + straight, the MDI straight through mode will be used. When + crossover, the MDI crossover (MDI-X) mode will be used. When + auto, the MDI status is automatically detected. Defaults to unset, and the + kernel's default will be used. + + + + + SR-IOVVirtualFunctions= + + Specifies the number of SR-IOV virtual functions. Takes an integer in the range + 0…2147483647. Defaults to unset, and automatically determined from the values specified in + the VirtualFunction= settings in the [SR-IOV] sections. + + + + + + + + [SR-IOV] Section Options + The [SR-IOV] section accepts the following keys. Specify several [SR-IOV] sections to + configure several SR-IOVs. SR-IOV provides the ability to partition a single physical PCI resource + into virtual PCI functions which can then be injected into a VM. In the case of network VFs, SR-IOV + improves north-south network performance (that is, traffic with endpoints outside the host machine) + by allowing traffic to bypass the host machine’s network stack. + + + + VirtualFunction= + + Specifies a Virtual Function (VF), lightweight PCIe function designed solely to move + data in and out. Takes an integer in the range 0…2147483646. This option is compulsory. + + + + + + VLANId= + + Specifies VLAN ID of the virtual function. Takes an integer in the range 1…4095. + + + + + QualityOfService= + + Specifies quality of service of the virtual function. Takes an integer in the range + 1…4294967294. + + + + + VLANProtocol= + + Specifies VLAN protocol of the virtual function. Takes 802.1Q or + 802.1ad. + + + + + MACSpoofCheck= + + Takes a boolean. Controls the MAC spoof checking. When unset, the kernel's default will + be used. + + + + + QueryReceiveSideScaling= + + Takes a boolean. Toggle the ability of querying the receive side scaling (RSS) + configuration of the virtual function (VF). The VF RSS information like RSS hash key may be + considered sensitive on some devices where this information is shared between VF and the + physical function (PF). When unset, the kernel's default will be used. + + + + + Trust= + + Takes a boolean. Allows one to set trust mode of the virtual function (VF). When set, + VF users can set a specific feature which may impact security and/or performance. When unset, + the kernel's default will be used. + + + + + LinkState= + + Allows one to set the link state of the virtual function (VF). Takes a boolean or a + special value auto. Setting to auto means a + reflection of the physical function (PF) link state, yes lets the VF to + communicate with other VFs on this host even if the PF link state is down, + no causes the hardware to drop any packets sent by the VF. When unset, + the kernel's default will be used. + + + + + MACAddress= + + Specifies the MAC address for the virtual function. + + diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 04c00beb2..0ba3c8552 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -94,13 +94,13 @@ A dummy device drops all packets sent to it. gre - A Level 3 GRE tunnel over IPv4. See RFC 2784 for details. + A Level 3 GRE tunnel over IPv4. See RFC 2784 for details. Name gre0 should not be used, as the kernel creates a device with this name when the corresponding kernel module is loaded. gretap - A Level 2 GRE tunnel over IPv4. + A Level 2 GRE tunnel over IPv4. Name gretap0 should not be used, as the kernel creates a device with this name when the corresponding kernel module is loaded. erspan - ERSPAN mirrors traffic on one or more source ports and delivers the mirrored traffic to one or more destination ports on another switch. The traffic is encapsulated in generic routing encapsulation (GRE) and is therefore routable across a layer 3 network between the source switch and the destination switch. + ERSPAN mirrors traffic on one or more source ports and delivers the mirrored traffic to one or more destination ports on another switch. The traffic is encapsulated in generic routing encapsulation (GRE) and is therefore routable across a layer 3 network between the source switch and the destination switch. Name erspan0 should not be used, as the kernel creates a device with this name when the corresponding kernel module is loaded. ip6gre A Level 3 GRE tunnel over IPv6. @@ -192,6 +192,9 @@ ipoib An IP over Infiniband subinterface. + + wlan + A virtual wireless network (WLAN) interface. @@ -573,7 +576,11 @@ Local= - Configures local IP address. + Configures local IP address. It must be an address on the underlying interface of the + VXLAN interface, or one of the special values ipv4_link_local, + ipv6_link_local, dhcp4, dhcp6, and + slaac. If one of the special values is specified, an address which matches + the corresponding type on the underlying interface will be used. Defaults to unset. @@ -625,7 +632,7 @@ endpoint answers ARP requests from the local bridge on behalf of remote Distributed Overlay Virtual Ethernet - (DVOE) clients. Defaults to false. + (DOVE) clients. Defaults to false. @@ -874,12 +881,15 @@ Local= - Specifies the IP address of the local interface. Takes an IP address, or the special values - auto, static, or dynamic. When an address - is set, then the local interface must have the address. If auto, then one of the - addresses on the local interface is used. Similarly, if static or - dynamic is set, then one of the static or dynamic addresses on the local - interface is used. Defaults to auto. + Specifies the IP address of a local interface. Takes an IP address, or the special + values auto, static, or dynamic. + Optionally a name of a local interface can be specified after @, e.g. + 192.168.0.1@eth0 or auto@eth0. When an address is + specified, then a local or specified interface must have the address, and the remote address + must be accessible through the local address. If auto, then one of the + addresses on a local or specified interface which is accessible to the remote address will be + used. Similarly, if static or dynamic is set, then one + of the static or dynamic addresses will be used. Defaults to auto. @@ -1145,11 +1155,25 @@ the following keys: + + External= + + Takes a boolean value. When true, then the tunnel is externally controlled, which is + also known as collect metadata mode, and most settings below like Local= + or Remote= are ignored. This implies Independent=. + Defaults to false. + + Local= - A static local address for tunneled packets. It must be an address on another interface of - this host, or the special value any. + A static local address for tunneled packets. It must be an address on another interface + of this host, or one of the special values any, + ipv4_link_local, ipv6_link_local, + dhcp4, dhcp6, and slaac. If one + of the special values except for any is specified, an address which + matches the corresponding type on the underlying interface will be used. Defaults to + any. @@ -2161,6 +2185,42 @@ + + [WLAN] Section Options + The [WLAN] section only applies to WLAN interfaces, and accepts the following keys: + + + + PhysicalDevice= + + Specifies the name or index of the physical WLAN device (e.g. 0 or + phy0). The list of the physical WLAN devices that exist os the host can be + obtained by iw phy command. This option is mandatory. + + + + + Type= + + Specifies the type of the interface. Takes one of the ad-hoc, + station, ap, ap-vlan, + wds, monitor, mesh-point, + p2p-client, p2p-go, p2p-device, + ocb, and nan. This option is mandatory. + + + + + WDS= + + Enables the Wireless Distribution System (WDS) mode on the interface. The mode is also + known as the 4 address mode. Takes a boolean value. Defaults to unset, and + the kernel's default will be used. + + + + + Examples diff --git a/man/systemd.network.xml b/man/systemd.network.xml index 6fe87bbeb..c849cfc4f 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -85,6 +85,7 @@ + @@ -300,94 +301,7 @@ - - [SR-IOV] Section Options - The [SR-IOV] section accepts the following keys. Specify several [SR-IOV] sections to - configure several SR-IOVs. SR-IOV provides the ability to partition a single physical PCI resource - into virtual PCI functions which can then be injected into a VM. In the case of network VFs, SR-IOV - improves north-south network performance (that is, traffic with endpoints outside the host machine) - by allowing traffic to bypass the host machine’s network stack. - - - - VirtualFunction= - - Specifies a Virtual Function (VF), lightweight PCIe function designed solely to move - data in and out. Takes an integer in the range 0…2147483646. This option is compulsory. - - - - - - VLANId= - - Specifies VLAN ID of the virtual function. Takes an integer in the range 1…4095. - - - - - QualityOfService= - - Specifies quality of service of the virtual function. Takes an integer in the range - 1…4294967294. - - - - - VLANProtocol= - - Specifies VLAN protocol of the virtual function. Takes 802.1Q or - 802.1ad. - - - - - MACSpoofCheck= - - Takes a boolean. Controls the MAC spoof checking. When unset, the kernel's default will - be used. - - - - - QueryReceiveSideScaling= - - Takes a boolean. Toggle the ability of querying the receive side scaling (RSS) - configuration of the virtual function (VF). The VF RSS information like RSS hash key may be - considered sensitive on some devices where this information is shared between VF and the - physical function (PF). When unset, the kernel's default will be used. - - - - - Trust= - - Takes a boolean. Allows one to set trust mode of the virtual function (VF). When set, VF - users can set a specific feature which may impact security and/or performance. When unset, - the kernel's default will be used. - - - - - LinkState= - - Allows one to set the link state of the virtual function (VF). Takes a boolean or a - special value auto. Setting to auto means a - reflection of the physical function (PF) link state, yes lets the VF to - communicate with other VFs on this host even if the PF link state is down, - no causes the hardware to drop any packets sent by the VF. When unset, - the kernel's default will be used. - - - - - MACAddress= - - Specifies the MAC address for the virtual function. - - - - + [Network] Section Options @@ -910,12 +824,11 @@ Table=1234 DHCPPrefixDelegation= - Takes a boolean value. When enabled, requests subnet prefixes acquired by a DHCPv6 - client, or by a DHCPv4 client through the 6RD option configured on another link. By default, - an address within each delegated prefix will be assigned, and the prefixes will be announced - through IPv6 Router Advertisement when IPv6SendRA= is enabled. Such - default settings can be configured in the [DHCPPrefixDelegation] section. Defaults to - disabled. + Takes a boolean value. When enabled, requests subnet prefixes on another link via the DHCPv6 + protocol or via the 6RD option in the DHCPv4 protocol. An address within each delegated prefix will + be assigned, and the prefixes will be announced through IPv6 Router Advertisement if + IPv6SendRA= is enabled. This behaviour can be configured in the + [DHCPPrefixDelegation] section. Defaults to disabled. @@ -956,7 +869,6 @@ Table=1234 IPoIB= IPVLAN= IPVTAP= - L2TP= MACsec= MACVLAN= MACVTAP= @@ -965,7 +877,7 @@ Table=1234 VXLAN= Xfrm= - The name of an IPoIB, IPVLAN, IPVTAP, L2TP, MACsec, MACVLAN, MACVTAP, tunnel, VLAN, + The name of an IPoIB, IPVLAN, IPVTAP, MACsec, MACVLAN, MACVTAP, tunnel, VLAN, VXLAN, or Xfrm to be created on the link. See systemd.netdev5. This option may be specified more than once. @@ -1031,8 +943,10 @@ Table=1234 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 no. - + dhcp and static. Defaults to + dhcp-on-stop when systemd-networkd is running in + initrd, yes when the root filesystem is a network filesystem, and + no otherwise. @@ -1131,7 +1045,8 @@ Table=1234 Detection. See RFC 5227. When ipv6, performs IPv6 Duplicate Address Detection. See RFC 4862. Defaults to - ipv6. + ipv4 for IPv4 link-local addresses, ipv6 for IPv6 + addresses, and none otherwise. @@ -1547,9 +1462,9 @@ Table=1234 For IPv4 route, defaults to host if Type= is local or nat, and link if - Type= is broadcast, multicast, or - anycast. In other cases, defaults to global. The value - is not used for IPv6. + Type= is broadcast, multicast, + anycast, or direct unicast routes. In other cases, + defaults to global. The value is not used for IPv6. @@ -2645,18 +2560,24 @@ Token=prefixstable:2002:da8:1:: DNS= EmitDNS= takes a boolean. Configures whether the DHCP leases - handed out to clients shall contain DNS server information. Defaults to yes. The - DNS servers to pass to clients may be configured with the DNS= option, which takes - a list of IPv4 addresses. If the EmitDNS= option is enabled but no servers - configured, the servers are automatically propagated from an "uplink" interface that has appropriate - servers set. The "uplink" interface is determined by the default route of the system with the highest - priority. Note that this information is acquired at the time the lease is handed out, and does not - take uplink interfaces into account that acquire DNS server information at a later point. If no - suitable uplink interface is found the DNS server data from /etc/resolv.conf is - used. Also, note that the leases are not refreshed if the uplink network configuration changes. To - ensure clients regularly acquire the most current uplink DNS server information, it is thus advisable - to shorten the DHCP lease time via MaxLeaseTimeSec= described - above. + handed out to clients shall contain DNS server information. Defaults to yes. + The DNS servers to pass to clients may be configured with the DNS= option, + which takes a list of IPv4 addresses, or special value _server_address which + will be converted to the address used by the DHCP server. + + If the EmitDNS= option is enabled but no servers configured, the + servers are automatically propagated from an "uplink" interface that has appropriate servers + set. The "uplink" interface is determined by the default route of the system with the highest + priority. Note that this information is acquired at the time the lease is handed out, and does + not take uplink interfaces into account that acquire DNS server information at a later point. + If no suitable uplink interface is found the DNS server data from + /etc/resolv.conf is used. Also, note that the leases are not refreshed if + the uplink network configuration changes. To ensure clients regularly acquire the most current + uplink DNS server information, it is thus advisable to shorten the DHCP lease time via + MaxLeaseTimeSec= described above. + + This setting can be specified multiple times. If an empty string is specified, then all + DNS servers specified earlier are cleared. @@ -2705,6 +2626,42 @@ Token=prefixstable:2002:da8:1:: /etc/localtime symlink. + + BootServerAddress= + + + Takes an IPv4 address of the boot server used by e.g. PXE boot systems. When specified, this + address is sent in the field of the DHCP message header. See RFC 2131 for more details. Defaults to + unset. + + + + + BootServerName= + + + Takes a name of the boot server used by e.g. PXE boot systems. When specified, this name is + sent in the DHCP option 66 ("TFTP server name"). See RFC 2132 for more details. Defaults to + unset. + + Note that typically setting one of BootServerName= or + BootServerAddress= is sufficient, but both can be set too, if desired. + + + + + BootFilename= + + + Takes a path or URL to a file loaded by e.g. a PXE boot loader. When specified, this path is + sent in the DHCP option 67 ("Bootfile name"). See RFC 2132 for more details. Defaults to + unset. + + + SendOption= @@ -3039,6 +2996,15 @@ Token=prefixstable:2002:da8:1:: receiving port. When unset, the kernel's default will be used. + + Isolated= + + Takes a boolean. Configures whether this port is isolated or not. Within a bridge, + isolated ports can only communicate with non-isolated ports. When set to true, this port can only + communicate with other ports whose Isolated setting is false. When set to false, this port + can communicate with any other ports. When unset, the kernel's default will be used. + + UseBPDU= @@ -3746,7 +3712,7 @@ Token=prefixstable:2002:da8:1:: - Flows are defined only by source address. Equivalnet to the srchost + Flows are defined only by source address. Equivalent to the srchost option for tc qdisc command. See also tc-cake8. @@ -3754,8 +3720,8 @@ Token=prefixstable:2002:da8:1:: - Flows are defined only by destination address. Equivalnet to the - srchost option for tc qdisc command. See also + Flows are defined only by destination address. Equivalent to the + dsthost option for tc qdisc command. See also tc-cake8. @@ -3780,7 +3746,7 @@ Token=prefixstable:2002:da8:1:: Flows are defined by the 5-tuple (see flows in the above), and - fairness is applied first over source addresses, then over individual flows. Equivalnet + fairness is applied first over source addresses, then over individual flows. Equivalent to the dual-srchost option for tc qdisc command. See also tc-cake8. @@ -3791,7 +3757,7 @@ Token=prefixstable:2002:da8:1:: Flows are defined by the 5-tuple (see flows in the above), and fairness is applied first over destination addresses, then over individual flows. - Equivalnet to the dual-dsthost option for + Equivalent to the dual-dsthost option for tc qdisc command. See also tc-cake8. @@ -3801,7 +3767,7 @@ Token=prefixstable:2002:da8:1:: Flows are defined by the 5-tuple (see flows), and fairness is applied over source and destination addresses, and also over individual flows. - Equivalnet to the triple-isolate option for + Equivalent to the triple-isolate option for tc qdisc command. See also tc-cake8. @@ -4497,22 +4463,48 @@ DHCP=yes - IPv6 Prefix Delegation + IPv6 Prefix Delegation (DHCPv6 PD) - # /etc/systemd/network/55-ipv6-pd-upstream.network + # /etc/systemd/network/55-dhcpv6-pd-upstream.network [Match] Name=enp1s0 [Network] -DHCP=ipv6 +DHCP=ipv6 - # /etc/systemd/network/56-ipv6-pd-downstream.network +# The below setting is optional, to also assign an address in the delegated prefix +# to the upstream interface. If not necessary, then comment out the line below and +# the [DHCPPrefixDelegation] section. +DHCPPrefixDelegation=yes + +# If the upstream network provides Router Advertisement with Managed bit set, +# then comment out the line below and WithoutRA= setting in the [DHCPv6] section. +IPv6AcceptRA=no + +[DHCPv6] +WithoutRA=solicit + +[DHCPPrefixDelegation] +UplinkInterface=:self +SubnetId=0 +Announce=no + + # /etc/systemd/network/55-dhcpv6-pd-downstream.network [Match] Name=enp2s0 [Network] +DHCPPrefixDelegation=yes IPv6SendRA=yes -DHCPPrefixDelegation=yes + +# It is expected that the host is acting as a router. So, usually it is not +# necessary to receive Router Advertisement from other hosts in the downstream network. +IPv6AcceptRA=no + +[DHCPPrefixDelegation] +UplinkInterface=enp1s0 +SubnetId=1 +Announce=yes This will enable DHCPv6-PD on the interface enp1s0 as an upstream interface where the DHCPv6 client is running and enp2s0 as a downstream interface where the prefix is delegated to. @@ -4520,6 +4512,46 @@ DHCPPrefixDelegation=yes + + IPv6 Prefix Delegation (DHCPv4 6RD) + + # /etc/systemd/network/55-dhcpv4-6rd-upstream.network +[Match] +Name=enp1s0 + +[Network] +DHCP=ipv4 + +# When DHCPv4-6RD is used, the upstream network does not support IPv6. +# Hence, it is not necessary to wait for Router Advertisement, which is enabled by default. +IPv6AcceptRA=no + +[DHCPv4] +Use6RD=yes + + # /etc/systemd/network/55-dhcpv4-6rd-downstream.network +[Match] +Name=enp2s0 + +[Network] +DHCPPrefixDelegation=yes +IPv6SendRA=yes + +# It is expected that the host is acting as a router. So, usually it is not +# necessary to receive Router Advertisement from other hosts in the downstream network. +IPv6AcceptRA=no + +[DHCPPrefixDelegation] +UplinkInterface=enp1s0 +SubnetId=1 +Announce=yes + + This will enable DHCPv4-6RD on the interface enp1s0 as an upstream interface where the + DHCPv4 client is running and enp2s0 as a downstream interface where the prefix is delegated to. + The delegated prefixes are distributed by IPv6 Router Advertisement on the downstream network. + + + A bridge with two enslaved links diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 95cb0aca3..4e4a9732e 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -1140,7 +1140,11 @@ shall be considered preferred or less preferred candidates for process termination by the Linux OOM killer logic. See systemd.exec5 for - details. + details. + + This setting also applies to systemd-oomd, similar to kernel OOM kills + this setting determines the state of the service after systemd-oomd kills a cgroup associated + with the service. diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index f17fa66b1..dd3553aa1 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -138,7 +138,7 @@ a symlink, so when systemd is asked through D-Bus to load dbus-org.freedesktop.network1.service, it'll load systemd-networkd.service. As another example, default.target — - the default system target started at boot — is commonly symlinked (aliased) to either + the default system target started at boot — is commonly aliased to either multi-user.target or graphical.target to select what is started by default. Alias names may be used in commands like disable, start, stop, status, and similar, and in all @@ -154,8 +154,12 @@ template instance (e.g. alias@inst.service) may be a symlink to different template (e.g. template@inst.service). In that case, just this specific instance is aliased, while other instances of the template (e.g. alias@foo.service, - alias@bar.service) are not aliased. Those rule preserve the requirement that the - instance (if any) is always uniquely defined for a given unit and all its aliases. + alias@bar.service) are not aliased. Those rules preserve the requirement that the + instance (if any) is always uniquely defined for a given unit and all its aliases. The target of alias + symlink must point to a valid unit file location, i.e. the symlink target name must match the symlink + source name as described, and the destination path must be in one of the unit search paths, see UNIT FILE + LOAD PATH section below for more details. Note that the target file may not exist, i.e. the symlink may + be dangling. Unit files may specify aliases through the Alias= directive in the [Install] section. When the unit is enabled, symlinks will be created for those names, and removed when the unit is @@ -175,11 +179,18 @@ exists for Requires= type dependencies as well, the directory suffix is .requires/ in this case. This functionality is useful to hook units into the start-up of other units, without having to modify their unit files. For details about the semantics of - Wants=, see below. The preferred way to create symlinks in the - .wants/ or .requires/ directory of a unit file is by embedding - the dependency in [Install] section of the target unit, and creating the symlink in the file system with - the enable or preset commands of - systemctl1. + Wants= and Requires=, see below. The preferred way to create + symlinks in the .wants/ or .requires/ directories is by + specifying the dependency in [Install] section of the target unit, and creating the symlink in the file + system with the enable or preset commands of + systemctl1. The + target can be a normal unit (either plain or a specific instance of a template unit). In case when the + source unit is a template, the target can also be a template, in which case the instance will be + "propagated" to the target unit to form a valid unit instance. The target of symlinks in + .wants/ or .requires/ must thus point to a valid unit file + location, i.e. the symlink target name must satisfy the described requirements, and the destination path + must be in one of the unit search paths, see UNIT FILE LOAD PATH section below for more details. Note + that the target file may not exist, i.e. the symlink may be dangling. Along with a unit file foo.service, a "drop-in" directory foo.service.d/ may exist. All files with the suffix @@ -427,11 +438,11 @@ $XDG_RUNTIME_DIR/systemd/user.control - /run/systemd/transient + $XDG_RUNTIME_DIR/systemd/transient Dynamic configuration for transient units - /run/systemd/generator.early + $XDG_RUNTIME_DIR/systemd/generator.early Generated units with high priority (see early-dir in systemd.generator7) @@ -501,13 +512,30 @@ systemd-analyze --user unit-paths - Moreover, additional units might be loaded into systemd from - directories not on the unit load path by creating a symlink pointing to a - unit file in the directories. You can use systemctl link - for this operation. See - systemctl1 - for its usage and precaution. - + Moreover, additional units might be loaded into systemd from directories not on the unit load path + by creating a symlink pointing to a unit file in the directories. You can use systemctl + link for this; see + systemctl1. The file + system where the linked unit files are located must be accessible when systemd is started (e.g. anything + underneath /home/ or /var/ is not allowed, unless those + directories are located on the root file system). + + It is important to distinguish "linked unit files" from "unit file aliases": any symlink where the + symlink target is within the unit load path becomes an alias: the source name and + the target file name must satisfy specific constraints listed above in the discussion of aliases, but the + symlink target doesn't have to exist, and in fact the symlink target path is not used, except to check + whether the target is within the unit load path. In constrast, a symlink which goes outside of the unit + load path signifies a linked unit file. The symlink is followed when loading the file, but the + destination name is otherwise unused (and may even not be a valid unit file name). For example, symlinks + /etc/systemd/system/alias1.serviceservice1.service, + /etc/systemd/system/alias2.service/usr/lib/systemd/service1.service, + /etc/systemd/system/alias3.service/etc/systemd/system/service1.service + are all valid aliases and service1.service will have + four names, even if the unit file is located at + /run/systemd/system/service1.service. In contrast, + a symlink /etc/systemd/system/link1.service../link1_service_file + means that link1.service is a "linked unit" and the contents of + /etc/systemd/link1_service_file provide its configuration. @@ -628,8 +656,8 @@ If this unit gets activated, the units listed will be activated as well. If one of the other units fails to activate, and an ordering dependency After= on the failing unit is set, this unit will not be started. Besides, with or without specifying - After=, this unit will be stopped if one of the other units is explicitly - stopped. + After=, this unit will be stopped (or restarted) if one of the other units is + explicitly stopped (or restarted). Often, it is a better choice to use Wants= instead of Requires= in order to achieve a system that is more robust when dealing with @@ -1876,34 +1904,31 @@ WantedBy= RequiredBy= - This option may be used more than once, or a - space-separated list of unit names may be given. A symbolic - link is created in the .wants/ or - .requires/ directory of each of the - listed units when this unit is installed by systemctl - enable. This has the effect that a dependency of - type Wants= or Requires= - is added from the listed unit to the current unit. The primary - result is that the current unit will be started when the - listed unit is started. See the description of - Wants= and Requires= in - the [Unit] section for details. + This option may be used more than once, or a space-separated list of unit names may + be given. A symbolic link is created in the .wants/ or + .requires/ directory of each of the listed units when this unit is installed by + systemctl enable. This has the effect of a dependency of type + Wants= or Requires= being added from the listed unit to the + current unit. The primary result is that the current unit will be started when the listed unit is + started, see the description of Wants= and Requires= in the + [Unit] section for details. - WantedBy=foo.service in a service - bar.service is mostly equivalent to - Alias=foo.service.wants/bar.service in the - same file. In case of template units, systemctl - enable must be called with an instance name, and - this instance will be added to the - .wants/ or - .requires/ list of the listed unit. E.g. - WantedBy=getty.target in a service - getty@.service will result in - systemctl enable getty@tty2.service - creating a - getty.target.wants/getty@tty2.service - link to getty@.service. - + In case of template units listing non template units, the listing unit must have + DefaultInstance= set, or systemctl enable must be called with + an instance name. The instance (default or specified) will be added to the + .wants/ or .requires/ list of the listed unit. For example, + WantedBy=getty.target in a service getty@.service will result + in systemctl enable getty@tty2.service creating a + getty.target.wants/getty@tty2.service link to + getty@.service. This also applies to listing specific instances of templated + units: this specific instance will gain the dependency. A template unit may also list a template + unit, in which case a generic dependency will be added where each instance of the listing unit will + have a dependency on an instance of the listed template with the same instance value. For example, + WantedBy=container@.target in a service monitor@.service will + result in systemctl enable monitor@.service creating a + container@.target.wants/monitor@.service link to + monitor@.service, which applies to all instances of + container@.target. @@ -1975,6 +2000,11 @@ Cache directory root This is either /var/cache (for the system manager) or the path $XDG_CACHE_HOME resolves to (for user managers). + + %d + Credentials directory + This is the value of the $CREDENTIALS_DIRECTORY environment variable if available. See section "Credentials" in systemd.exec5 for more information. + %E Configuration directory root @@ -2031,6 +2061,8 @@ Note that this setting is not influenced by the Us %l + Short host name The hostname of the running system at the point in time the unit configuration is loaded, truncated at the first dot to remove any domain component. @@ -2062,6 +2094,13 @@ Note that this setting is not influenced by the Us Unescaped prefix name Same as %p, but with escaping undone. + + + %R + Pretty host name + The pretty hostname of the running system at the point in time the unit configuration is loaded, as read from the PRETTY_HOSTNAME= field of /etc/machine-info. If not set, resolves to the short hostname. See machine-info5 for more information. + %s User shell @@ -2096,6 +2135,16 @@ Note that this setting is not influenced by the Us + + %y + The path to the fragment + This is the path where the main part of the unit file is located. For linked unit files, the real path outside of the unit search directories is used. For units that don't have a fragment file, this specifier will raise an error. + + + %Y + The directory of the fragment + This is the directory part of %y. + diff --git a/man/sysupdate.d.xml b/man/sysupdate.d.xml new file mode 100644 index 000000000..03d27b9fb --- /dev/null +++ b/man/sysupdate.d.xml @@ -0,0 +1,885 @@ + + + + + + + + sysupdate.d + systemd + + + + sysupdate.d + 5 + + + + sysupdate.d + Transfer Definition Files for Automatic Updates + + + + /etc/sysupdate.d/*.conf +/run/sysupdate.d/*.conf +/usr/lib/sysupdate.d/*.conf + + + + + Description + + sysupdate.d/*.conf files describe how specific resources on the local system + shall be updated from a remote source. Each such file defines one such transfer: typically a remote + HTTP/HTTPS resource as source; and a local file, directory or partition as target. This may be used as a + simple, automatic, atomic update mechanism for the OS itself, for containers, portable services or system + extension images — but in fact may be used to update any kind of file from a remote source. + + The + systemd-sysupdate8 + command reads these files and uses them to determine which local resources should be updated, and then + executes the update. + + Both the remote HTTP/HTTPS source and the local target typically exist in multiple, concurrent + versions, in order to implement flexible update schemes, e.g. A/B updating (or a superset thereof, + e.g. A/B/C, A/B/C/D, …). + + Each *.conf file defines one transfer, i.e. describes one resource to + update. Typically, multiple of these files (i.e. multiple of such transfers) are defined together, and + are bound together by a common version identifier in order to update multiple resources at once on each + update operation, for example to update a kernel, a root file system and a Verity partition in a single, + combined, synchronized operation, so that only a combined update of all three together constitutes a + complete update. + + Each *.conf file contains three sections: [Transfer], [Source] and [Target]. + + + + Basic Mode of Operation + + Disk-image based OS updates typically consist of multiple different resources that need to be + updated together, for example a secure OS update might consist of a root file system image to drop into a + partition, a matching Verity integrity data partition image, and a kernel image prepared to boot into the + combination of the two partitions. The first two resources are files that are downloaded and placed in a + disk partition, the latter is a file that is downloaded and placed in a regular file in the boot file + system (e.g. EFI system partition). Hence, during an update of a hypothetical operating system "foobarOS" + to a hypothetical version 47 the following operations should take place: + + + A file https://download.example.com/foobarOS_47.root.xz should be + downloaded, decompressed and written to a previously unused partition with GPT partition type UUID + 4f68bce3-e8cd-4db1-96e7-fbcaf984b709 for x86-64, as per Discoverable Partitions + Specification. + + Similarly, a file https://download.example.com/foobarOS_47.verity.xz + should be downloaded, decompressed and written to a previously empty partition with GPT partition type + UUID of 2c7357ed-ebd2-46d9-aec1-23d437ec2bf5 (i.e the partition type for Verity integrity information + for x86-64 root file systems). + + Finally, a file https://download.example.com/foobarOS_47.efi.xz (a + unified kernel, as per Boot Loader + Specification Type #2) should be downloaded, decompressed and written to the ESP file system, + i.e. to EFI/Linux/foobarOS_47.efi in the ESP. + + + The version-independent generalization of this would be (using the special marker + @v as wildcard for the version identifier): + + + A transfer of a file https://download.example.com/foobarOS_@v.root.xz + → a local, previously empty GPT partition of type 4f68bce3-e8cd-4db1-96e7-fbcaf984b709, with the label to + be set to foobarOS_@v. + + A transfer of a file https://download.example.com/foobarOS_@v.verity.xz + → a local, previously empty GPT partition of type 2c7357ed-ebd2-46d9-aec1-23d437ec2bf5, with the label to be + set to foobarOS_@v_verity. + + A transfer of a file https://download.example.com/foobarOS_@v.efi.xz + → a local file /efi/EFI/Linux/foobarOS_@v.efi. + + + An update can only complete if the relevant URLs provide their resources for the same version, + i.e. for the same value of @v. + + The above may be translated into three *.conf files in + sysupdate.d/, one for each resource to transfer. The *.conf + files configure the type of download, and what place to write the download to (i.e. whether to a + partition or a file in the file system). Most importantly these files contain the URL, partition name and + filename patterns shown above that describe how these resources are called on the source and how they + shall be called on the target. + + In order to enumerate available versions and figuring out candidates to update to, a mechanism is + necessary to list suitable files: + + + For partitions: the surrounding GPT partition table contains a list of defined + partitions, including a partition type UUID and a partition label (in this scheme the partition label + plays a role for the partition similar to the filename for a regular file) + + For regular files: the directory listing of the directory the files are contained in + provides a list of existing files in a straightforward way. + + For HTTP/HTTPS sources a simple scheme is used: a manifest file + SHA256SUMS, following the format defined by sha256sum1, + lists file names and their SHA256 hashes. + + + Transfers are done in the alphabetical order of the .conf file names they are + defined in. First, the resource data is downloaded directly into a target file/directory/partition. Once + this is completed for all defined transfers, in a second step the files/directories/partitions are + renamed to their final names as defined by the target MatchPattern=, again in the + order the .conf transfer file names dictate. This step is not atomic, however it is + guaranteed to be executed strictly in order with suitable disk synchronization in place. Typically, when + updating an OS one of the transfers defines the entry point when booting. Thus it is generally a good idea + to order the resources via the transfer configuration file names so that the entry point is written + last, ensuring that any abnormal termination does not leave an entry point around whose backing is not + established yet. In the example above it would hence make sense to establish the EFI kernel image last + and thus give its transfer configuration file the alphabetically last name. + + See below for an extended, more specific example based on the above. + + + + Resource Types + + Each transfer file defines one source resource to transfer to one target resource. The following + resource types are supported: + + + + Resources of type url-file encapsulate a file on a web server, + referenced via a HTTP or HTTPS URL. When an update takes place, the file is downloaded and decompressed + and then written to the target file or partition. This resource type is only available for sources, not + for targets. The list of available versions of resources of this type is encoded in + SHA256SUMS manifest files, accompanied by + SHA256SUMS.gpg detached signatures. + + The url-tar resource type is similar, but the file must be a + .tar archive. When an update takes place, the file is decompressed and unpacked + into a directory or btrfs subvolume. This resource type is only available for sources, not for + targets. Just like url-file, url-tar version enumeration makes + use of SHA256SUMS files, authenticated via + SHA256SUMS.gpg. + + The regular-file resource type encapsulates a local regular file on + disk. During updates the file is uncompressed and written to the target file or partition. This + resource type is available both as source and as target. When updating no integrity or authentication + verification is done for resources of this type. + + The partition resource type is similar to + regular-file, and encapsulates a GPT partition on disk. When updating, the partition + must exist already, and have the correct GPT partition type. A partition whose GPT partition label is + set to _empty is considered empty, and a candidate to place a newly downloaded + resource in. The GPT partition label is used to store version information, once a partition is + updated. This resource type is only available for target resources. + + The tar resource type encapsulates local .tar + archive files. When an update takes place, the files are uncompressed and unpacked into a target + directory or btrfs subvolume. Behaviour of tar and url-tar is + generally similar, but the latter downloads from remote sources, and does integrity and authentication + checks while the former does not. The tar resource type is only available for source + resources. + + The directory resource type encapsulates local directory trees. This + type is available both for source and target resources. If an update takes place on a source resource + of this type, a recursive copy of the directory is done. + + The subvolume resource type is identical to + directory, except when used as the target, in which case the file tree is placed in + a btrfs subvolume instead of a plain directory, if the backing file system supports it (i.e. is + btrfs). + + + As already indicated, only a subset of source and target resource type combinations are + supported: + + + Resource Types + + + + + + + + Identifier + Description + Usable as Source + When Used as Source: Compatible Targets + When Used as Source: Integrity + Authentication + When Used as Source: Decompression + Usable as Target + When Used as Target: Compatible Sources + + + + + + url-file + HTTP/HTTPS files + yes + regular-file, partition + yes + yes + no + - + + + + url-tar + HTTP/HTTPS .tar archives + yes + directory, subvolume + yes + yes + no + - + + + + regular-file + Local files + yes + regular-file, partition + no + yes + yes + url-file, regular-file + + + + partition + Local GPT partitions + no + - + - + - + yes + url-file, regular-file + + + + tar + Local .tar archives + yes + directory, subvolume + no + yes + no + - + + + + directory + Local directories + yes + directory, subvolume + no + no + yes + url-tar, tar, directory, subvolume + + + + subvolume + Local btrfs subvolumes + yes + directory, subvolume + no + no + yes + url-tar, tar, directory, subvolume + + + + +
+
+ + + Match Patterns + + Both the source and target resources typically exist in multiple versions concurrently. An update + operation is done whenever the newest of the source versions is newer than the newest of the target + versions. To determine the newest version of the resources a directory listing, partition listing or + manifest listing is used, a subset of qualifying entries selected from that, and the version identifier + extracted from the file names or partition labels of these selected entries. Subset selection and + extraction of the version identifier (plus potentially other metadata) is done via match patterns, + configured in MatchPattern= in the [Source] and [Target] sections. These patterns are + strings that describe how files or partitions are named, with named wildcards for specific fields such as + the version identifier. The following wildcards are defined: + + + Match Pattern Wildcards + + + + + + + + Wildcard + Description + Format + Notes + + + + + + @v + Version identifier + Valid version string + Mandatory + + + + @u + GPT partition UUID + Valid 128-Bit UUID string + Only relevant if target resource type chosen as partition + + + + @f + GPT partition flags + Formatted hexadecimal integer + Only relevant if target resource type chosen as partition + + + + @a + GPT partition flag NoAuto + Either 0 or 1 + Controls NoAuto bit of the GPT partition flags, as per Discoverable Partitions Specification; only relevant if target resource type chosen as partition + + + + @g + GPT partition flag GrowFileSystem + Either 0 or 1 + Controls GrowFileSystem bit of the GPT partition flags, as per Discoverable Partitions Specification; only relevant if target resource type chosen as partition + + + + @r + Read-only flag + Either 0 or 1 + Controls ReadOnly bit of the GPT partition flags, as per Discoverable Partitions Specification and other output read-only flags, see ReadOnly= below. + + + + @t + File modification time + Formatted decimal integer, µs since UNIX epoch Jan 1st 1970 + Only relevant if target resource type chosen as regular-file + + + + @m + File access mode + Formatted octal integer, in UNIX fashion + Only relevant if target resource type chosen as regular-file + + + + @s + File size after decompression + Formatted decimal integer + Useful for measuring progress and to improve partition allocation logic + + + + @d + Tries done + Formatted decimal integer + Useful when operating with kernel image files, as per Automatic Boot Assessment + + + + @l + Tries left + Formatted decimal integer + Useful when operating with kernel images, as per Automatic Boot Assessment + + + + @h + SHA256 hash of compressed file + 64 hexadecimal characters + The SHA256 hash of the compressed file; not useful for url-file or url-tar where the SHA256 hash is already included in the manifest file anyway. + + + +
+ + Of these wildcards only @v must be present in a valid pattern, all other + wildcards are optional. Each wildcard may be used at most once in each pattern. A typical wildcard + matching a file system source image could be MatchPattern=foobar_@v.raw.xz, i.e. any file + whose name begins with foobar_, followed by a version ID and suffixed by + .raw.xz. + + Do not confuse the @ pattern matching wildcard prefix with the + % specifier expansion prefix. The former encapsulate a variable part of a match + pattern string, the latter are simple shortcuts that are expanded while the drop-in files are + parsed. For details about specifiers, see below. +
+ + + [Transfer] Section Options + + This section defines general properties of this transfer. + + + + MinVersion= + + Specifies the minimum version to require for this transfer to take place. If the + source or target patterns in this transfer definition match files older than this version they will + be considered obsolete, and never be considered for the update operation. + + + + ProtectVersion= + + Takes one or more version strings to mark as "protected". Protected versions are + never removed while making room for new, updated versions. This is useful to ensure that the + currently booted OS version (or auxiliary resources associated with it) is not replaced/overwritten + during updates, in order to avoid runtime file system corruptions. + + Like many of the settings in these configuration files this setting supports specifier + expansion. It's particularly useful to set this setting to one of the %A, + %B or %w specifiers to automatically refer to the current OS + version of the running system. See below for details on supported specifiers. + + + + Verify= + + Takes a boolean, defaults to yes. Controls whether to cryptographically verify + downloaded resources (specifically: validate the GPG signatures for downloaded + SHA256SUMS manifest files, via their detached signature files + SHA256SUMS.gpg in combination with the system keyring + /usr/lib/systemd/import-pubring.gpg or + /etc/systemd/import-pubring.gpg). + + This option is essential to provide integrity guarantees for downloaded resources and thus + should be left enabled, outside of test environments. + + Note that the downloaded payload files are unconditionally checked against the SHA256 hashes + listed in the manifest. This option only controls whether the signatures of these manifests are + verified. + + This option only has an effect if the source resource type is selected as + url-file or url-tar, as integrity and authentication + checking is only available for transfers from remote sources. + + + + + + + [Source] Section Options + + This section defines properties of the transfer source: + + + + Type= + + Specifies the resource type of the source for the transfer. Takes one of + url-file, url-tar, tar, + regular-file, directory or + subvolume. For details about the resource types, see above. This option is + mandatory. + + Note that only some combinations of source and target resource types are supported, see + above. + + + + + + Path= + + Specifies where to find source versions of this resource. + + If the source type is selected as url-file or + url-tar this must be a HTTP/HTTPS URL. The URL is suffixed with + /SHA256SUMS to acquire the manifest file, with + /SHA256SUMS.gpg to acquire the detached signature file for it, and with the file + names listed in the manifest file in case an update is executed and a resource shall be + downloaded. + + For all other source resource types this must be a local path in the file system, referring to + a local directory to find the versions of this resource in. + + + + MatchPattern= + + Specifies one or more file name match patterns that select the subset of files that + are update candidates as source for this transfer. See above for details on match patterns. + + This option is mandatory. Any pattern listed must contain at least the @v + wildcard, so that a version identifier may be extracted from the filename. All other wildcards are + optional. + + + + + + [Target] Section Options + + This section defines properties of the transfer target: + + + + Type= + + Specifies the resource type of the target for the transfer. Takes one of + partition, regular-file, directory or + subvolume. For details about the resource types, see above. This option is + mandatory. + + Note that only some combinations of source and target resource types are supported, see above. + + + + Path= + + Specifies a file system path where to look for already installed versions or place + newly downloaded versions of this configured resource. If Type= is set to + partition, expects a path to a (whole) block device node, or the special string + auto in which case the block device the root file system of the currently booted + system is automatically determined and used. If Type= is set to + regular-file, directory or subvolume, + must refer to a path in the local file system referencing the directory to find or place the version + files or directories under. + + Note that this mechanism cannot be used to create or remove partitions, in case + Type= is set to partition. Partitions must exist already, and + a special partition label _empty is used to indicate empty partitions. To + automatically generate suitable partitions on first boot, use a tool such as + systemd-repart8. + + + + MatchPattern= + + Specifies one or more file name or partition label match patterns that select the + subset of files or partitions that are update candidates as targets for this transfer. See above for + details on match patterns. + + This option is mandatory. Any pattern listed must contain at least the @v + wildcard, so that a version identifier may be extracted from the filename. All other wildcards are + optional. + + This pattern is both used for matching existing installed versions and for determining the name + of new versions to install. If multiple patterns are specified, the first specified is used for + naming newly installed versions. + + + + MatchPartitionType= + + When the target Type= is chosen as partition, + specifies the GPT partition type to look for. Only partitions of this type are considered, all other + partitions are ignored. If not specified, the GPT partition type linux-generic + is used. Accepts either a literal type UUID or a symbolic type identifier. For a list of supported + type identifiers, see the Type= setting in + repart.d5. + + + + PartitionUUID= + PartitionFlags= + PartitionNoAuto= + PartitionGrowFileSystem= + + When the target Type= is picked as partition, + selects the GPT partition UUID and partition flags to use for the updated partition. Expects a valid + UUID string, a hexadecimal integer, or booleans, respectively. If not set, but the source match + pattern includes wildcards for these fields (i.e. @u, @f, + @a, or @g), the values from the patterns are used. If neither + configured with wildcards or these explicit settings, the values are left untouched. If both the + overall PartitionFlags= flags setting and the individual flag settings + PartitionNoAuto= and PartitionGrowFileSystem= are used (or the + wildcards for them), then the latter override the former, i.e. the individual flag bit overrides the + overall flags value. See Discoverable + Partitions Specification for details about these flags. + + Note that these settings are not used for matching, they only have effect on newly written + partitions in case a transfer takes place. + + + + ReadOnly= + + Controls whether to mark the resulting file, subvolume or partition read-only. If the + target type is partition this controls the ReadOnly partition flag, as per + Discoverable Partitions + Specification, similar to the PartitionNoAuto= and + PartitionGrowFileSystem= flags described above. If the target type is + regular-file, the writable bit is removed from the access mode. If the the + target type is subvolume, the subvolume will be marked read-only as a + whole. Finally, if the target Type= is selected as directory, + the "immutable" file attribute is set, see chattr1 for + details. + + + + Mode= + + The UNIX file access mode to use for newly created files in case the target resource + type is picked as regular-file. Expects an octal integer, in typical UNIX + fashion. If not set, but the source match pattern includes a wildcard for this field + (i.e. @t), the value from the pattern is used. + + Note that this setting is not used for matching, it only has an effect on newly written + files when a transfer takes place. + + + + TriesDone= + TriesLeft= + + These options take positive, decimal integers, and control the number of attempts + done and left for this file. These settings are useful for managing kernel images, following the + scheme defined in Automatic Boot + Assessment, and only have an effect if the target pattern includes the @d + or @l wildcards. + + + + InstancesMax= + + Takes a decimal integer equal to or greater than 2. This configures how many concurrent + versions of the resource to keep. Whenever a new update is initiated it is made sure that no more + than the number of versions specified here minus one exist in the target. Any excess versions are + deleted (in case the target Type= of regular-file, + directory, subvolume is used) or emptied (in case the + target Type= of partition is used; emptying in this case + simply means to set the partition label to the special string _empty; note that no + partitions are actually removed). After an update is completed the number of concurrent versions of + the target resources is equal to or below the number specified here. + + Note that this setting may be set differently for each transfer. However, it generally is + advisable to keep this setting the same for all transfers, since otherwise incomplete combinations of + files or partitions will be left installed. + + If the target Type= is selected as partition, the number + of concurrent versions to keep is additionally restricted by the number of partition slots of the + right type in the partition table. i.e. if there are only 2 partition slots for the selected + partition type, setting this value larger than 2 is without effect, since no more than 2 concurrent + versions could be stored in the image anyway. + + + + RemoveTemporary= + + Takes a boolean argument. If this option is enabled (which is the default) before + initiating an update, all left-over, incomplete updates from a previous attempt are removed from the + target directory. This only has an effect if the target resource Type= is selected + as regular-file, directory or + subvolume. + + + + CurrentSymlink= + + Takes a symlink name as argument. If this option is used, as the last step of the + update a symlink under the specified name is created/updated pointing to the completed update. This + is useful in to provide a stable name always pointing to the newest version of the resource. This is + only supported if the target resource Type= is selected as + regular-file, directory or + subvolume. + + + + + + + Specifiers + + Specifiers may be used in the MinVersion=, ProtectVersion=, + Path=, MatchPattern= and CurrentSymlink= + settings. The following expansions are understood: + + Specifiers available + + + + + + + Specifier + Meaning + Details + + + + + + + + + + + + + + + + + + + + +
+ + Do not confuse the % specifier expansion prefix with the @ + pattern matching wildcard prefix. The former are simple shortcuts that are expanded while the drop-in + files are parsed, the latter encapsulate a variable part of a match pattern string. For details about + pattern matching wildcards, see above. +
+ + + Examples + + + Updates for a Verity Enabled Secure OS + + With the following three files we define a root file system partition, a matching Verity + partition, and a unified kernel image to update as one. This example is an extension of the example + discussed earlier in this man page. + + # /usr/lib/sysupdate.d/50-verity.conf +[Transfer] +ProtectVersion=%A + +[Source] +Type=url-file +Path=https://download.example.com/ +MatchPattern=foobarOS_@v_@u.verity.xz + +[Target] +Type=partition +Path=auto +MatchPattern=foobarOS_@v_verity +MatchPartitionType=root-verity +PartitionFlags=0 +PartitionReadOnly=1 + + The above defines the update mechanism for the Verity partition of the root file system. Verity + partition images are downloaded from + https://download.example.com/foobarOS_@v_@u.verity.xz and written to a suitable + local partition, which is marked read-only. Under the assumption this update is run from the image + itself the current image version (i.e. the %A specifier) is marked as protected, to + ensure it is not corrupted while booted. Note that the partition UUID for the target partition is + encoded in the source file name. Fixating the partition UUID can be useful to ensure that + roothash= on the kernel command line is sufficient to pinpoint both the Verity and + root file system partition, and also encode the Verity root level hash (under the assumption the UUID + in the file names match their top-level hash, the way + systemd-gpt-auto-generator8 + suggests). + + # /usr/lib/sysupdate.d/60-root.conf +[Transfer] +ProtectVersion=%A + +[Source] +Type=url-file +Path=https://download.example.com/ +MatchPattern=foobarOS_@v_@u.root.xz + +[Target] +Type=partition +Path=auto +MatchPattern=foobarOS_@v +MatchPartitionType=root +PartitionFlags=0 +PartitionReadOnly=1 + + The above defines a matching transfer definition for the root file system. + + # /usr/lib/sysupdate.d/70-kernel.conf +[Transfer] +ProtectVersion=%A + +[Source] +Type=url-file +Path=https://download.example.com/ +MatchPattern=foobarOS_@v.efi.xz + +[Target] +Type=file +Path=/efi/EFI/Linux +MatchPattern=foobarOS_@v+@l-@d.efi \ + foobarOS_@v+@l.efi \ + foobarOS_@v.efi +Mode=0444 +TriesLeft=3 +TriesDone=0 +InstancesMax=2 + + The above installs a unified kernel image into the ESP (which is mounted to + /efi/), as per Boot + Loader Specification Type #2. This defines three possible patterns for the names of the + kernel images images, as per Automatic Boot + Assessment, and ensures when installing new kernels, they are set up with 3 tries left. No + more than two parallel kernels are kept. + + With this setup the web server would serve the following files, for a hypothetical version 7 of + the OS: + + + SHA256SUMS – The manifest file, containing available files and their SHA256 hashes + SHA256SUMS.gpg – The detached cryptographic signature for the manifest file + foobarOS_7_8b8186b1-2b4e-4eb6-ad39-8d4d18d2a8fb.verity.xz – The Verity image for version 7 + foobarOS_7_f4d1234f-3ebf-47c4-b31d-4052982f9a2f.root.xz – The root file system image for version 7 + foobarOS_7_efi.xz – The unified kernel image for version 7 + + + For each new OS release a new set of the latter three files would be added, each time with an + updated version. The SHA256SUMS manifest should then be updated accordingly, + listing all files for all versions that shall be offered for download. + + + + Updates for Plain Directory Container Image + + +[Source] +Type=url-tar +Path=https://download.example.com/ +MatchPattern=myContainer_@v.tar.gz + +[Target] +Type=subvolume +Path=/var/lib/machines +MatchPattern=myContainer_@v +CurrentSymlink=myContainer + + On updates this downloads https://download.example.com/myContainer_@v.tar.gz + and decompresses/unpacks it to /var/lib/machines/myContainer_@v. After each update + a symlink /var/lib/machines/myContainer is created/updated always pointing to the + most recent update. + + + + + See Also + + systemd1, + systemd-sysupdate8, + systemd-repart8 + + + +
diff --git a/man/udev.xml b/man/udev.xml index f6ea2abc1..886455f61 100644 --- a/man/udev.xml +++ b/man/udev.xml @@ -669,7 +669,7 @@ log level is applied when the line including this rule is processed. So, for debugging, it is recommended that this is specified at earlier place, e.g., the first line of 00-debug.rules.
- Example for debugging uevent processing for network interfaces. + Example for debugging uevent processing for network interfaces: # /etc/udev/rules.d/00-debug-net.rules SUBSYSTEM=="net", OPTIONS="log_level=debug" diff --git a/man/udevadm.xml b/man/udevadm.xml index 6091a3421..af4857112 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -280,9 +280,9 @@ - Trigger a specific type of devices. Valid types are: - devices, subsystems. - The default value is devices. + Trigger a specific type of devices. Valid types are all, + devices, and subsystems. The default value is + devices. @@ -297,6 +297,19 @@ + + + + Takes a comma separated list of subsystems. When triggering events for devices, the + devices from the specified subsystems and their parents are triggered first. For example, + if , then firstly all block devices and + their parents are triggered, in the next all network devices and their parents are + triggered, and lastly the other devices are triggered. This option can be specified + multiple times, and in that case the lists of the subsystems will be merged. That is, + is equivalent to + . + + @@ -382,6 +395,35 @@ then each matching result is ORed, that is, all children of each specified device are triggered. + + + + + When is specified, trigger events for devices + that are already initialized by systemd-udevd, and skip devices that + are not initialized yet. + When is specified, trigger events for devices + that are not initialized by systemd-udevd yet, and skip devices that + are already initialized. + Here, initialized devices are those for which at least one udev rule already + completed execution – for any action but remove — that set a property + or other device setting (and thus has an entry in the udev device database). Devices are + no longer considered initialized if a remove action is seen for them + (which removes their entry in the udev device database). Note that devices that have no + udev rules are never considered initialized, but might still be announced via the sd-device + API (or similar). Typically, it is thus essential that applications which intend to use + such a match, make sure a suitable udev rule is installed that sets at least one property + on devices that shall be matched. + WARNING: can potentially save a significant + amount of time compared to re-triggering all devices in the system and e.g. can be used to + optimize boot time. However, this is not safe to be used in a boot sequence in general. + Especially, when udev rules for a device depend on its parent devices (e.g. + ATTRS or IMPORT{parent} keys, see + udev7 + for more details), the final state of the device becomes easily unstable with this option. + + + @@ -595,7 +637,7 @@ udevadm test <arg choice="opt"><replaceable>options</replaceable></arg> - <arg><replaceable>devpath</replaceable></arg> + <arg choice="opt"><replaceable>devpath</replaceable>|<replaceable>file</replaceable>|<replaceable>unit</replaceable></arg> Simulate a udev event run for the given device, and print debug output. @@ -631,7 +673,7 @@ udevadm test-builtin <arg choice="opt"><replaceable>options</replaceable></arg> <arg><replaceable>command</replaceable></arg> - <arg><replaceable>devpath</replaceable></arg> + <arg choice="opt"><replaceable>devpath</replaceable>|<replaceable>file</replaceable>|<replaceable>unit</replaceable></arg> Run a built-in command COMMAND for device DEVPATH, and print debug diff --git a/man/vconsole.conf.xml b/man/vconsole.conf.xml index 378812bd2..e4e2864ff 100644 --- a/man/vconsole.conf.xml +++ b/man/vconsole.conf.xml @@ -32,13 +32,13 @@ You can safely mask this file if you want to avoid this kind of initialization. - The basic file format of the - vconsole.conf is a newline-separated list of - environment-like shell-compatible variable assignments. It is - possible to source the configuration from shell scripts, however, - beyond mere variable assignments no shell features are supported, - allowing applications to read the file without implementing a - shell compatible execution engine. + The format of vconsole.conf is a newline-separated list of environment-like + shell-compatible variable assignments, ignoring comments and empty lines. It is possible to source the + configuration from shell scripts, however, beyond mere variable assignments no shell features are + supported, allowing applications to read the file without implementing a shell compatible execution + engine. See + os-release5 for a + detailed description of the format. Note that the kernel command line options vconsole.keymap=, diff --git a/meson.build b/meson.build index cb9936ee8..e68791b8b 100644 --- a/meson.build +++ b/meson.build @@ -1,10 +1,10 @@ # SPDX-License-Identifier: LGPL-2.1-or-later project('systemd', 'c', - version : '250', + version : '251', license : 'LGPLv2+', default_options: [ - 'c_std=gnu99', + 'c_std=gnu11', 'prefix=/usr', 'sysconfdir=/etc', 'localstatedir=/var', @@ -13,8 +13,8 @@ project('systemd', 'c', meson_version : '>= 0.53.2', ) -libsystemd_version = '0.33.0' -libudev_version = '1.7.3' +libsystemd_version = '0.34.0' +libudev_version = '1.7.4' conf = configuration_data() conf.set_quoted('PROJECT_URL', 'https://www.freedesktop.org/wiki/Software/systemd') @@ -40,9 +40,13 @@ if want_ossfuzz and want_libfuzzer error('only one of oss-fuzz or llvm-fuzz can be specified') endif -skip_deps = want_ossfuzz or want_libfuzzer +skip_deps = want_ossfuzz or get_option('skip-deps') fuzzer_build = want_ossfuzz or want_libfuzzer +# Create a title-less summary section early, so it ends up first in the output. +# More items are added later after they have been detected. +summary({'build mode' : get_option('mode')}) + ##################################################################### # Try to install the git pre-commit hook @@ -187,21 +191,6 @@ if docdir == '' docdir = datadir / 'doc/systemd' endif -dbuspolicydir = get_option('dbuspolicydir') -if dbuspolicydir == '' - dbuspolicydir = datadir / 'dbus-1/system.d' -endif - -dbussessionservicedir = get_option('dbussessionservicedir') -if dbussessionservicedir == '' - dbussessionservicedir = datadir / 'dbus-1/services' -endif - -dbussystemservicedir = get_option('dbussystemservicedir') -if dbussystemservicedir == '' - dbussystemservicedir = datadir / 'dbus-1/system-services' -endif - pamlibdir = get_option('pamlibdir') if pamlibdir == '' pamlibdir = rootlibdir / 'security' @@ -261,7 +250,6 @@ conf.set_quoted('SYSTEMD_LANGUAGE_FALLBACK_MAP', pkgdatadir / 'lang conf.set_quoted('SYSTEMD_MAKEFS_PATH', rootlibexecdir / 'systemd-makefs') conf.set_quoted('SYSTEMD_PULL_PATH', rootlibexecdir / 'systemd-pull') conf.set_quoted('SYSTEMD_SHUTDOWN_BINARY_PATH', rootlibexecdir / 'systemd-shutdown') -conf.set_quoted('SYSTEMD_STDIO_BRIDGE_BINARY_PATH', bindir / 'systemd-stdio-bridge') conf.set_quoted('SYSTEMD_TEST_DATA', testsdir / 'testdata') conf.set_quoted('SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH', rootbindir / 'systemd-tty-ask-password-agent') conf.set_quoted('SYSTEMD_UPDATE_HELPER_PATH', rootlibexecdir / 'systemd-update-helper') @@ -373,7 +361,7 @@ possible_common_cc_flags = [ '-Wno-string-plus-int', # clang ] -# Disable -Wmaybe-unitialized when compiling with -Os/-O1/-O3/etc. There are +# Disable -Wmaybe-uninitialized when compiling with -Os/-O1/-O3/etc. There are # too many false positives with gcc >= 8. Effectively, we only test with -O0 # and -O2; this should be enough to catch most important cases without too much # busywork. See https://github.com/systemd/systemd/pull/19226. @@ -595,6 +583,11 @@ endif versiondep = declare_dependency(sources: version_h) +shared_lib_tag = get_option('shared-lib-tag') +if shared_lib_tag == '' + shared_lib_tag = meson.project_version() +endif + sh = find_program('sh') echo = find_program('echo') sed = find_program('sed') @@ -606,10 +599,8 @@ env = find_program('env') perl = find_program('perl', required : false) rsync = find_program('rsync', required : false) meson_make_symlink = project_source_root + '/tools/meson-make-symlink.sh' -test_efi_create_disk_sh = find_program('test/test-efi-create-disk.sh') mkdir_p = 'mkdir -p $DESTDIR/@0@' -splash_bmp = files('test/splash.bmp') # If -Dxxx-path option is found, use that. Otherwise, check in $PATH, # /usr/sbin, /sbin, and fall back to the default from middle column. @@ -647,7 +638,8 @@ endif ############################################################ -if run_command('python3', '-c', 'import jinja2', check : false).returncode() != 0 +python = find_program('python3') +if run_command(python, '-c', 'import jinja2', check : false).returncode() != 0 error('python3 jinja2 missing') endif @@ -768,6 +760,10 @@ conf.set('TIME_EPOCH', time_epoch) conf.set('CLOCK_VALID_RANGE_USEC_MAX', get_option('clock-valid-range-usec-max')) +default_user_shell = get_option('default-user-shell') +conf.set_quoted('DEFAULT_USER_SHELL', default_user_shell) +conf.set_quoted('DEFAULT_USER_SHELL_NAME', fs.name(default_user_shell)) + foreach tuple : [['system-alloc-uid-min', 'SYS_UID_MIN', 1], # Also see login.defs(5). ['system-uid-max', 'SYS_UID_MAX', 999], ['system-alloc-gid-min', 'SYS_GID_MIN', 1], @@ -991,29 +987,66 @@ bpf_framework_required = want_bpf_framework == 'true' libbpf = dependency('libbpf', required : bpf_framework_required, version : '>= 0.2') conf.set10('HAVE_LIBBPF', libbpf.found()) -if want_bpf_framework == 'false' +if want_bpf_framework == 'false' or not libbpf.found() conf.set10('BPF_FRAMEWORK', 0) else # Support 'versioned' clang/llvm-strip binaries, as seen on Debian/Ubuntu # (like clang-10/llvm-strip-10) - clang_bin = cc.get_id() == 'clang' ? cc.cmd_array()[0] : 'clang' - if meson.is_cross_build() or clang_bin.contains('afl-clang') or clang_bin.contains('hfuzz-clang') - clang_bin = 'clang' - endif - clang = find_program(clang_bin, required : bpf_framework_required) - if not meson.is_cross_build() and clang.found() - llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip', - check : true).stdout().strip() + if meson.is_cross_build() or cc.get_id() != 'clang' or cc.cmd_array()[0].contains('afl-clang') or cc.cmd_array()[0].contains('hfuzz-clang') + r = find_program('clang', required : bpf_framework_required, version : '>= 10.0.0') + clang_found = r.found() + if clang_found + if meson.version().version_compare('>= 0.55') + clang = [r.full_path()] + else + clang = [r.path()] + endif + endif + # Assume that the required flags are supported by the found clang. + clang_supports_flags = clang_found else - llvm_strip_bin = 'llvm-strip' + clang_found = true + clang = cc.cmd_array() + clang_supports_flags = cc.has_argument('-Wno-compare-distinct-pointer-types') + endif + + if clang_found + # Check if 'clang -target bpf' is supported. + clang_supports_bpf = run_command(clang, '-target', 'bpf', '--print-supported-cpus', check : false).returncode() == 0 + else + clang_supports_bpf = false endif - llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required) # Debian installs this in /usr/sbin/ which is not in $PATH. # We check for 'bpftool' first, honouring $PATH, and in /usr/sbin/ for Debian. - bpftool = find_program('bpftool', '/usr/sbin/bpftool', required : bpf_framework_required) + # We use 'bpftool gen' subcommand, it was added by 985ead416df39d6fe8e89580cc1db6aa273e0175 (v5.6). + bpftool = find_program('bpftool', + '/usr/sbin/bpftool', + required : false, + version : '>= 5.13.0') + + if bpftool.found() + bpftool_strip = true + else + bpftool_strip = false + bpftool = find_program('bpftool', + '/usr/sbin/bpftool', + required : bpf_framework_required, + version : '>= 5.6.0') + endif + + if not bpftool_strip + if not meson.is_cross_build() and clang_found + llvm_strip_bin = run_command(clang, '--print-prog-name', 'llvm-strip', + check : true).stdout().strip() + else + llvm_strip_bin = 'llvm-strip' + endif + llvm_strip = find_program(llvm_strip_bin, required : bpf_framework_required, version : '>= 10.0.0') + endif + + deps_found = clang_found and clang_supports_bpf and clang_supports_flags and (bpftool_strip or llvm_strip.found()) and bpftool.found() - deps_found = libbpf.found() and clang.found() and llvm_strip.found() and bpftool.found() # Can build BPF program from source code in restricted C conf.set10('BPF_FRAMEWORK', deps_found) endif @@ -1473,6 +1506,59 @@ else endif conf.set10('HAVE_DBUS', have) +dbusdatadir = datadir / 'dbus-1' +if conf.get('HAVE_DBUS') == 1 + dbusdatadir = libdbus.get_variable(pkgconfig: 'datadir', default_value: datadir) / 'dbus-1' +endif + +dbuspolicydir = get_option('dbuspolicydir') +if dbuspolicydir == '' + dbuspolicydir = dbusdatadir / 'system.d' +endif + +dbussessionservicedir = get_option('dbussessionservicedir') +if dbussessionservicedir == '' + dbussessionservicedir = dbusdatadir / 'services' + if conf.get('HAVE_DBUS') == 1 + dbussessionservicedir = libdbus.get_variable(pkgconfig: 'session_bus_services_dir', default_value: dbussessionservicedir) + endif +endif + +dbussystemservicedir = get_option('dbussystemservicedir') +if dbussystemservicedir == '' + dbussystemservicedir = dbusdatadir / 'system-services' + if conf.get('HAVE_DBUS') == 1 + dbussystemservicedir = libdbus.get_variable(pkgconfig: 'system_bus_services_dir', default_value: dbussystemservicedir) + endif +endif + +dbus_interfaces_dir = get_option('dbus-interfaces-dir') +if dbus_interfaces_dir == '' or dbus_interfaces_dir == 'yes' + if meson.is_cross_build() and dbus_interfaces_dir != 'yes' + dbus_interfaces_dir = 'no' + warning('Exporting D-Bus interface XML files is disabled during cross build. Pass path or "yes" to force enable.') + else + dbus_interfaces_dir = dbusdatadir / 'interfaces' + if conf.get('HAVE_DBUS') == 1 + dbus_interfaces_dir = libdbus.get_variable(pkgconfig: 'interfaces_dir', default_value: dbus_interfaces_dir) + endif + endif +endif +if dbus_interfaces_dir == dbusdatadir / 'interfaces' or dbus_interfaces_dir == 'no' + dbus_interfaces_dir_name = 'interfaces' + dbus_interfaces_dir_parent = dbusdatadir +else + elements = dbus_interfaces_dir.split('/') + dbus_interfaces_dir_name = elements[-1] + dbus_interfaces_dir_parent = '/' + foreach elem : elements + if elem == dbus_interfaces_dir_name and dbus_interfaces_dir == dbus_interfaces_dir_parent / dbus_interfaces_dir_name + break + endif + dbus_interfaces_dir_parent = dbus_interfaces_dir_parent / elem + endforeach +endif + # We support one or the other. If gcrypt is available, we assume it's there to # be used, and use it in preference. opt = get_option('cryptolib') @@ -1563,6 +1649,18 @@ conf.set('DEFAULT_DNSSEC_MODE', 'DNSSEC_' + default_dnssec.underscorify().to_upper()) conf.set_quoted('DEFAULT_DNSSEC_MODE_STR', default_dnssec) +want_sysupdate = get_option('sysupdate') +if want_sysupdate != 'false' + have = (conf.get('HAVE_OPENSSL') == 1 and + conf.get('HAVE_LIBFDISK') == 1) + if want_sysupdate == 'true' and not have + error('sysupdate support was requested, but dependencies are not available') + endif +else + have = false +endif +conf.set10('ENABLE_SYSUPDATE', have) + want_importd = get_option('importd') if want_importd != 'false' have = (conf.get('HAVE_LIBCURL') == 1 and @@ -1694,6 +1792,7 @@ conf.set10('SYSTEMD_SLOW_TESTS_DEFAULT', slow_tests) tests = [] fuzzers = [] +catalogs = [] ############################################################ @@ -1709,6 +1808,7 @@ make_directive_index_py = find_program('tools/make-directive-index.py') make_man_index_py = find_program('tools/make-man-index.py') meson_render_jinja2 = find_program('tools/meson-render-jinja2.py') update_dbus_docs_py = find_program('tools/update-dbus-docs.py') +update_man_rules_py = find_program('tools/update-man-rules.py') update_hwdb_sh = find_program('tools/update-hwdb.sh') update_hwdb_autosuspend_sh = find_program('tools/update-hwdb-autosuspend.sh') update_syscall_tables_sh = find_program('tools/update-syscall-tables.sh') @@ -1731,17 +1831,6 @@ public_programs = [] # D-Bus introspection XML export dbus_programs = [] -dbus_interfaces_dir = get_option('dbus-interfaces-dir') -if dbus_interfaces_dir == '' - if not meson.is_cross_build() - dbus_interfaces_dir = datadir / 'dbus-1' - else - message('D-Bus interfaces export is disabled during cross build. Pass path or yes to force enable.') - dbus_interfaces_dir = 'no' - endif -elif dbus_interfaces_dir == 'yes' - dbus_interfaces_dir = datadir / 'dbus-1' -endif basic_includes = include_directories( 'src/basic', @@ -1935,6 +2024,7 @@ subdir('src/rpm') subdir('src/shutdown') subdir('src/sysext') subdir('src/systemctl') +subdir('src/sysupdate') subdir('src/timedate') subdir('src/timesync') subdir('src/tmpfiles') @@ -2012,7 +2102,8 @@ foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'], test('dlopen-nss_' + module, test_dlopen, # path to dlopen must include a slash - args : nss.full_path()) + args : nss.full_path(), + depends : nss) endif endif endforeach @@ -2026,12 +2117,7 @@ dbus_programs += executable( link_with : [libcore, libshared], dependencies : [versiondep, - threads, - librt, - libseccomp, - libselinux, - libmount, - libblkid], + libseccomp], install_rpath : rootlibexecdir, install : true, install_dir : rootlibexecdir) @@ -2047,12 +2133,7 @@ public_programs += executable( link_with : [libcore, libshared], dependencies : [versiondep, - threads, - librt, - libseccomp, - libselinux, - libmount, - libblkid], + libseccomp], install_rpath : rootlibexecdir, install : conf.get('ENABLE_ANALYZE')) @@ -2136,7 +2217,8 @@ if want_tests != 'false' test('test-fstab-generator', test_fstab_generator_sh, # https://github.com/mesonbuild/meson/issues/2681 - args : exe.full_path()) + args : exe.full_path(), + depends : exe) endif if conf.get('ENABLE_ENVIRONMENT_D') == 1 @@ -2287,7 +2369,8 @@ if conf.get('ENABLE_LOGIND') == 1 test('dlopen-pam_systemd', test_dlopen, # path to dlopen must include a slash - args : pam_systemd.full_path()) + args : pam_systemd.full_path(), + depends : pam_systemd) endif endif @@ -2367,7 +2450,7 @@ public_programs += executable( install_rpath : rootlibexecdir, install : true) -public_programs += executable( +systemctl = executable( 'systemctl', systemctl_sources, include_directories : includes, @@ -2381,6 +2464,7 @@ public_programs += executable( install_rpath : rootlibexecdir, install : true, install_dir : rootbindir) +public_programs += systemctl if conf.get('ENABLE_PORTABLED') == 1 dbus_programs += executable( @@ -2626,7 +2710,7 @@ if conf.get('HAVE_LIBCRYPTSETUP') == 1 endif if conf.get('HAVE_SYSV_COMPAT') == 1 - executable( + exe = executable( 'systemd-sysv-generator', 'src/sysv-generator/sysv-generator.c', include_directories : includes, @@ -2635,6 +2719,13 @@ if conf.get('HAVE_SYSV_COMPAT') == 1 install : true, install_dir : systemgeneratordir) + sysv_generator_test_py = find_program('test/sysv-generator-test.py') + if want_tests != 'false' + test('sysv-generator-test', + sysv_generator_test_py, + depends : exe) + endif + executable( 'systemd-rc-local-generator', 'src/rc-local-generator/rc-local-generator.c', @@ -2961,8 +3052,7 @@ if conf.get('ENABLE_OOMD') == 1 link_with : [libshared], dependencies : [], install_rpath : rootlibexecdir, - install : true, - install_dir : rootbindir) + install : true) endif if conf.get('ENABLE_BINFMT') == 1 @@ -3004,6 +3094,22 @@ if conf.get('ENABLE_REPART') == 1 endif endif +if conf.get('ENABLE_SYSUPDATE') == 1 + exe = executable( + 'systemd-sysupdate', + systemd_sysupdate_sources, + include_directories : includes, + link_with : [libshared], + dependencies : [threads, + libblkid, + libfdisk, + libopenssl], + install_rpath : rootlibexecdir, + install : true, + install_dir : rootlibexecdir) + public_programs += exe +endif + if conf.get('ENABLE_VCONSOLE') == 1 executable( 'systemd-vconsole-setup', @@ -3177,13 +3283,22 @@ executable( install : true, install_dir : rootlibexecdir) -public_programs += executable( +systemd_id128 = executable( 'systemd-id128', 'src/id128/id128.c', include_directories : includes, link_with : [libshared], install_rpath : rootlibexecdir, install : true) +public_programs += systemd_id128 + +if want_tests != 'false' + test('test-systemctl-enable', + test_systemctl_enable_sh, + # https://github.com/mesonbuild/meson/issues/2681 + args : [systemctl.full_path(), + systemd_id128.full_path()]) +endif public_programs += executable( 'systemd-path', @@ -3279,6 +3394,7 @@ public_programs += executable( busctl_sources, include_directories : includes, link_with : [libshared], + dependencies : [versiondep], install_rpath : rootlibexecdir, install : true) @@ -3518,7 +3634,8 @@ if want_tests != 'false' test('test-network-generator-conversion', test_network_generator_conversion_sh, # https://github.com/mesonbuild/meson/issues/2681 - args : exe.full_path()) + args : exe.full_path(), + depends : exe) endif executable( @@ -3532,13 +3649,14 @@ executable( ############################################################ -custom_target( +runtest_env = custom_target( 'systemd-runtest.env', output : 'systemd-runtest.env', command : [sh, '-c', '{ echo SYSTEMD_TEST_DATA=@0@; echo SYSTEMD_CATALOG_DIR=@1@; } >@OUTPUT@'.format( project_source_root / 'test', project_build_root / 'catalog')], + depends : catalogs, build_by_default : true) test_cflags = ['-DTEST_CODE=1'] @@ -3562,7 +3680,8 @@ foreach tuple : tests parallel = tuple.length() > 7 ? tuple[7] : true timeout = 30 - name = sources[0].split('/')[-1].split('.')[0] + # FIXME: Use fs.stem() with meson >= 0.54.0 + name = '@0@'.format(sources[0]).split('/')[-1].split('.')[0] if type.startswith('timeout=') timeout = type.split('=')[1].to_int() type = '' @@ -3580,7 +3699,8 @@ foreach tuple : tests build_by_default : want_tests != 'false', install_rpath : rootlibexecdir, install : install_tests, - install_dir : testsdir / type) + install_dir : testsdir / type, + link_depends : runtest_env) if type == 'manual' message('@0@ is a manual test'.format(name)) @@ -3672,7 +3792,8 @@ foreach tuple : fuzzers sources += 'src/fuzz/fuzz-main.c' endif - name = sources[0].split('/')[-1].split('.')[0] + # FIXME: Use fs.stem() with meson >= 0.54.0 + name = '@0@'.format(sources[0]).split('/')[-1].split('.')[0] exe = executable( name, @@ -3722,6 +3843,7 @@ subdir('docs/var-log') install_subdir('factory/etc', install_dir : factorydir) +subdir('factory/templates') if install_sysconfdir install_data('xorg/50-systemd-user.sh', @@ -3771,7 +3893,8 @@ foreach exec : public_programs if want_tests != 'false' test('check-help-' + name, check_help, - args : exec.full_path()) + args : exec.full_path(), + depends: exec) endif endforeach @@ -3848,7 +3971,7 @@ if git.found() command : [env, 'etags', '-o', '@0@/TAGS'.format(project_source_root)] + all_files) run_target( 'ctags', - command : [env, 'ctags', '-o', '@0@/tags'.format(project_source_root)] + all_files) + command : [env, 'ctags', '--tag-relative=never', '-o', '@0@/tags'.format(project_source_root)] + all_files) endif endif @@ -3892,9 +4015,9 @@ alias_target('update-man-rules', update_man_rules) if not meson.is_cross_build() custom_target( 'export-dbus-interfaces', - output : 'interfaces', + output : dbus_interfaces_dir_name, install : dbus_interfaces_dir != 'no', - install_dir : dbus_interfaces_dir, + install_dir : dbus_interfaces_dir_parent, command : [export_dbus_interfaces_py, '@OUTPUT@', dbus_programs]) endif @@ -3904,7 +4027,6 @@ alt_time_epoch = run_command('date', '-Is', '-u', '-d', '@@0@'.format(time_epoch check : true).stdout().strip() summary({ - 'build mode' : get_option('mode'), 'split /usr' : split_usr, 'split bin-sbin' : split_bin, 'prefix directory' : prefixdir, @@ -3923,8 +4045,10 @@ summary({ 'D-Bus policy directory' : dbuspolicydir, 'D-Bus session directory' : dbussessionservicedir, 'D-Bus system directory' : dbussystemservicedir, + 'D-Bus interfaces directory' : dbus_interfaces_dir, 'bash completions directory' : bashcompletiondir, 'zsh completions directory' : zshcompletiondir, + 'private shared lib version tag' : shared_lib_tag, 'extra start script' : get_option('rc-local'), 'debug shell' : '@0@ @ @1@'.format(get_option('debug-shell'), get_option('debug-tty')), @@ -3962,17 +4086,6 @@ summary({ # CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS} # LDFLAGS: ${OUR_LDFLAGS} ${LDFLAGS} -if conf.get('ENABLE_EFI') == 1 and conf.get('HAVE_GNU_EFI') == 1 - summary({ - 'EFI machine type' : efi_arch[0], - 'EFI CC' : '@0@'.format(' '.join(efi_cc)), - 'EFI LD' : efi_ld, - 'EFI lds' : efi_lds, - 'EFI crt0' : efi_crt0, - 'EFI include directory' : efi_incdir}, - section : 'Extensible Firmware Interface') -endif - found = [] missing = [] @@ -4049,6 +4162,7 @@ foreach tuple : [ ['rfkill'], ['sysext'], ['systemd-analyze', conf.get('ENABLE_ANALYZE') == 1], + ['sysupdate'], ['sysusers'], ['timedated'], ['timesyncd'], diff --git a/meson_options.txt b/meson_options.txt index 401f0933d..430b03d2b 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -3,6 +3,8 @@ option('version-tag', type : 'string', description : 'override the git version string') +option('shared-lib-tag', type : 'string', + description : 'override the private shared library version tag (defaults to project version)') option('mode', type : 'combo', choices : ['developer', 'release'], description : 'autoenable features suitable for systemd development/release builds') @@ -98,6 +100,8 @@ option('binfmt', type : 'boolean', description : 'support for custom binary formats') option('repart', type : 'combo', choices : ['auto', 'true', 'false'], description : 'install the systemd-repart tool') +option('sysupdate', type : 'combo', choices : ['auto', 'true', 'false'], + description : 'install the systemd-sysupdate tool') option('coredump', type : 'boolean', description : 'install the coredump handler') option('pstore', type : 'boolean', @@ -177,6 +181,8 @@ option('dbussessionservicedir', type : 'string', description : 'D-Bus session service directory') option('dbussystemservicedir', type : 'string', description : 'D-Bus system service directory') +option('dbus-interfaces-dir', type : 'string', + description : 'export D-Bus introspection XML as standalone files') option('pkgconfigdatadir', type : 'string', value : '', description : 'directory for arch-independent pkg-config files') option('pkgconfiglibdir', type : 'string', value : '', @@ -214,6 +220,8 @@ option('time-epoch', type : 'integer', value : '-1', description : 'time epoch for time clients') option('clock-valid-range-usec-max', type : 'integer', value : '473364000000000', # 15 years description : 'maximum value in microseconds for the difference between RTC and epoch, exceeding which is considered an RTC error') +option('default-user-shell', type : 'string', value : '/bin/bash', + description : 'default interactive shell') option('system-alloc-uid-min', type : 'integer', value : '-1', description : 'minimum system UID used when allocating') @@ -414,8 +422,6 @@ option('dbus', type : 'combo', choices : ['auto', 'true', 'false'], option('gnu-efi', type : 'combo', choices : ['auto', 'true', 'false'], description : 'gnu-efi support for sd-boot') -option('efi-cc', type : 'array', - description : 'the compiler to use for EFI modules') # Note that LLD does not support PE/COFF relocations # https://lists.llvm.org/pipermail/llvm-dev/2021-March/149234.html option('efi-ld', type : 'combo', choices : ['auto', 'bfd', 'gold'], @@ -424,6 +430,8 @@ option('efi-libdir', type : 'string', description : 'path to the EFI lib directory') option('efi-includedir', type : 'string', value : '/usr/include/efi', description : 'path to the EFI header directory') +option('efi-tpm-pcr-compat', type : 'boolean', value : 'false', + description : 'Measure kernel command line also into TPM PCR 8 (in addition to 12)') option('sbat-distro', type : 'string', value : 'auto', description : 'SBAT distribution ID, e.g. fedora, or auto for autodetection') option('sbat-distro-generation', type : 'integer', value : 1, @@ -483,5 +491,5 @@ option('analyze', type: 'boolean', value: 'true', option('bpf-framework', type : 'combo', choices : ['auto', 'true', 'false'], description: 'build BPF programs from source code in restricted C') -option('dbus-interfaces-dir', type : 'string', - description : 'export D-Bus introspection XML as standalone files') +option('skip-deps', type : 'boolean', value : 'false', + description : 'skip optional dependencies') diff --git a/mkosi.default.d/arch/10-mkosi.arch b/mkosi.default.d/arch/10-mkosi.arch index 19bc45a8d..55e8a206c 100644 --- a/mkosi.default.d/arch/10-mkosi.arch +++ b/mkosi.default.d/arch/10-mkosi.arch @@ -63,3 +63,9 @@ Packages= # For testing systemd's zsh completion scripts # Run `autoload -Uz compinit; compinit` from a zsh shell in the booted image to enable completions. zsh + # Required to run systemd-networkd-tests.py + python + iproute + dnsmasq + wireguard-tools + dhcp diff --git a/mkosi.default.d/centos_epel/10-mkosi.centos_epel b/mkosi.default.d/centos_epel/10-mkosi.centos_epel new file mode 100644 index 000000000..cf6c74f01 --- /dev/null +++ b/mkosi.default.d/centos_epel/10-mkosi.centos_epel @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +# This is a settings file for OS image generation using mkosi (https://github.com/systemd/mkosi). +# Symlink this file to mkosi.default in the project root directory and invoke "mkosi" to build an OS image. + +[Distribution] +Distribution=centos_epel + +[Output] +Format=gpt_xfs +HostonlyInitrd=no + +[Packages] +BuildPackages= + diffutils + docbook-style-xsl + findutils + gcc + gettext + git + glibc-minimal-langpack + gnu-efi + gnu-efi-devel + gperf + lz4 + meson + ninja-build + pam-devel + # CentOS Stream 8 libgcrypt-devel doesn't ship a pkg-config file. + libgcrypt-devel + pkgconfig + pkgconfig(audit) + pkgconfig(blkid) + pkgconfig(bzip2) + pkgconfig(dbus-1) + pkgconfig(fdisk) + pkgconfig(gnutls) + pkgconfig(libacl) + pkgconfig(libcap) + pkgconfig(libcryptsetup) + pkgconfig(libcurl) + pkgconfig(libdw) + pkgconfig(libidn2) + pkgconfig(libkmod) + pkgconfig(liblz4) + pkgconfig(liblzma) + pkgconfig(libmicrohttpd) + pkgconfig(libpcre2-8) + pkgconfig(libqrencode) + pkgconfig(libseccomp) + pkgconfig(libselinux) + pkgconfig(libzstd) + pkgconfig(mount) + pkgconfig(openssl) + pkgconfig(p11-kit-1) + pkgconfig(pwquality) + pkgconfig(tss2-esys) + pkgconfig(tss2-mu) + pkgconfig(tss2-rc) + pkgconfig(valgrind) + pkgconfig(xkbcommon) + python3dist(jinja2) + python3dist(lxml) + rpm + tree + zstd + /usr/bin/xsltproc + +Packages= + gdb + nano + # procps-ng provides a set of useful utilities (ps, free, etc) + procps-ng + strace + tpm2-tss + less + netcat + e2fsprogs + # Required to run systemd-networkd-tests.py + python3 + iproute + iproute-tc + dnsmasq + wireguard-tools + dhcp-server + kernel-modules-extra diff --git a/mkosi.default.d/debian/10-mkosi.debian b/mkosi.default.d/debian/10-mkosi.debian index 2be71f3c7..7aa0194a6 100644 --- a/mkosi.default.d/debian/10-mkosi.debian +++ b/mkosi.default.d/debian/10-mkosi.debian @@ -5,7 +5,7 @@ [Distribution] Distribution=debian -Release=unstable +Release=testing [Packages] BuildPackages= @@ -73,3 +73,9 @@ Packages= locales nano strace + # Required to run systemd-networkd-tests.py + python3 + iproute2 + dnsmasq-base + wireguard-tools + isc-dhcp-server diff --git a/mkosi.default.d/fedora/10-mkosi.fedora b/mkosi.default.d/fedora/10-mkosi.fedora index d68405e91..5fb4cca6b 100644 --- a/mkosi.default.d/fedora/10-mkosi.fedora +++ b/mkosi.default.d/fedora/10-mkosi.fedora @@ -75,3 +75,11 @@ Packages= netcat e2fsprogs compsize + # Required to run systemd-networkd-tests.py + python + iproute + iproute-tc + dnsmasq + wireguard-tools + dhcp-server + kernel-modules-extra diff --git a/mkosi.default.d/ubuntu/10-mkosi.ubuntu b/mkosi.default.d/ubuntu/10-mkosi.ubuntu index c9c473e7b..109bd468d 100644 --- a/mkosi.default.d/ubuntu/10-mkosi.ubuntu +++ b/mkosi.default.d/ubuntu/10-mkosi.ubuntu @@ -71,3 +71,9 @@ Packages= locales nano strace + # Required to run systemd-networkd-tests.py + python3 + iproute2 + dnsmasq-base + wireguard-tools + isc-dhcp-server diff --git a/network/80-6rd-tunnel.network b/network/80-6rd-tunnel.network index 2e479eb1a..6b9748a07 100644 --- a/network/80-6rd-tunnel.network +++ b/network/80-6rd-tunnel.network @@ -1,11 +1,15 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later +# SPDX-License-Identifier: CC0-1.0 # -# This file is part of systemd. +# This config file is installed as part of systemd. +# It may be freely copied and edited (following the Creative Commons Zero v1.0 Universal License). # -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. +# To make local modifications, one of the following methods may be used: +# 1. add a drop-in file that extends this file by creating the +# /etc/systemd/network/80-6rd-tunnel.network.d/ directory and creating a +# new .conf file there. +# 2. copy this file into /etc/systemd/network or one of the other paths checked +# by systemd-networkd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. # This network file matches 6rd-* SIT devices which is automatically created by # systemd-networkd when DHCPv4 6RD option is received. diff --git a/network/80-container-host0.network b/network/80-container-host0.network index a8e683455..da92b6bd0 100644 --- a/network/80-container-host0.network +++ b/network/80-container-host0.network @@ -1,11 +1,15 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later +# SPDX-License-Identifier: CC0-1.0 # -# This file is part of systemd. +# This config file is installed as part of systemd. +# It may be freely copied and edited (following the Creative Commons Zero v1.0 Universal License). # -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. +# To make local modifications, one of the following methods may be used: +# 1. add a drop-in file that extends this file by creating the +# /etc/systemd/network/80-container-host0.network.d/ directory and creating a +# new .conf file there. +# 2. copy this file into /etc/systemd/network or one of the other paths checked +# by systemd-networkd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. # This network file matches the container-side of the virtual Ethernet link # created by systemd-nspawn's --network-veth switch. See systemd-nspawn(1) for diff --git a/network/80-container-vb.network b/network/80-container-vb.network index 37f43c654..9908f8758 100644 --- a/network/80-container-vb.network +++ b/network/80-container-vb.network @@ -1,11 +1,15 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later +# SPDX-License-Identifier: CC0-1.0 # -# This file is part of systemd. +# This config file is installed as part of systemd. +# It may be freely copied and edited (following the Creative Commons Zero v1.0 Universal License). # -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. +# To make local modifications, one of the following methods may be used: +# 1. add a drop-in file that extends this file by creating the +# /etc/systemd/network/80-container-vb.network.d/ directory and creating a +# new .conf file there. +# 2. copy this file into /etc/systemd/network or one of the other paths checked +# by systemd-networkd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. # This network file matches the host-side of the virtual Ethernet link # created by systemd-nspawn's --network-veth switch with --network-bridge or diff --git a/network/80-container-ve.network b/network/80-container-ve.network index 0c233bc8e..10441a571 100644 --- a/network/80-container-ve.network +++ b/network/80-container-ve.network @@ -1,11 +1,15 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later +# SPDX-License-Identifier: CC0-1.0 # -# This file is part of systemd. +# This config file is installed as part of systemd. +# It may be freely copied and edited (following the Creative Commons Zero v1.0 Universal License). # -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. +# To make local modifications, one of the following methods may be used: +# 1. add a drop-in file that extends this file by creating the +# /etc/systemd/network/80-container-ve.network.d/ directory and creating a +# new .conf file there. +# 2. copy this file into /etc/systemd/network or one of the other paths checked +# by systemd-networkd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. # This network file matches the host-side of the virtual Ethernet link # created by systemd-nspawn's --network-veth switch. See systemd-nspawn(1) for diff --git a/network/80-container-vz.network b/network/80-container-vz.network index 1c58f7d50..6861fe7df 100644 --- a/network/80-container-vz.network +++ b/network/80-container-vz.network @@ -1,11 +1,15 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later +# SPDX-License-Identifier: CC0-1.0 # -# This file is part of systemd. +# This config file is installed as part of systemd. +# It may be freely copied and edited (following the Creative Commons Zero v1.0 Universal License). # -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. +# To make local modifications, one of the following methods may be used: +# 1. add a drop-in file that extends this file by creating the +# /etc/systemd/network/80-container-vz.network.d/ directory and creating a +# new .conf file there. +# 2. copy this file into /etc/systemd/network or one of the other paths checked +# by systemd-networkd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. # This network file matches the bridge interface created by systemd-nspawn's # --network-zone= switch. See systemd-nspawn(1) for details. diff --git a/network/80-ethernet.network.example b/network/80-ethernet.network.example new file mode 100644 index 000000000..30c710098 --- /dev/null +++ b/network/80-ethernet.network.example @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: CC0-1.0 +# +# This example config file is installed as part of systemd. +# It may be freely copied and edited (following the Creative Commons Zero v1.0 Universal License). +# +# To use the file, one of the following methods may be used: +# 1. add a symlink from /etc/systemd/network to the current location of this file, +# 2. copy the file into /etc/systemd/network or one of the other paths checked +# by systemd-networkd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. + +# Enable DHCPv4 and DHCPv6 on all ethernet links +[Match] +Type=ether + +[Network] +DHCP=yes diff --git a/network/80-vm-vt.network b/network/80-vm-vt.network index e8365df31..f1cb60fd1 100644 --- a/network/80-vm-vt.network +++ b/network/80-vm-vt.network @@ -1,11 +1,15 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later +# SPDX-License-Identifier: CC0-1.0 # -# This file is part of systemd. +# This config file is installed as part of systemd. +# It may be freely copied and edited (following the Creative Commons Zero v1.0 Universal License). # -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. +# To make local modifications, one of the following methods may be used: +# 1. add a drop-in file that extends this file by creating the +# /etc/systemd/network/80-vm-vt.network.d/ directory and creating a +# new .conf file there. +# 2. copy this file into /etc/systemd/network or one of the other paths checked +# by systemd-networkd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. # This network file matches vt-* TUN/TAP devices and applies a similar # configuration as ve-* to provide NAT/DHCP to VMs. diff --git a/network/80-wifi-adhoc.network b/network/80-wifi-adhoc.network index c0c388f16..3d5ba3cad 100644 --- a/network/80-wifi-adhoc.network +++ b/network/80-wifi-adhoc.network @@ -1,11 +1,15 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later +# SPDX-License-Identifier: CC0-1.0 # -# This file is part of systemd. +# This config file is installed as part of systemd. +# It may be freely copied and edited (following the Creative Commons Zero v1.0 Universal License). # -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. +# To make local modifications, one of the following methods may be used: +# 1. add a drop-in file that extends this file by creating the +# /etc/systemd/network/80-wifi-adhoc.network.d/ directory and creating a +# new .conf file there. +# 2. copy this file into /etc/systemd/network or one of the other paths checked +# by systemd-networkd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. [Match] Type=wlan diff --git a/network/80-wifi-ap.network.example b/network/80-wifi-ap.network.example index 854938767..d866f7900 100644 --- a/network/80-wifi-ap.network.example +++ b/network/80-wifi-ap.network.example @@ -1,3 +1,14 @@ +# SPDX-License-Identifier: CC0-1.0 +# +# This example config file is installed as part of systemd. +# It may be freely copied and edited (following the Creative Commons Zero v1.0 Universal License). +# +# To use the file, one of the following methods may be used: +# 1. add a symlink from /etc/systemd/network to the current location of this file, +# 2. copy the file into /etc/systemd/network or one of the other paths checked +# by systemd-networkd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. + [Match] Type=wlan WLANInterfaceType=ap diff --git a/network/80-wifi-station.network.example b/network/80-wifi-station.network.example index 1fb28a1b0..a1cd3530e 100644 --- a/network/80-wifi-station.network.example +++ b/network/80-wifi-station.network.example @@ -1,3 +1,14 @@ +# SPDX-License-Identifier: CC0-1.0 +# +# This example config file is installed as part of systemd. +# It may be freely copied and edited (following the Creative Commons Zero v1.0 Universal License). +# +# To use the file, one of the following methods may be used: +# 1. add a symlink from /etc/systemd/network to the current location of this file, +# 2. copy the file into /etc/systemd/network or one of the other paths checked +# by systemd-networkd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. + [Match] Type=wlan WLANInterfaceType=station diff --git a/network/99-default.link b/network/99-default.link index bca660ac2..dad91bb1a 100644 --- a/network/99-default.link +++ b/network/99-default.link @@ -1,11 +1,15 @@ -# SPDX-License-Identifier: LGPL-2.1-or-later +# SPDX-License-Identifier: CC0-1.0 # -# This file is part of systemd. +# This config file is installed as part of systemd. +# It may be freely copied and edited (following the Creative Commons Zero v1.0 Universal License). # -# systemd is free software; you can redistribute it and/or modify it -# under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; either version 2.1 of the License, or -# (at your option) any later version. +# To make local modifications, one of the following methods may be used: +# 1. add a drop-in file that extends this file by creating the +# /etc/systemd/network/99-default.link.d/ directory and creating a +# new .conf file there. +# 2. copy this file into /etc/systemd/network or one of the other paths checked +# by systemd-udevd and edit it there. +# This file should not be edited in place, because it'll be overwritten on upgrades. [Match] OriginalName=* diff --git a/network/meson.build b/network/meson.build index f4ae2194d..4c6de2051 100644 --- a/network/meson.build +++ b/network/meson.build @@ -5,6 +5,7 @@ if conf.get('ENABLE_NETWORKD') == 1 '80-container-host0.network', '80-container-ve.network', '80-container-vz.network', + '80-ethernet.network.example', '80-vm-vt.network', '80-wifi-adhoc.network', '80-wifi-ap.network.example', diff --git a/po/LINGUAS b/po/LINGUAS index 6c9b770b9..f81c2d766 100644 --- a/po/LINGUAS +++ b/po/LINGUAS @@ -33,3 +33,4 @@ tr uk zh_CN zh_TW +ka diff --git a/po/be.po b/po/be.po index f34c0f8c5..e143fb758 100644 --- a/po/be.po +++ b/po/be.po @@ -7,7 +7,6 @@ # Zmicer Turok , 2020, 2021. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-01-10 18:36+0000\n" diff --git a/po/be@latin.po b/po/be@latin.po index 28654885b..394bff8f0 100644 --- a/po/be@latin.po +++ b/po/be@latin.po @@ -6,7 +6,6 @@ # Viktar Vaŭčkievič , 2015, 2016. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2016-06-09 19:50+0300\n" diff --git a/po/bg.po b/po/bg.po index 3ab13ffa9..53356397e 100644 --- a/po/bg.po +++ b/po/bg.po @@ -6,7 +6,6 @@ # msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2016-05-14 13:28+0300\n" diff --git a/po/ca.po b/po/ca.po index 0cb2e497e..eb33b8847 100644 --- a/po/ca.po +++ b/po/ca.po @@ -5,7 +5,6 @@ # Robert Antoni Buj Gelonch , 2018. #zanata msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2018-02-27 04:18-0500\n" diff --git a/po/cs.po b/po/cs.po index b76d5a1fa..57f06bf58 100644 --- a/po/cs.po +++ b/po/cs.po @@ -4,7 +4,6 @@ # msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2020-10-26 22:48+0100\n" diff --git a/po/da.po b/po/da.po index 7941c6ba2..443b0b000 100644 --- a/po/da.po +++ b/po/da.po @@ -5,7 +5,6 @@ # scootergrisen , 2020, 2021. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-06-02 16:03+0000\n" diff --git a/po/de.po b/po/de.po index e52216bfb..70d33879c 100644 --- a/po/de.po +++ b/po/de.po @@ -9,7 +9,6 @@ # Christian Wehrli , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-11-08 00:49+0000\n" @@ -1064,8 +1063,8 @@ msgid "" "Authentication is required to send a UNIX signal to the processes of " "'$(unit)'." msgstr "" -"Legitimierung ist zum Senden eines UNIX-Signals an die Prozesse von »$(unit)«" -" notwendig." +"Legitimierung ist zum Senden eines UNIX-Signals an die Prozesse von " +"»$(unit)« notwendig." #: src/core/dbus-unit.c:566 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." diff --git a/po/el.po b/po/el.po index 7d992885d..7a6b6645f 100644 --- a/po/el.po +++ b/po/el.po @@ -5,7 +5,6 @@ # Dimitris Spingos (Δημήτρης Σπίγγος) , 2014. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2014-04-29 09:17+0300\n" diff --git a/po/es.po b/po/es.po index c7fc862d1..212ad70a9 100644 --- a/po/es.po +++ b/po/es.po @@ -8,7 +8,6 @@ # Emilio Herrera , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-08-26 18:05+0000\n" diff --git a/po/fi.po b/po/fi.po index 222f5753e..10ea364df 100644 --- a/po/fi.po +++ b/po/fi.po @@ -4,7 +4,6 @@ # Jan Kuparinen , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-09-14 19:04+0000\n" diff --git a/po/fr.po b/po/fr.po index 0e791fc57..bee6792a2 100644 --- a/po/fr.po +++ b/po/fr.po @@ -7,7 +7,6 @@ # Arnaud T. , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-01-14 06:37+0000\n" diff --git a/po/gl.po b/po/gl.po index babdfd25b..e57925245 100644 --- a/po/gl.po +++ b/po/gl.po @@ -3,7 +3,6 @@ # Fran Dieguez , 2015. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2019-12-29 22:30+0100\n" diff --git a/po/hr.po b/po/hr.po index f80bf74a5..d525d4923 100644 --- a/po/hr.po +++ b/po/hr.po @@ -6,7 +6,6 @@ # Gogo Gogsi , 2020, 2021. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-10-03 16:04+0000\n" @@ -365,7 +364,8 @@ msgstr "" #: src/login/org.freedesktop.login1.policy:191 msgid "Power off the system while an application is inhibiting this" -msgstr "Isključi sustav kada je aplikacija zatražila sprječavanje isključivanja" +msgstr "" +"Isključi sustav kada je aplikacija zatražila sprječavanje isključivanja" #: src/login/org.freedesktop.login1.policy:192 msgid "" @@ -431,7 +431,8 @@ msgstr "" #: src/login/org.freedesktop.login1.policy:257 msgid "Halt the system while an application is inhibiting this" -msgstr "Zaustavi sustav kada je aplikacija zatražila sprječavanje zaustavljanja" +msgstr "" +"Zaustavi sustav kada je aplikacija zatražila sprječavanje zaustavljanja" #: src/login/org.freedesktop.login1.policy:258 msgid "" @@ -496,7 +497,8 @@ msgstr "" #: src/login/org.freedesktop.login1.policy:321 msgid "Hibernate the system while an application is inhibiting this" -msgstr "Hiberniraj sustav kada je aplikacija zatražila sprječavanje hibernacije" +msgstr "" +"Hiberniraj sustav kada je aplikacija zatražila sprječavanje hibernacije" #: src/login/org.freedesktop.login1.policy:322 msgid "" diff --git a/po/hu.po b/po/hu.po index 3a1d36dfe..5044f4ded 100644 --- a/po/hu.po +++ b/po/hu.po @@ -7,7 +7,6 @@ # Balázs Úr , 2016. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2016-08-23 18:03+0100\n" diff --git a/po/id.po b/po/id.po index 79d2482ed..3957a03bc 100644 --- a/po/id.po +++ b/po/id.po @@ -4,7 +4,6 @@ # Andika Triwidada , 2014, 2021. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-09-24 11:05+0000\n" diff --git a/po/it.po b/po/it.po index 42156d5a1..5b0f3ff83 100644 --- a/po/it.po +++ b/po/it.po @@ -5,7 +5,6 @@ # Daniele Medri , 2013-2021. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-01-08 17:51+0100\n" @@ -67,7 +66,7 @@ msgstr "Ricarica lo stato di systemd" #: src/core/org.freedesktop.systemd1.policy.in:65 msgid "Authentication is required to reload the systemd state." -msgstr "Autenticazione richiesta per riavviare lo stato di sistemd." +msgstr "Autenticazione richiesta per ricaricare lo stato di systemd." #: src/home/org.freedesktop.home1.policy:13 msgid "Create a home area" @@ -919,19 +918,19 @@ msgstr "" #: src/core/dbus-unit.c:359 msgid "Authentication is required to start '$(unit)'." -msgstr "Autenticazione richiesta per avviare '${unit}'." +msgstr "Autenticazione richiesta per avviare '$(unit)'." #: src/core/dbus-unit.c:360 msgid "Authentication is required to stop '$(unit)'." -msgstr "Autenticazione richiesta per fermare '${unit}'." +msgstr "Autenticazione richiesta per fermare '$(unit)'." #: src/core/dbus-unit.c:361 msgid "Authentication is required to reload '$(unit)'." -msgstr "Autenticazione richiesta per ricaricare '${unit}'." +msgstr "Autenticazione richiesta per ricaricare '$(unit)'." #: src/core/dbus-unit.c:362 src/core/dbus-unit.c:363 msgid "Authentication is required to restart '$(unit)'." -msgstr "Autenticazione richiesta per riavviare '${unit}'." +msgstr "Autenticazione richiesta per riavviare '$(unit)'." #: src/core/dbus-unit.c:535 msgid "" @@ -939,16 +938,16 @@ msgid "" "'$(unit)'." msgstr "" "Autenticazione richiesta per inviare un segnale UNIX ai processi di " -"'${unit}'." +"'$(unit)'." #: src/core/dbus-unit.c:566 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." msgstr "" -"Autenticazione richiesta per riconfigurare lo stato \"fallito\" di '${unit}'." +"Autenticazione richiesta per riconfigurare lo stato \"fallito\" di '$(unit)'." #: src/core/dbus-unit.c:599 msgid "Authentication is required to set properties on '$(unit)'." -msgstr "Autenticazione richiesta per configurare le proprietà di '${unit}'." +msgstr "Autenticazione richiesta per configurare le proprietà di '$(unit)'." #: src/core/dbus-unit.c:708 msgid "" @@ -956,7 +955,7 @@ msgid "" "'$(unit)'." msgstr "" "Autenticazione richiesta per eliminare i file e le directory associate a " -"'${unit}'." +"'$(unit)'." #: src/core/dbus-unit.c:757 msgid "" diff --git a/po/ja.po b/po/ja.po index a4d519a6c..56a30b790 100644 --- a/po/ja.po +++ b/po/ja.po @@ -5,7 +5,6 @@ # Takuro Onoue , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-09-09 03:04+0000\n" diff --git a/po/ka.po b/po/ka.po new file mode 100644 index 000000000..43ecdccca --- /dev/null +++ b/po/ka.po @@ -0,0 +1,920 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# Temuri Doghonadze , 2022. +msgid "" +msgstr "" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-01-08 17:48+0100\n" +"PO-Revision-Date: 2022-03-10 18:16+0000\n" +"Last-Translator: Temuri Doghonadze \n" +"Language-Team: Georgian \n" +"Language: ka\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.11.2\n" + +#: src/core/org.freedesktop.systemd1.policy.in:22 +msgid "Send passphrase back to system" +msgstr "საკვანძო ფრაზის სისტემაში გადაგზავნა" + +#: src/core/org.freedesktop.systemd1.policy.in:23 +msgid "" +"Authentication is required to send the entered passphrase back to the system." +msgstr "საკვანძო ფრაზის სისტემისთვის დასაბრუნებლად საჭიროა ავთენტიკაცია." + +#: src/core/org.freedesktop.systemd1.policy.in:33 +msgid "Manage system services or other units" +msgstr "სისტემური და სხვა სერვისების მართვა" + +#: src/core/org.freedesktop.systemd1.policy.in:34 +msgid "Authentication is required to manage system services or other units." +msgstr "სისტემური და სხვა სერვისების მართვისთვის საჭიროა ავთენტიკაცია." + +#: src/core/org.freedesktop.systemd1.policy.in:43 +msgid "Manage system service or unit files" +msgstr "სისტემური სერვისების ან ფაილების მართვა" + +#: src/core/org.freedesktop.systemd1.policy.in:44 +msgid "Authentication is required to manage system service or unit files." +msgstr "სისტემური სერვისების ან ფაილების მართვისათვის საჭიროა ავთენტიკაცია." + +#: src/core/org.freedesktop.systemd1.policy.in:54 +msgid "Set or unset system and service manager environment variables" +msgstr "სისტემისა და სერვისების მმართველის გარემოს ცვლადების დაყენება და წაშლა" + +#: src/core/org.freedesktop.systemd1.policy.in:55 +msgid "" +"Authentication is required to set or unset system and service manager " +"environment variables." +msgstr "" +"სისტემისა და სერვისის მენეჯერის გარემოს ცვლადების დასაყენებლად ან წასაშლელად " +"საჭიროა ავთენტიკაცია." + +#: src/core/org.freedesktop.systemd1.policy.in:64 +msgid "Reload the systemd state" +msgstr "systemd-ის მდგომარეობის თავიდან ჩატვირთვა" + +#: src/core/org.freedesktop.systemd1.policy.in:65 +msgid "Authentication is required to reload the systemd state." +msgstr "systemd-ის მდგომარეობის თავიდან ჩატვირთვის საჭიროა ავთენტიკაცია." + +#: src/home/org.freedesktop.home1.policy:13 +msgid "Create a home area" +msgstr "შექმენით სახლის ტერიტორია" + +#: src/home/org.freedesktop.home1.policy:14 +msgid "Authentication is required to create a user's home area." +msgstr "სახლის ტერიტორიის შესაქმნელად საჭიროა ავთენტიკაცია." + +#: src/home/org.freedesktop.home1.policy:23 +msgid "Remove a home area" +msgstr "სახლის ტერიტორიის წაშლა" + +#: src/home/org.freedesktop.home1.policy:24 +msgid "Authentication is required to remove a user's home area." +msgstr "სახლის ტერიტორიის წასაშლელად საჭიროა ავთენტიკაცია." + +#: src/home/org.freedesktop.home1.policy:33 +msgid "Check credentials of a home area" +msgstr "სახლის ტერიტორიის მომხმარებლისა და პაროლის შემოწმება" + +#: src/home/org.freedesktop.home1.policy:34 +msgid "" +"Authentication is required to check credentials against a user's home area." +msgstr "" +"სახლის ტერიტორიის მომხმარებლისა და პაროლის შემოწმებისთვის საჭიროა " +"ავთენტიკაცია." + +#: src/home/org.freedesktop.home1.policy:43 +msgid "Update a home area" +msgstr "სახლის ტერიტორიის განახლება" + +#: src/home/org.freedesktop.home1.policy:44 +msgid "Authentication is required to update a user's home area." +msgstr "სახლის ტერიტორიის განახლებისთვის საჭიროა ავთენტიკაცია." + +#: src/home/org.freedesktop.home1.policy:53 +msgid "Resize a home area" +msgstr "სახლის ტერიტორიის ზომის შეცვლა" + +#: src/home/org.freedesktop.home1.policy:54 +msgid "Authentication is required to resize a user's home area." +msgstr "სახლის ტერიტორიის ზომის შეცვლისთვის საჭიროა ავთენტიკაცია." + +#: src/home/org.freedesktop.home1.policy:63 +msgid "Change password of a home area" +msgstr "სახლის ტერიტორიის პაროლის შეცვლა" + +#: src/home/org.freedesktop.home1.policy:64 +msgid "" +"Authentication is required to change the password of a user's home area." +msgstr "სახლის ტერიტორიის პაროლის შეცვლისთვის საჭიროა ავთენტიკაცია." + +#: src/hostname/org.freedesktop.hostname1.policy:20 +msgid "Set hostname" +msgstr "ჰოსტის სახელის დაყენება" + +#: src/hostname/org.freedesktop.hostname1.policy:21 +msgid "Authentication is required to set the local hostname." +msgstr "ჰოსტის სახელის დაყენებისთვის საჭიროა ავთენტიკაცია." + +#: src/hostname/org.freedesktop.hostname1.policy:30 +msgid "Set static hostname" +msgstr "ჰოსტის სტატიკური სახელის დაყენება" + +#: src/hostname/org.freedesktop.hostname1.policy:31 +msgid "" +"Authentication is required to set the statically configured local hostname, " +"as well as the pretty hostname." +msgstr "ჰოსტის სტატიკური სახელის დაყენებისთვის საჭიროა ავთენტიკაცია." + +#: src/hostname/org.freedesktop.hostname1.policy:41 +msgid "Set machine information" +msgstr "მანქანის ინფორმაციის დაყენება" + +#: src/hostname/org.freedesktop.hostname1.policy:42 +msgid "Authentication is required to set local machine information." +msgstr "მანქანის ინფორმაციის დასაყენებლად საჭიროა ავთენტიკაცია." + +#: src/hostname/org.freedesktop.hostname1.policy:51 +msgid "Get product UUID" +msgstr "პროდუქტის UUID-ის ამოღება" + +#: src/hostname/org.freedesktop.hostname1.policy:52 +msgid "Authentication is required to get product UUID." +msgstr "პროდუქტის UUID-ის ამოსაღებად საჭიროა ავთენტიკაცია." + +#: src/import/org.freedesktop.import1.policy:22 +msgid "Import a VM or container image" +msgstr "VM-ის ან კონტეინერის იმიჯის შემოტანა" + +#: src/import/org.freedesktop.import1.policy:23 +msgid "Authentication is required to import a VM or container image" +msgstr "VM-ის ან კონტეინერის იმიჯის შემოსატანად საჭიროა ავთენტიკაცია" + +#: src/import/org.freedesktop.import1.policy:32 +msgid "Export a VM or container image" +msgstr "VM-ის ან კონტეინერის იმიჯის გატანა" + +#: src/import/org.freedesktop.import1.policy:33 +msgid "Authentication is required to export a VM or container image" +msgstr "VM-ის ან კონტეინერის იმიჯის გასატანად საჭიროა ავთენტიკაცია" + +#: src/import/org.freedesktop.import1.policy:42 +msgid "Download a VM or container image" +msgstr "VM-ის ან კონტეინერის იმიჯის გადმოწერა" + +#: src/import/org.freedesktop.import1.policy:43 +msgid "Authentication is required to download a VM or container image" +msgstr "VM-ის ან კონტეინერის იმიჯის გადმოსაწერად საჭიროა ავთენტიკაცია" + +#: src/locale/org.freedesktop.locale1.policy:22 +msgid "Set system locale" +msgstr "სისტემური მდებარეობის დაყენება" + +#: src/locale/org.freedesktop.locale1.policy:23 +msgid "Authentication is required to set the system locale." +msgstr "სისტემური მდებარეობის დაყენებისთვის საჭიროა ავთენტიკაცია." + +#: src/locale/org.freedesktop.locale1.policy:33 +msgid "Set system keyboard settings" +msgstr "სისტემური კლავიატურის მორგება" + +#: src/locale/org.freedesktop.locale1.policy:34 +msgid "Authentication is required to set the system keyboard settings." +msgstr "სისტემური კლავიატურის მოსარგებად საჭიროა ავთენტიკაცია." + +#: src/login/org.freedesktop.login1.policy:22 +msgid "Allow applications to inhibit system shutdown" +msgstr "აპლიკაციებისთვის სისტემის გათიშვის დაყოვნების უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:23 +msgid "" +"Authentication is required for an application to inhibit system shutdown." +msgstr "აპლიკაციას სისტემის გამორთვის დასაყოვნებლად სჭირდება ავთენტიკაცია." + +#: src/login/org.freedesktop.login1.policy:33 +msgid "Allow applications to delay system shutdown" +msgstr "აპლიკაციებისთვის სისტემის გამორთვის დაყოვნების უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:34 +msgid "Authentication is required for an application to delay system shutdown." +msgstr "" +"აპლიკაციებისთვის სისტემის გამორთვის დაყოვნების უფლების მიცემისთვის საჭიროა " +"ავთენტიკაცია." + +#: src/login/org.freedesktop.login1.policy:44 +msgid "Allow applications to inhibit system sleep" +msgstr "აპლიკაციებისთვის სისტემს დაძინების აკრძალვის უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:45 +msgid "Authentication is required for an application to inhibit system sleep." +msgstr "" +"აპლიკაციებისთვის სისტემს დაძინების აკრძალვის უფლების მიცემას ავთენტიკაცია " +"სჭირდება." + +#: src/login/org.freedesktop.login1.policy:55 +msgid "Allow applications to delay system sleep" +msgstr "აპლიკაციებისთვის სისტემს დაძინების გადადების უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:56 +msgid "Authentication is required for an application to delay system sleep." +msgstr "" +"აპლიკაციებისთვის სისტემს დაძინების გადადების უფლების მიცემას ავთენტიკაცია " +"სჭირდება." + +#: src/login/org.freedesktop.login1.policy:65 +msgid "Allow applications to inhibit automatic system suspend" +msgstr "აპლიკაციებისთვის სისტემს შეჩერების აკრძალვის უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:66 +msgid "" +"Authentication is required for an application to inhibit automatic system " +"suspend." +msgstr "" +"აპლიკაციებისთვის სისტემს შეჩერების აკრძალვის უფლების მიცემას ავთენტიკაცია " +"სჭირდება." + +#: src/login/org.freedesktop.login1.policy:75 +msgid "Allow applications to inhibit system handling of the power key" +msgstr "აპლიკაციებისთვის ჩართვის ღილაკის უგულებელყოფის უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:76 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the power key." +msgstr "" +"აპლიკაციებისთვის ჩართვის ღილაკის უგულებელყოფის უფლების მიცემას ავთენტიკაცია " +"სჭირდება." + +#: src/login/org.freedesktop.login1.policy:86 +msgid "Allow applications to inhibit system handling of the suspend key" +msgstr "აპლიკაციებისთვის შეჩერების ღილაკის უგულებელყოფის უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:87 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the suspend key." +msgstr "" +"აპლიკაციებისთვის შეჩერების ღილაკის უგულებელყოფის უფლების მიცემას " +"ავთენტიკაცის სჭირდება." + +#: src/login/org.freedesktop.login1.policy:97 +msgid "Allow applications to inhibit system handling of the hibernate key" +msgstr "აპლიკაციებისთვის დაყვინთვის ღილაკის უგულებელყოფის უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:98 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the hibernate key." +msgstr "" +"აპლიკაციებისთვის დაყვინთვის ღილაკის უგულებელყოფის უფლების მიცემას " +"ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:107 +msgid "Allow applications to inhibit system handling of the lid switch" +msgstr "" +"აპლიკაციებისთვის ნოუთბუქის დახურვის ღილაკის უგულებელყოფის უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:108 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the lid switch." +msgstr "" +"აპლიკაციებისთვის ნოუთბუქის დახურვის ღილაკის უგულებელყოფის უფლების მიცემას " +"ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:117 +msgid "Allow applications to inhibit system handling of the reboot key" +msgstr "აპლიკაციებისთვის გადატვირთვის ღილაკის უგულებელყოფის უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:118 +msgid "" +"Authentication is required for an application to inhibit system handling of " +"the reboot key." +msgstr "" +"აპლიკაციებისთვის გადატვირთვის ღილაკის უგულებელყოფის უფლების მიცემას " +"ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:128 +msgid "Allow non-logged-in user to run programs" +msgstr "არ-შესული მომხმარებლებისთვის პროგრამების გაშვების უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:129 +msgid "Explicit request is required to run programs as a non-logged-in user." +msgstr "" +"არ-შესული მომხმარებლებისთვის პროგრამების გაშვების უფლების მიცემას " +"სპეციფიკური მოთხოვნა სჭირდება." + +#: src/login/org.freedesktop.login1.policy:138 +msgid "Allow non-logged-in users to run programs" +msgstr "არ-შესული მომხმარებლებისთვის პროგრამების გაშვების უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:139 +msgid "Authentication is required to run programs as a non-logged-in user." +msgstr "" +"არ-შესული მომხმარებლებისთვის პროგრამების გაშვების უფლების მიცემას " +"ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:148 +msgid "Allow attaching devices to seats" +msgstr "სამუშაო მაგიდებისთვის მოწყობილობების მიერთების უფლების მიცემა" + +#: src/login/org.freedesktop.login1.policy:149 +msgid "Authentication is required to attach a device to a seat." +msgstr "" +"სამუშაო მაგიდებისთვის მოწყობილობების მიერთების უფლების მიცემას ავთენტიკაცია " +"სჭირდება." + +#: src/login/org.freedesktop.login1.policy:159 +msgid "Flush device to seat attachments" +msgstr "სამუშაო მაგიდებისთვის მოწყობილობების ბმების ჩახსნა" + +#: src/login/org.freedesktop.login1.policy:160 +msgid "Authentication is required to reset how devices are attached to seats." +msgstr "" +"სამუშაო მაგიდებისთვის მოწყობილობების ბმების წასაშლელად საჭიროა ავთენტიკაცია." + +#: src/login/org.freedesktop.login1.policy:169 +msgid "Power off the system" +msgstr "სისტემის გამორთვა" + +#: src/login/org.freedesktop.login1.policy:170 +msgid "Authentication is required to power off the system." +msgstr "სისტემის გამორთვას ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:180 +msgid "Power off the system while other users are logged in" +msgstr "სისტემის გამორთვა, როცა სხვა მომხმარებლებიც არიან შესული" + +#: src/login/org.freedesktop.login1.policy:181 +msgid "" +"Authentication is required to power off the system while other users are " +"logged in." +msgstr "" +"სისტემის გამორთვას, როცა სხვა მომხმარებლებიც არიან შესული, ავთენტიკაცია " +"სჭირდება." + +#: src/login/org.freedesktop.login1.policy:191 +msgid "Power off the system while an application is inhibiting this" +msgstr "სისტემის გამორთვა, როცა აპლიკაციები ბლოკავენ ამას" + +#: src/login/org.freedesktop.login1.policy:192 +msgid "" +"Authentication is required to power off the system while an application is " +"inhibiting this." +msgstr "" +"სისტემის გამორთვას, როცა აპლიკაციები ბლოკავენ ამას, ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:202 +msgid "Reboot the system" +msgstr "სისტემის გადატვირთვა" + +#: src/login/org.freedesktop.login1.policy:203 +msgid "Authentication is required to reboot the system." +msgstr "სისტემის გადატვირთვას ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:213 +msgid "Reboot the system while other users are logged in" +msgstr "სისტემის გამორთვა, როცა სხვა მომხმარებლებიც არიან შესული" + +#: src/login/org.freedesktop.login1.policy:214 +msgid "" +"Authentication is required to reboot the system while other users are logged " +"in." +msgstr "" +"სისტემის გამორთვას, როცა სხვა მომხმარებლებიც არიან შესული, ავთენტიკაცია " +"სჭირდება." + +#: src/login/org.freedesktop.login1.policy:224 +msgid "Reboot the system while an application is inhibiting this" +msgstr "სისტემის გადატვირთვა მაშინაც კი, როცა აპლიკაცია ბლოკავს" + +#: src/login/org.freedesktop.login1.policy:225 +msgid "" +"Authentication is required to reboot the system while an application is " +"inhibiting this." +msgstr "" +"სისტემის გადატვირთვას მაშინაც კი, როცა აპლიკაცია ბლოკავს, ავთენტიკაცია " +"სჭირდება." + +#: src/login/org.freedesktop.login1.policy:235 +msgid "Halt the system" +msgstr "სისტემის გაჩერება" + +#: src/login/org.freedesktop.login1.policy:236 +msgid "Authentication is required to halt the system." +msgstr "სისტემის გაჩერებას ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:246 +msgid "Halt the system while other users are logged in" +msgstr "სისტემის გაჩერება მაშინაც კი, როცა სხვა მომხმარებლებიც არიან შესული" + +#: src/login/org.freedesktop.login1.policy:247 +msgid "" +"Authentication is required to halt the system while other users are logged " +"in." +msgstr "" +"სისტემის გაჩერებას მაშინაც კი, როცა სხვა მომხმარებლებიც არიან შესული, " +"ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:257 +msgid "Halt the system while an application is inhibiting this" +msgstr "სისტემის გაჩერება აპლიკაციის მიერ აკრძალვის შემთხვევაშიც" + +#: src/login/org.freedesktop.login1.policy:258 +msgid "" +"Authentication is required to halt the system while an application is " +"inhibiting this." +msgstr "" +"სისტემის გაჩერებას აპლიკაციის მიერ აკრძალვის შემთხვევაშიც კი სჭირდება " +"ავთენტიკაცია." + +#: src/login/org.freedesktop.login1.policy:268 +msgid "Suspend the system" +msgstr "სისტემის შეჩერება" + +#: src/login/org.freedesktop.login1.policy:269 +msgid "Authentication is required to suspend the system." +msgstr "სისტემის შეჩერებას ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:278 +msgid "Suspend the system while other users are logged in" +msgstr "სისტემის შეჩერება, როცა სხვა მომხმარებლებიც არიან შესული" + +#: src/login/org.freedesktop.login1.policy:279 +msgid "" +"Authentication is required to suspend the system while other users are " +"logged in." +msgstr "" +"სისტემის შეჩერებას, როცა სხვა მომხმარებლებიც არიან შესული, ავთენტიკაცია " +"სჭირდება." + +#: src/login/org.freedesktop.login1.policy:289 +msgid "Suspend the system while an application is inhibiting this" +msgstr "სისტემის შეჩერება მაშინაც კი, როცა აპლიკაცია კრძალავს ამას" + +#: src/login/org.freedesktop.login1.policy:290 +msgid "" +"Authentication is required to suspend the system while an application is " +"inhibiting this." +msgstr "" +"სისტემის შეჩერებას მაშინაც კი, როცა აპლიკაცია კრძალავს ამას, სჭირდება " +"ავთენტიკაცია." + +#: src/login/org.freedesktop.login1.policy:300 +msgid "Hibernate the system" +msgstr "სისტემის ჩაყვინთვა" + +#: src/login/org.freedesktop.login1.policy:301 +msgid "Authentication is required to hibernate the system." +msgstr "სისტემის ჩაყვინთვას ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:310 +msgid "Hibernate the system while other users are logged in" +msgstr "სისტემის მიძინება მაშინაც კი, როცა სხვა მომხმარებლები არიან შესული" + +#: src/login/org.freedesktop.login1.policy:311 +msgid "" +"Authentication is required to hibernate the system while other users are " +"logged in." +msgstr "" +"სისტემის მიძინებას მაშინაც კი, როცა სხვა მომხმარებლები არიან შესული, " +"ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:321 +msgid "Hibernate the system while an application is inhibiting this" +msgstr "სისტემის მიძინება მაშინაც კი, როცა აპლიკაცია კრძალავს ამას" + +#: src/login/org.freedesktop.login1.policy:322 +msgid "" +"Authentication is required to hibernate the system while an application is " +"inhibiting this." +msgstr "" +"სისტემის მიძინებას მაშინაც კი, როცა აპლიკაცია კრძალავს ამას, სჭირდება " +"ავთენტიკაცია." + +#: src/login/org.freedesktop.login1.policy:332 +msgid "Manage active sessions, users and seats" +msgstr "აქტიური სესიების, მომხმარებლებისა და სამუშაო მაგიდების მართვა" + +#: src/login/org.freedesktop.login1.policy:333 +msgid "Authentication is required to manage active sessions, users and seats." +msgstr "" +"აქტიური სესიების, მომხმარებლებისა და სამუშაო მაგიდების მართვას ავთენტიკაცია " +"სჭირდება." + +#: src/login/org.freedesktop.login1.policy:342 +msgid "Lock or unlock active sessions" +msgstr "აქტიური სესიების დაბლოკვა ან განბლოკვა" + +#: src/login/org.freedesktop.login1.policy:343 +msgid "Authentication is required to lock or unlock active sessions." +msgstr "აქტიური სესიების დაბლოკვა ან განბლოკვას ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:352 +msgid "Set the reboot \"reason\" in the kernel" +msgstr "გადატვირთვის \"მიზეზი\"-ის დაყენება" + +#: src/login/org.freedesktop.login1.policy:353 +msgid "Authentication is required to set the reboot \"reason\" in the kernel." +msgstr "გადატვირთვის \"მიზეზი\"-ის დაყენებას ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:363 +msgid "Indicate to the firmware to boot to setup interface" +msgstr "მიკროკოდისთვის დაყენების ინტერფეისში ჩატვირთვის ბრძანების გადაცემა" + +#: src/login/org.freedesktop.login1.policy:364 +msgid "" +"Authentication is required to indicate to the firmware to boot to setup " +"interface." +msgstr "" +"მიკროკოდისთვის დაყენების ინტერფეისში ჩატვირთვის ბრძანების გადაცემას " +"ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:374 +msgid "Indicate to the boot loader to boot to the boot loader menu" +msgstr "ბირთვის ჩამტვირთველისთვის მენიუში ჩატვირთვის ბრძანების გადაცემა" + +#: src/login/org.freedesktop.login1.policy:375 +msgid "" +"Authentication is required to indicate to the boot loader to boot to the " +"boot loader menu." +msgstr "" +"ბირთვის ჩამტვირთველისთვის მენიუში ჩატვირთვის ბრძანების გადაცემას " +"ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:385 +msgid "Indicate to the boot loader to boot a specific entry" +msgstr "" +"ბირთვის ჩამტვირთველისთვის მითითებული ელემენტის ჩატვირთვის ბრძანების გადაცემა" + +#: src/login/org.freedesktop.login1.policy:386 +msgid "" +"Authentication is required to indicate to the boot loader to boot into a " +"specific boot loader entry." +msgstr "" +"ბირთვის ჩამტვირთველისთვის მითითებული ელემენტის ჩატვირთვის ბრძანების " +"გადაცემას ავთენტიკაცია სჭირდება." + +#: src/login/org.freedesktop.login1.policy:396 +msgid "Set a wall message" +msgstr "კედლის შეტყობინების დაყენება" + +#: src/login/org.freedesktop.login1.policy:397 +msgid "Authentication is required to set a wall message" +msgstr "კედლის შეტყობინების დაყენებას ავთენტიკაცია სჭირდება" + +#: src/login/org.freedesktop.login1.policy:406 +msgid "Change Session" +msgstr "სესიის შეცვლა" + +#: src/login/org.freedesktop.login1.policy:407 +msgid "Authentication is required to change the virtual terminal." +msgstr "ვირტუალური ტერმინალის შეცვლას ავთენტიკაცია სჭირდება." + +#: src/machine/org.freedesktop.machine1.policy:22 +msgid "Log into a local container" +msgstr "ლოკალურ კონტეინერში შესვლა" + +#: src/machine/org.freedesktop.machine1.policy:23 +msgid "Authentication is required to log into a local container." +msgstr "ლოკალურ კონტეინერში შესვლას ავთენტიკაცია სჭირდება." + +#: src/machine/org.freedesktop.machine1.policy:32 +msgid "Log into the local host" +msgstr "ლოკალურ ჰოსტზე შესვლა" + +#: src/machine/org.freedesktop.machine1.policy:33 +msgid "Authentication is required to log into the local host." +msgstr "ლოკალურ ჰოსტზე შესვლას ავთენტიკაცია სჭირდება." + +#: src/machine/org.freedesktop.machine1.policy:42 +msgid "Acquire a shell in a local container" +msgstr "ლოკალურ კონტეინერში გარსის მიღება" + +#: src/machine/org.freedesktop.machine1.policy:43 +msgid "Authentication is required to acquire a shell in a local container." +msgstr "ლოკალურ კონტეინერში გარსის მიღებას ავთენტიკაცია სჭირდება." + +#: src/machine/org.freedesktop.machine1.policy:53 +msgid "Acquire a shell on the local host" +msgstr "ლოკალურ ჰოსტზე გარსის მიღება" + +#: src/machine/org.freedesktop.machine1.policy:54 +msgid "Authentication is required to acquire a shell on the local host." +msgstr "ლოკალურ ჰოსტზე გარსის მიღებას ავთენტიკაცია სჭირდება." + +#: src/machine/org.freedesktop.machine1.policy:64 +msgid "Acquire a pseudo TTY in a local container" +msgstr "ლოკალურ კონტეინერში ფსევდოტერმინალის მიღება" + +#: src/machine/org.freedesktop.machine1.policy:65 +msgid "" +"Authentication is required to acquire a pseudo TTY in a local container." +msgstr "ლოკალურ კონტეინერში ფსევდოტერმინალის მიღებას ავთენტიკაცია სჭირდება." + +#: src/machine/org.freedesktop.machine1.policy:74 +msgid "Acquire a pseudo TTY on the local host" +msgstr "ლოკალურ ჰოსტზე ვირტუალური ტერმინალის მიღება" + +#: src/machine/org.freedesktop.machine1.policy:75 +msgid "Authentication is required to acquire a pseudo TTY on the local host." +msgstr "ლოკალურ ჰოსტზე ვირტუალური ტერმინალის მიღებას ავთენტიკაცია სჭირდება." + +#: src/machine/org.freedesktop.machine1.policy:84 +msgid "Manage local virtual machines and containers" +msgstr "ლოკალური ვირტუალური მანქანებისა და კონტეინერების მართვა" + +#: src/machine/org.freedesktop.machine1.policy:85 +msgid "" +"Authentication is required to manage local virtual machines and containers." +msgstr "" +"ლოკალური ვირტუალური მანქანებისა და კონტეინერების მართვას ავთენტიკაცია " +"სჭირდება." + +#: src/machine/org.freedesktop.machine1.policy:95 +msgid "Manage local virtual machine and container images" +msgstr "ლოკალური ვირტუალური მანქანებისა და კონტეინერების იმიჯების მართვა" + +#: src/machine/org.freedesktop.machine1.policy:96 +msgid "" +"Authentication is required to manage local virtual machine and container " +"images." +msgstr "" +"ლოკალური ვირტუალური მანქანებისა და კონტეინერების იმიჯების მართვას " +"ავთენტიკაცია სჭირდება." + +#: src/network/org.freedesktop.network1.policy:22 +msgid "Set NTP servers" +msgstr "NTP სერვერის დაყენება" + +#: src/network/org.freedesktop.network1.policy:23 +msgid "Authentication is required to set NTP servers." +msgstr "NTP სერვერის დაყენებას ავთენტიკაცია სჭირდება." + +#: src/network/org.freedesktop.network1.policy:33 +#: src/resolve/org.freedesktop.resolve1.policy:44 +msgid "Set DNS servers" +msgstr "DNS სერვერის დაყენება" + +#: src/network/org.freedesktop.network1.policy:34 +#: src/resolve/org.freedesktop.resolve1.policy:45 +msgid "Authentication is required to set DNS servers." +msgstr "DNS სერვერის დაყენებას ავთენტიკაცია სჭირდება." + +#: src/network/org.freedesktop.network1.policy:44 +#: src/resolve/org.freedesktop.resolve1.policy:55 +msgid "Set domains" +msgstr "დომენის დაყენება" + +#: src/network/org.freedesktop.network1.policy:45 +#: src/resolve/org.freedesktop.resolve1.policy:56 +msgid "Authentication is required to set domains." +msgstr "დომენის დაყენებას ავთენტიკაცია სჭირდება." + +#: src/network/org.freedesktop.network1.policy:55 +#: src/resolve/org.freedesktop.resolve1.policy:66 +msgid "Set default route" +msgstr "ქსელის ნაგულისხმევი მარშრუტის დაყენება" + +#: src/network/org.freedesktop.network1.policy:56 +#: src/resolve/org.freedesktop.resolve1.policy:67 +msgid "Authentication is required to set default route." +msgstr "ქსელის ნაგულისხმევი მარშრუტის დაყენებას ავთენტიკაცია სჭირდება." + +#: src/network/org.freedesktop.network1.policy:66 +#: src/resolve/org.freedesktop.resolve1.policy:77 +msgid "Enable/disable LLMNR" +msgstr "LLMNR-ის ჩართ/გამორთ" + +#: src/network/org.freedesktop.network1.policy:67 +#: src/resolve/org.freedesktop.resolve1.policy:78 +msgid "Authentication is required to enable or disable LLMNR." +msgstr "LLMNR-ის ჩართ/გამორთ-თვის საჭიროა ავთენტიკაცია." + +#: src/network/org.freedesktop.network1.policy:77 +#: src/resolve/org.freedesktop.resolve1.policy:88 +msgid "Enable/disable multicast DNS" +msgstr "multicast DNS-ის ჩართ/გამორთ" + +#: src/network/org.freedesktop.network1.policy:78 +#: src/resolve/org.freedesktop.resolve1.policy:89 +msgid "Authentication is required to enable or disable multicast DNS." +msgstr "multicast DNS-ის ჩართ/გამორთ-თვის საჭიროა ავთენტიკაცია." + +#: src/network/org.freedesktop.network1.policy:88 +#: src/resolve/org.freedesktop.resolve1.policy:99 +msgid "Enable/disable DNS over TLS" +msgstr "DNS TLS-ზე-ის ჩართ/გამორთ" + +#: src/network/org.freedesktop.network1.policy:89 +#: src/resolve/org.freedesktop.resolve1.policy:100 +msgid "Authentication is required to enable or disable DNS over TLS." +msgstr "DNS TLS-ზე-ის ჩართ/გამორთ-თვის საჭიროა ავთენტიკაცია." + +#: src/network/org.freedesktop.network1.policy:99 +#: src/resolve/org.freedesktop.resolve1.policy:110 +msgid "Enable/disable DNSSEC" +msgstr "DNSSEC-ის ჩართ/გამორთ" + +#: src/network/org.freedesktop.network1.policy:100 +#: src/resolve/org.freedesktop.resolve1.policy:111 +msgid "Authentication is required to enable or disable DNSSEC." +msgstr "DNSSEC-ის ჩართ/გამორთ-თვის საჭიროა ავთენტიკაცია." + +#: src/network/org.freedesktop.network1.policy:110 +#: src/resolve/org.freedesktop.resolve1.policy:121 +msgid "Set DNSSEC Negative Trust Anchors" +msgstr "DNSSEC Negative Trust Anchor-ების დაყენება" + +#: src/network/org.freedesktop.network1.policy:111 +#: src/resolve/org.freedesktop.resolve1.policy:122 +msgid "Authentication is required to set DNSSEC Negative Trust Anchors." +msgstr "DNSSEC Negative Trust Anchor-ების დაყენებისთვის საჭიროა ავთენტიკაცია." + +#: src/network/org.freedesktop.network1.policy:121 +msgid "Revert NTP settings" +msgstr "NTP პარამეტრების დაბრუნება" + +#: src/network/org.freedesktop.network1.policy:122 +msgid "Authentication is required to reset NTP settings." +msgstr "NTP პარამეტრების დაბრუნებისთვის საჭიროა ავთენტიკაცია." + +#: src/network/org.freedesktop.network1.policy:132 +msgid "Revert DNS settings" +msgstr "DNS პარამეტრების დაბრუნება" + +#: src/network/org.freedesktop.network1.policy:133 +msgid "Authentication is required to reset DNS settings." +msgstr "DNS პარამეტრების დაბრუნებისთვის საჭიროა ავთენტიკაცია." + +#: src/network/org.freedesktop.network1.policy:143 +msgid "DHCP server sends force renew message" +msgstr "DHCP სერვერმა ნაძალადევი განახლების შეტყობინება გამოაგზავნა" + +#: src/network/org.freedesktop.network1.policy:144 +msgid "Authentication is required to send force renew message." +msgstr "ნაძალადევი განახლების შეტყობინების გასაგზავნად საჭიროა ავთენტიკაცია." + +#: src/network/org.freedesktop.network1.policy:154 +msgid "Renew dynamic addresses" +msgstr "დინამიური მისამართების განახლება" + +#: src/network/org.freedesktop.network1.policy:155 +msgid "Authentication is required to renew dynamic addresses." +msgstr "დინამიური მისამართების განახლებისთვის საჭიროა ავთენტიკაცია." + +#: src/network/org.freedesktop.network1.policy:165 +msgid "Reload network settings" +msgstr "ქსელის პარამეტრების გადატვირთვა" + +#: src/network/org.freedesktop.network1.policy:166 +msgid "Authentication is required to reload network settings." +msgstr "ქსელის პარამეტრების გადასატვირთად საჭიროა ავთენტიკაცია." + +#: src/network/org.freedesktop.network1.policy:176 +msgid "Reconfigure network interface" +msgstr "ქსელური ინტერფეისის თავიდან მორგება" + +#: src/network/org.freedesktop.network1.policy:177 +msgid "Authentication is required to reconfigure network interface." +msgstr "ქსელური ინტერფეისის თავიდან მოსარგებად საჭიროა ავთენტიკაცია." + +#: src/portable/org.freedesktop.portable1.policy:13 +msgid "Inspect a portable service image" +msgstr "გადატანადი სერვისის იმიჯის შემოწმება" + +#: src/portable/org.freedesktop.portable1.policy:14 +msgid "Authentication is required to inspect a portable service image." +msgstr "გადატანადი სერვისის იმიჯის შესამოწმებლად საჭიროა ავთენტიკაცია." + +#: src/portable/org.freedesktop.portable1.policy:23 +msgid "Attach or detach a portable service image" +msgstr "გადატანადი სერვისის იმიჯის მოძრობა ან მიმაგრება" + +#: src/portable/org.freedesktop.portable1.policy:24 +msgid "" +"Authentication is required to attach or detach a portable service image." +msgstr "" +"გადატანადი სერვისის იმიჯის მოსაძრობად ან მისამაგრებლად საჭიროა ავთენტიკაცია." + +#: src/portable/org.freedesktop.portable1.policy:34 +msgid "Delete or modify portable service image" +msgstr "გადატანადი სერვისის იმიჯის წაშლა ან შეცვლა" + +#: src/portable/org.freedesktop.portable1.policy:35 +msgid "" +"Authentication is required to delete or modify a portable service image." +msgstr "" +"გადატანადი სერვისის იმიჯის წასაშლელად ან შესაცვლელად საჭიროა ავთენტიკაცია." + +#: src/resolve/org.freedesktop.resolve1.policy:22 +msgid "Register a DNS-SD service" +msgstr "DNS-SD სერვისის რეგისტრაცია" + +#: src/resolve/org.freedesktop.resolve1.policy:23 +msgid "Authentication is required to register a DNS-SD service" +msgstr "DNS-SD სერვისის რეგისტრაციისთვის საჭიროა ავთენტიკაცია" + +#: src/resolve/org.freedesktop.resolve1.policy:33 +msgid "Unregister a DNS-SD service" +msgstr "DNS-SD სერვისის მოცილება" + +#: src/resolve/org.freedesktop.resolve1.policy:34 +msgid "Authentication is required to unregister a DNS-SD service" +msgstr "DNS-SD სერვისის მოსაცილებლადს აჭიროა ავთენტიკაცია" + +#: src/resolve/org.freedesktop.resolve1.policy:132 +msgid "Revert name resolution settings" +msgstr "DNS პარამეტრების დაბრუნება" + +#: src/resolve/org.freedesktop.resolve1.policy:133 +msgid "Authentication is required to reset name resolution settings." +msgstr "DNS პარამეტრების დასაბრუნებლად საჭიროა ავთენტიკაცია." + +#: src/timedate/org.freedesktop.timedate1.policy:22 +msgid "Set system time" +msgstr "სისტემური დროის დაყენება" + +#: src/timedate/org.freedesktop.timedate1.policy:23 +msgid "Authentication is required to set the system time." +msgstr "სისტემური დროის დასაყენებლად საჭიროა ავთენტიკაცია." + +#: src/timedate/org.freedesktop.timedate1.policy:33 +msgid "Set system timezone" +msgstr "სისტემური დროის სარტყლის დაყენება" + +#: src/timedate/org.freedesktop.timedate1.policy:34 +msgid "Authentication is required to set the system timezone." +msgstr "სისტემური დროის სარტყლის დასაყენებლად საჭიროა ავთენტიკაცია." + +#: src/timedate/org.freedesktop.timedate1.policy:43 +msgid "Set RTC to local timezone or UTC" +msgstr "RTC-ის ლოკალურ დროის სარტყელზე ან UTC-ზე დაყენება" + +#: src/timedate/org.freedesktop.timedate1.policy:44 +msgid "" +"Authentication is required to control whether the RTC stores the local or " +"UTC time." +msgstr "" +"RTC-ის ლოკალურ დროის სარტყელზე ან UTC-ზე დასაყენებლად საჭიროა ავთენტიკაცია." + +#: src/timedate/org.freedesktop.timedate1.policy:53 +msgid "Turn network time synchronization on or off" +msgstr "დროის ქსელური სინქრონიზაციის ჩართ/გამორთ" + +#: src/timedate/org.freedesktop.timedate1.policy:54 +msgid "" +"Authentication is required to control whether network time synchronization " +"shall be enabled." +msgstr "დროის ქსელური სინქრონიზაციის ჩართ/გამორთ-თვის საჭიროა ავთენტიკაცია." + +#: src/core/dbus-unit.c:359 +msgid "Authentication is required to start '$(unit)'." +msgstr "'$(unit)'-ის გასაშვებად საჭიროა ავთენტიკაცია." + +#: src/core/dbus-unit.c:360 +msgid "Authentication is required to stop '$(unit)'." +msgstr "'$(unit)'-ის გასაჩერებლად საჭიროა ავთენტიკაცია." + +#: src/core/dbus-unit.c:361 +msgid "Authentication is required to reload '$(unit)'." +msgstr "'$(unit)'-ის გადასატვირთად საჭიროა ავთენტიკაცია." + +#: src/core/dbus-unit.c:362 src/core/dbus-unit.c:363 +msgid "Authentication is required to restart '$(unit)'." +msgstr "'$(unit)'-ის გადასატვირთად საჭიროა ავთენტიკაცია." + +#: src/core/dbus-unit.c:535 +msgid "" +"Authentication is required to send a UNIX signal to the processes of " +"'$(unit)'." +msgstr "" +"'$(unit)'-ის პროცესებისთვის UNIX სიგნალის გასაგზავნად საჭიროა ავთნტიკაცია." + +#: src/core/dbus-unit.c:566 +msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." +msgstr "" +"'$(unit)'-ის შეცდომით გაშვების მდგომარეობის გასასუფთავებლად საჭიროა " +"ავთენტიკაცია." + +#: src/core/dbus-unit.c:599 +msgid "Authentication is required to set properties on '$(unit)'." +msgstr "'$(unit)'-ის თვისებების დასაყენებლად საჭიროა ავთენტიკაცია." + +#: src/core/dbus-unit.c:708 +msgid "" +"Authentication is required to delete files and directories associated with " +"'$(unit)'." +msgstr "" +"'$(unit)'-თან ასოცირებული ფაილების და საქაღალდეების წასაშლელად საჭიროა " +"ავთენტიკაცია." + +#: src/core/dbus-unit.c:757 +msgid "" +"Authentication is required to freeze or thaw the processes of '$(unit)' unit." +msgstr "'$(unit)'-ის პროცესების გასაყინად საჭიროა ავთენტიკაცია." diff --git a/po/kab.po b/po/kab.po index 528eb479f..0d6fb12bf 100644 --- a/po/kab.po +++ b/po/kab.po @@ -4,7 +4,6 @@ # Slimane Selyan Amiri , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-10-03 16:04+0000\n" diff --git a/po/ko.po b/po/ko.po index 8f5e1e6a9..48f9d3d17 100644 --- a/po/ko.po +++ b/po/ko.po @@ -6,7 +6,6 @@ # simmon , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-06-22 10:04+0000\n" diff --git a/po/lt.po b/po/lt.po index c8eac8590..2a9b62a70 100644 --- a/po/lt.po +++ b/po/lt.po @@ -3,7 +3,6 @@ # Moo, 2018. #zanata msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2019-04-08 22:01+0300\n" diff --git a/po/nl.po b/po/nl.po index b02f6c43b..6285450d6 100644 --- a/po/nl.po +++ b/po/nl.po @@ -4,7 +4,6 @@ # Pjotr Vertaalt , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-03-24 09:16+0000\n" diff --git a/po/pa.po b/po/pa.po index 892a8e105..4a71ddaf2 100644 --- a/po/pa.po +++ b/po/pa.po @@ -3,7 +3,6 @@ # A S Alam , 2020, 2021. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-01-24 16:38+0000\n" diff --git a/po/pl.po b/po/pl.po index a4251187f..e16500325 100644 --- a/po/pl.po +++ b/po/pl.po @@ -4,7 +4,6 @@ # msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2020-10-18 13:10+0200\n" diff --git a/po/pt.po b/po/pt.po index f8aea6f87..8bec31bd5 100644 --- a/po/pt.po +++ b/po/pt.po @@ -1,13 +1,12 @@ # SPDX-License-Identifier: LGPL-2.1-or-later # # Portuguese translation of systemd. -# Hugo Carvalho , 2021. +# Hugo Carvalho , 2021, 2022. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" -"PO-Revision-Date: 2021-09-16 18:04+0000\n" +"PO-Revision-Date: 2022-01-13 11:16+0000\n" "Last-Translator: Hugo Carvalho \n" "Language-Team: Portuguese \n" @@ -16,18 +15,18 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.8\n" +"X-Generator: Weblate 4.10.1\n" #: src/core/org.freedesktop.systemd1.policy.in:22 msgid "Send passphrase back to system" -msgstr "Enviar frase secreta de volta ao sistema" +msgstr "Enviar a palavra-passe de volta ao sistema" #: src/core/org.freedesktop.systemd1.policy.in:23 msgid "" "Authentication is required to send the entered passphrase back to the system." msgstr "" -"É necessária autenticação para enviar a frase secreta informada de volta ao " -"sistema." +"É necessária autenticação para enviar a palavra-passe introduzida de volta " +"ao sistema." #: src/core/org.freedesktop.systemd1.policy.in:33 msgid "Manage system services or other units" @@ -45,21 +44,22 @@ msgstr "Gerir ficheiros de unidades e serviços do sistema" #: src/core/org.freedesktop.systemd1.policy.in:44 msgid "Authentication is required to manage system service or unit files." msgstr "" -"É necessária autenticação para gerir ficheiros “unit” e “service” do sistema." +"É necessária a autenticação para gerir o serviço do sistema ou ficheiros de " +"unidades." #: src/core/org.freedesktop.systemd1.policy.in:54 msgid "Set or unset system and service manager environment variables" msgstr "" -"Definir ou retirar definição de variáveis de ambiente de gerenciador de " -"serviço e sistema" +"Definir ou retirar definição de variáveis do ambiente de gestor de serviço e " +"sistema" #: src/core/org.freedesktop.systemd1.policy.in:55 msgid "" "Authentication is required to set or unset system and service manager " "environment variables." msgstr "" -"É necessária autenticação para definir ou retirar definição de variáveis de " -"ambiente de gerenciador de serviço e sistema." +"É necessária autenticação para definir ou retirar definição de variáveis do " +"ambiente de gestor de serviço e sistema." #: src/core/org.freedesktop.systemd1.policy.in:64 msgid "Reload the systemd state" diff --git a/po/pt_BR.po b/po/pt_BR.po index 89d8467e9..9da6cebc8 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -7,7 +7,6 @@ # Gustavo Costa , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-08-17 07:04+0000\n" diff --git a/po/ro.po b/po/ro.po index 1dd04fc21..d3fb332ac 100644 --- a/po/ro.po +++ b/po/ro.po @@ -6,7 +6,6 @@ # Vlad , 2020, 2021. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-01-12 17:36+0000\n" diff --git a/po/ru.po b/po/ru.po index e3cd42a62..1009a85a1 100644 --- a/po/ru.po +++ b/po/ru.po @@ -8,7 +8,6 @@ # Alexey Rubtsov , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-07-02 09:04+0000\n" diff --git a/po/si.po b/po/si.po index 8c9e870cf..c7ceb1ecc 100644 --- a/po/si.po +++ b/po/si.po @@ -4,7 +4,6 @@ # Hela Basa , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-08-19 07:04+0000\n" diff --git a/po/sk.po b/po/sk.po index 5d9d28c4e..044c4ac47 100644 --- a/po/sk.po +++ b/po/sk.po @@ -5,7 +5,6 @@ # Frantisek Sumsal , 2021. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-02-22 20:21+0000\n" diff --git a/po/sr.po b/po/sr.po index df2f59d46..de47076b0 100644 --- a/po/sr.po +++ b/po/sr.po @@ -4,7 +4,6 @@ # Frantisek Sumsal , 2021. msgid "" msgstr "" -"Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-02-23 22:40+0000\n" diff --git a/po/sv.po b/po/sv.po index 5d141c274..af80c0e7e 100644 --- a/po/sv.po +++ b/po/sv.po @@ -8,7 +8,6 @@ # Luna Jernberg , 2020. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-02-10 15:40+0000\n" diff --git a/po/systemd.pot b/po/systemd.pot index 8e900b0d7..c1f0066e4 100644 --- a/po/systemd.pot +++ b/po/systemd.pot @@ -3,7 +3,6 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" diff --git a/po/tr.po b/po/tr.po index df901f16d..8a752aa81 100644 --- a/po/tr.po +++ b/po/tr.po @@ -7,7 +7,6 @@ # Muhammet Kara , 2015-2020. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2020-11-28 13:35+0000\n" diff --git a/po/uk.po b/po/uk.po index 6343db3a8..37fec88f8 100644 --- a/po/uk.po +++ b/po/uk.po @@ -6,7 +6,6 @@ # Yuri Chornoivan , 2019, 2020, 2021. msgid "" msgstr "" -"Project-Id-Version: systemd master\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-01-09 10:51+0000\n" diff --git a/po/zh_CN.po b/po/zh_CN.po index 2b5f3969a..b283b90af 100644 --- a/po/zh_CN.po +++ b/po/zh_CN.po @@ -8,7 +8,6 @@ # Whired Planck , 2020. msgid "" msgstr "" -"Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-03-01 09:40+0000\n" diff --git a/po/zh_TW.po b/po/zh_TW.po index 4b297aa43..68ade44f8 100644 --- a/po/zh_TW.po +++ b/po/zh_TW.po @@ -5,7 +5,6 @@ # pan93412 , 2019. msgid "" msgstr "" -"Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-08 17:48+0100\n" "PO-Revision-Date: 2021-08-10 11:36+0800\n" diff --git a/rules.d/50-udev-default.rules.in b/rules.d/50-udev-default.rules.in index 8fae58f11..9cdaadb4c 100644 --- a/rules.d/50-udev-default.rules.in +++ b/rules.d/50-udev-default.rules.in @@ -12,7 +12,6 @@ SUBSYSTEM=="rtc", KERNEL=="rtc0", SYMLINK+="rtc", OPTIONS+="link_priority=-100" SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="usb_id", IMPORT{builtin}="hwdb --subsystem=usb" ENV{MODALIAS}!="", IMPORT{builtin}="hwdb --subsystem=$env{SUBSYSTEM}" -SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", IMPORT{builtin}="hwdb 'usb:v$attr{idVendor}p$attr{idProduct}'" ACTION!="add", GOTO="default_end" @@ -106,7 +105,13 @@ SUBSYSTEM=="ptp", ATTR{clock_name}=="KVM virtual PTP", SYMLINK += "ptp_kvm" SUBSYSTEM=="ptp", ATTR{clock_name}=="hyperv", SYMLINK += "ptp_hyperv" -SUBSYSTEM=="dmi", ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="", ENV{ID_VENDOR}="$attr{sys_vendor}", ENV{ID_MODEL}="$attr{product_name}" -SUBSYSTEM=="dmi", ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="product_version", ENV{ID_VENDOR}="$attr{sys_vendor}", ENV{ID_MODEL}="$attr{product_version}" +SUBSYSTEM!="dmi", GOTO="dmi_end" +ENV{ID_VENDOR}="$attr{sys_vendor}" +ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="|product_name", ENV{ID_MODEL}="$attr{product_name}" +ENV{ID_SYSFS_ATTRIBUTE_MODEL}=="product_version", ENV{ID_MODEL}="$attr{product_version}" +# fallback to board information +ENV{ID_VENDOR}=="", ENV{ID_VENDOR}="$attr{board_vendor}" +ENV{ID_MODEL}=="", ENV{ID_MODEL}="$attr{board_name}" +LABEL="dmi_end" LABEL="default_end" diff --git a/rules.d/60-persistent-storage-tape.rules b/rules.d/60-persistent-storage-tape.rules index 0136140a4..607e51ae2 100644 --- a/rules.d/60-persistent-storage-tape.rules +++ b/rules.d/60-persistent-storage-tape.rules @@ -7,7 +7,7 @@ ENV{UDEV_DISABLE_PERSISTENT_STORAGE_RULES_FLAG}=="1", GOTO="persistent_storage_t # type 8 devices are "Medium Changers" SUBSYSTEM=="scsi_generic", SUBSYSTEMS=="scsi", ATTRS{type}=="8", IMPORT{program}="scsi_id --sg-version=3 --export --whitelisted -d $devnode", \ - SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL}" + SYMLINK+="tape/by-id/scsi-$env{ID_SERIAL} tape/by-id/scsi-$env{ID_SERIAL}-changer" # iSCSI devices from the same host have all the same ID_SERIAL, # but additionally a property named ID_SCSI_SERIAL. @@ -23,7 +23,7 @@ KERNEL=="st*[0-9]|nst*[0-9]", ATTRS{ieee1394_id}=="?*", ENV{ID_SERIAL}="$attr{ie KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="usb", IMPORT{builtin}="usb_id" KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", SUBSYSTEMS=="scsi", KERNELS=="[0-9]*:*[0-9]", ENV{.BSG_DEV}="$root/bsg/$id" KERNEL=="st*[0-9]|nst*[0-9]", ENV{ID_SERIAL}!="?*", IMPORT{program}="scsi_id --whitelisted --export --device=$env{.BSG_DEV}", ENV{ID_BUS}="scsi" -KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}" +KERNEL=="st*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}", OPTIONS+="link_priority=10" KERNEL=="st*[0-9]", ENV{ID_SCSI_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SCSI_SERIAL}" KERNEL=="nst*[0-9]", ENV{ID_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SERIAL}-nst" KERNEL=="nst*[0-9]", ENV{ID_SCSI_SERIAL}=="?*", SYMLINK+="tape/by-id/$env{ID_BUS}-$env{ID_SCSI_SERIAL}-nst" diff --git a/shell-completion/bash/bootctl b/shell-completion/bash/bootctl index 190e3d33f..11062e1b7 100644 --- a/shell-completion/bash/bootctl +++ b/shell-completion/bash/bootctl @@ -32,7 +32,7 @@ _bootctl() { local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} local -A OPTS=( [STANDALONE]='-h --help -p --print-esp-path -x --print-boot-path --version --no-variables --no-pager --graceful' - [ARG]='--esp-path --boot-path' + [ARG]='--esp-path --boot-path --make-machine-id-directory' ) if __contains_word "$prev" ${OPTS[ARG]}; then @@ -45,6 +45,9 @@ _bootctl() { fi compopt -o filenames ;; + --make-machine-id-directory) + comps="yes no auto" + ;; esac COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) return 0 diff --git a/shell-completion/bash/meson.build b/shell-completion/bash/meson.build index c6668e5ea..963a11b6c 100644 --- a/shell-completion/bash/meson.build +++ b/shell-completion/bash/meson.build @@ -40,6 +40,7 @@ items = [['busctl', ''], ['loginctl', 'ENABLE_LOGIND'], ['machinectl', 'ENABLE_MACHINED'], ['networkctl', 'ENABLE_NETWORKD'], + ['oomctl', 'ENABLE_OOMD'], ['portablectl', 'ENABLE_PORTABLED'], ['resolvectl', 'ENABLE_RESOLVE'], ['systemd-resolve', 'ENABLE_RESOLVE'], diff --git a/shell-completion/bash/oomctl b/shell-completion/bash/oomctl new file mode 100644 index 000000000..cc778199c --- /dev/null +++ b/shell-completion/bash/oomctl @@ -0,0 +1,57 @@ +# oomctl(1) completion -*- shell-script -*- +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done +} + +_oomctl() { + local i verb comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local OPTS='-h --help --version --no-pager' + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + local -A VERBS=( + [STANDALONE]='help dump' + ) + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z ${verb-} ]]; then + comps=${VERBS[*]} + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + comps='' + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _oomctl oomctl diff --git a/shell-completion/bash/udevadm b/shell-completion/bash/udevadm index 08b4ab43a..23ce02365 100644 --- a/shell-completion/bash/udevadm +++ b/shell-completion/bash/udevadm @@ -51,10 +51,12 @@ _udevadm() { [INFO_STANDALONE]='-r --root -a --attribute-walk -x --export -e --export-db -c --cleanup-db -w --wait-for-initialization --value' [INFO_ARG]='-q --query -p --path -n --name -P --export-prefix -d --device-id-of-file --property' - [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid' + [TRIGGER_STANDALONE]='-v --verbose -n --dry-run -q --quiet -w --settle --wait-daemon --uuid + --initialized-match --initialized-nomatch' [TRIGGER_ARG]='-t --type -c --action -s --subsystem-match -S --subsystem-nomatch -a --attr-match -A --attr-nomatch -p --property-match - -g --tag-match -y --sysname-match --name-match -b --parent-match' + -g --tag-match -y --sysname-match --name-match -b --parent-match + --prioritized-subsystem' [SETTLE]='-t --timeout -E --exit-if-exists' [CONTROL_STANDALONE]='-e --exit -s --stop-exec-queue -S --start-exec-queue -R --reload --ping' [CONTROL_ARG]='-l --log-priority -p --property -m --children-max -t --timeout' @@ -117,7 +119,7 @@ _udevadm() { if __contains_word "$prev" ${OPTS[TRIGGER_ARG]}; then case $prev in -t|--type) - comps='devices subsystems' + comps='all devices subsystems' ;; -c|--action) comps=$( udevadm trigger --action help ) diff --git a/shell-completion/zsh/_bootctl b/shell-completion/zsh/_bootctl index 87ecbe37c..eef48d71d 100644 --- a/shell-completion/zsh/_bootctl +++ b/shell-completion/zsh/_bootctl @@ -69,6 +69,7 @@ _arguments \ '--boot-path=[Path to the $BOOT partition]:path:_directories' \ {-p,--print-esp-path}'[Print path to the EFI system partition]' \ {-x,--print-boot-path}'[Print path to the $BOOT partition]' \ + '--make-machine-id-directory=[Control creation and deletion of the top-level machine ID directory.]:options:(yes no auto)' \ '--no-variables[Do not touch EFI variables]' \ '--no-pager[Do not pipe output into a pager]' \ '--graceful[Do not fail when locating ESP or writing fails]' \ diff --git a/shell-completion/zsh/_busctl b/shell-completion/zsh/_busctl index ae87ddc9a..6b8d8a2ea 100644 --- a/shell-completion/zsh/_busctl +++ b/shell-completion/zsh/_busctl @@ -25,7 +25,7 @@ "list:List bus names" "status:Show bus service, process or bus owner credentials" "monitor:Show bus traffic" - "capture:Capture bus traffix as pcap" + "capture:Capture bus traffic" "tree:Show object tree of service" "introspect:Introspect object" "call:Call a method" diff --git a/shell-completion/zsh/_oomctl b/shell-completion/zsh/_oomctl new file mode 100644 index 000000000..f956340b7 --- /dev/null +++ b/shell-completion/zsh/_oomctl @@ -0,0 +1,28 @@ +#compdef oomctl +# SPDX-License-Identifier: LGPL-2.1-or-later + +(( $+functions[_oomctl_commands] )) || _oomctl_commands() +{ + local -a _oomctl_cmds + _oomctl_cmds=( + "dump:Show the current state of the cgroup(s) and system context(s)" + "help:Prints a short help text and exits." + ) + if (( CURRENT == 1 )); then + _describe -t commands 'oomctl command' _oomctl_cmds + else + local curcontext="$curcontext" + cmd="${${_oomctl_cmds[(r)$words[1]:*]%%:*}}" + if (( $+functions[_oomctl_$cmd] )); then + _oomctl_$cmd + else + _message "no more options" + fi + fi +} + +_arguments \ + {-h,--help}'[Prints a short help text and exits.]' \ + '--version[Prints a short version string and exits.]' \ + '--no-pager[Do not pipe output into a pager]' \ + '*::oomctl command:_oomctl_commands' diff --git a/shell-completion/zsh/_udevadm b/shell-completion/zsh/_udevadm index 14efe2480..63df8b7c9 100644 --- a/shell-completion/zsh/_udevadm +++ b/shell-completion/zsh/_udevadm @@ -24,7 +24,7 @@ _udevadm_trigger(){ '--verbose[Print the list of devices which will be triggered.]' \ '--dry-run[Do not actually trigger the event.]' \ '--quiet[Suppress error logging in triggering events.]' \ - '--type=[Trigger a specific type of devices.]:types:(devices subsystems failed)' \ + '--type=[Trigger a specific type of devices.]:types:(all devices subsystems failed)' \ '--action=[Type of event to be triggered.]:actions:(add change remove move online offline bind unbind)' \ '--subsystem-match=[Trigger events for devices which belong to a matching subsystem.]' \ '--subsystem-nomatch=[Do not trigger events for devices which belong to a matching subsystem.]' \ @@ -34,7 +34,10 @@ _udevadm_trigger(){ '--tag-match=property[Trigger events for devices with a matching tag.]' \ '--sysname-match=[Trigger events for devices with a matching sys device name.]' \ '--parent-match=[Trigger events for all children of a given device.]' \ - '--uuid[Print synthetic uevent UUID.]' + '--initialized-match[Trigger events for devices that are already initialized.]' \ + '--initialized-nomatch[Trigger events for devices that are not initialized yet.]' \ + '--uuid[Print synthetic uevent UUID.]' \ + '--prioritized-subsystem=[Trigger events for devices which belong to a matching subsystem earlier.]' } (( $+functions[_udevadm_settle] )) || diff --git a/shell-completion/zsh/meson.build b/shell-completion/zsh/meson.build index a0615c4df..6dca9dd59 100644 --- a/shell-completion/zsh/meson.build +++ b/shell-completion/zsh/meson.build @@ -34,6 +34,7 @@ items = [['_busctl', ''], ['_loginctl', 'ENABLE_LOGIND'], ['_machinectl', 'ENABLE_MACHINED'], ['_networkctl', 'ENABLE_NETWORKD'], + ['_oomctl', 'ENABLE_OOMD'], ['_systemd-inhibit', 'ENABLE_LOGIND'], ['_resolvectl', 'ENABLE_RESOLVE'], ['_systemd-tmpfiles', 'ENABLE_TMPFILES'], diff --git a/src/activate/activate.c b/src/activate/activate.c index 0c3215267..8ee7a3ec6 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -49,7 +49,6 @@ static int add_epoll(int epoll_fd, int fd) { } static int open_sockets(int *epoll_fd, bool accept) { - char **address; int n, fd, r, count = 0; n = sd_listen_fds(true); @@ -124,8 +123,6 @@ static int open_sockets(int *epoll_fd, bool accept) { static int exec_process(const char *name, char **argv, int start_fd, size_t n_fds) { _cleanup_strv_free_ char **envp = NULL; - const char *var; - char **s; int r; if (arg_inetd && n_fds != 1) @@ -390,7 +387,6 @@ static int parse_argv(int argc, char *argv[]) { case ARG_FDNAME: { _cleanup_strv_free_ char **names = NULL; - char **s; names = strv_split(optarg, ":"); if (!names) diff --git a/src/analyze/analyze-blame.c b/src/analyze/analyze-blame.c new file mode 100644 index 000000000..6cd684695 --- /dev/null +++ b/src/analyze/analyze-blame.c @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-blame.h" +#include "analyze-time-data.h" +#include "format-table.h" + +int verb_blame(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL; + _cleanup_(table_unrefp) Table *table = NULL; + TableCell *cell; + int n, r; + + r = acquire_bus(&bus, NULL); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + n = acquire_time_data(bus, ×); + if (n <= 0) + return n; + + table = table_new("time", "unit"); + if (!table) + return log_oom(); + + table_set_header(table, false); + + assert_se(cell = table_get_cell(table, 0, 0)); + r = table_set_ellipsize_percent(table, cell, 100); + if (r < 0) + return r; + + r = table_set_align_percent(table, cell, 100); + if (r < 0) + return r; + + assert_se(cell = table_get_cell(table, 0, 1)); + r = table_set_ellipsize_percent(table, cell, 100); + if (r < 0) + return r; + + r = table_set_sort(table, (size_t) 0); + if (r < 0) + return r; + + r = table_set_reverse(table, 0, true); + if (r < 0) + return r; + + for (UnitTimes *u = times; u->has_data; u++) { + if (u->time <= 0) + continue; + + r = table_add_many(table, + TABLE_TIMESPAN_MSEC, u->time, + TABLE_STRING, u->name); + if (r < 0) + return table_log_add_error(r); + } + + pager_open(arg_pager_flags); + + return table_print(table, NULL); +} diff --git a/src/analyze/analyze-blame.h b/src/analyze/analyze-blame.h new file mode 100644 index 000000000..d9aa985c1 --- /dev/null +++ b/src/analyze/analyze-blame.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_blame(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-calendar.c b/src/analyze/analyze-calendar.c new file mode 100644 index 000000000..8f6517fa8 --- /dev/null +++ b/src/analyze/analyze-calendar.c @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-calendar.h" +#include "calendarspec.h" +#include "format-table.h" +#include "terminal-util.h" + +static int test_calendar_one(usec_t n, const char *p) { + _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL; + _cleanup_(table_unrefp) Table *table = NULL; + _cleanup_free_ char *t = NULL; + TableCell *cell; + int r; + + r = calendar_spec_from_string(p, &spec); + if (r < 0) { + log_error_errno(r, "Failed to parse calendar specification '%s': %m", p); + time_parsing_hint(p, /* calendar= */ false, /* timestamp= */ true, /* timespan= */ true); + return r; + } + + r = calendar_spec_to_string(spec, &t); + if (r < 0) + return log_error_errno(r, "Failed to format calendar specification '%s': %m", p); + + table = table_new("name", "value"); + if (!table) + return log_oom(); + + table_set_header(table, false); + + assert_se(cell = table_get_cell(table, 0, 0)); + r = table_set_ellipsize_percent(table, cell, 100); + if (r < 0) + return r; + + r = table_set_align_percent(table, cell, 100); + if (r < 0) + return r; + + assert_se(cell = table_get_cell(table, 0, 1)); + r = table_set_ellipsize_percent(table, cell, 100); + if (r < 0) + return r; + + if (!streq(t, p)) { + r = table_add_many(table, + TABLE_STRING, "Original form:", + TABLE_STRING, p); + if (r < 0) + return table_log_add_error(r); + } + + r = table_add_many(table, + TABLE_STRING, "Normalized form:", + TABLE_STRING, t); + if (r < 0) + return table_log_add_error(r); + + for (unsigned i = 0; i < arg_iterations; i++) { + usec_t next; + + r = calendar_spec_next_usec(spec, n, &next); + if (r == -ENOENT) { + if (i == 0) { + r = table_add_many(table, + TABLE_STRING, "Next elapse:", + TABLE_STRING, "never", + TABLE_SET_COLOR, ansi_highlight_yellow()); + if (r < 0) + return table_log_add_error(r); + } + break; + } + if (r < 0) + return log_error_errno(r, "Failed to determine next elapse for '%s': %m", p); + + if (i == 0) { + r = table_add_many(table, + TABLE_STRING, "Next elapse:", + TABLE_TIMESTAMP, next, + TABLE_SET_COLOR, ansi_highlight_blue()); + if (r < 0) + return table_log_add_error(r); + } else { + int k = DECIMAL_STR_WIDTH(i + 1); + + if (k < 8) + k = 8 - k; + else + k = 0; + + r = table_add_cell_stringf(table, NULL, "Iter. #%u:", i+1); + if (r < 0) + return table_log_add_error(r); + + r = table_add_many(table, + TABLE_TIMESTAMP, next, + TABLE_SET_COLOR, ansi_highlight_blue()); + if (r < 0) + return table_log_add_error(r); + } + + if (!in_utc_timezone()) { + r = table_add_many(table, + TABLE_STRING, "(in UTC):", + TABLE_TIMESTAMP_UTC, next); + if (r < 0) + return table_log_add_error(r); + } + + r = table_add_many(table, + TABLE_STRING, "From now:", + TABLE_TIMESTAMP_RELATIVE, next); + if (r < 0) + return table_log_add_error(r); + + n = next; + } + + return table_print(table, NULL); +} + +int verb_calendar(int argc, char *argv[], void *userdata) { + int ret = 0, r; + usec_t n; + + if (arg_base_time != USEC_INFINITY) + n = arg_base_time; + else + n = now(CLOCK_REALTIME); /* We want to use the same "base" for all expressions */ + + STRV_FOREACH(p, strv_skip(argv, 1)) { + r = test_calendar_one(n, *p); + if (ret == 0 && r < 0) + ret = r; + + if (*(p + 1)) + putchar('\n'); + } + + return ret; +} diff --git a/src/analyze/analyze-calendar.h b/src/analyze/analyze-calendar.h new file mode 100644 index 000000000..3d6eac200 --- /dev/null +++ b/src/analyze/analyze-calendar.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_calendar(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-capability.c b/src/analyze/analyze-capability.c new file mode 100644 index 000000000..ebb205e7f --- /dev/null +++ b/src/analyze/analyze-capability.c @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-capability.h" +#include "cap-list.h" +#include "capability-util.h" +#include "format-table.h" + +int verb_capabilities(int argc, char *argv[], void *userdata) { + _cleanup_(table_unrefp) Table *table = NULL; + unsigned last_cap; + int r; + + table = table_new("name", "number"); + if (!table) + return log_oom(); + + (void) table_set_align_percent(table, table_get_cell(table, 0, 1), 100); + + /* Determine the maximum of the last cap known by the kernel and by us */ + last_cap = MAX((unsigned) CAP_LAST_CAP, cap_last_cap()); + + if (strv_isempty(strv_skip(argv, 1))) + for (unsigned c = 0; c <= last_cap; c++) { + r = table_add_many(table, + TABLE_STRING, capability_to_name(c) ?: "cap_???", + TABLE_UINT, c); + if (r < 0) + return table_log_add_error(r); + } + else { + for (int i = 1; i < argc; i++) { + int c; + + c = capability_from_name(argv[i]); + if (c < 0 || (unsigned) c > last_cap) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Capability \"%s\" not known.", argv[i]); + + r = table_add_many(table, + TABLE_STRING, capability_to_name(c) ?: "cap_???", + TABLE_UINT, (unsigned) c); + if (r < 0) + return table_log_add_error(r); + } + + (void) table_set_sort(table, (size_t) 1); + } + + pager_open(arg_pager_flags); + + return table_print(table, NULL); +} diff --git a/src/analyze/analyze-capability.h b/src/analyze/analyze-capability.h new file mode 100644 index 000000000..07ff0887f --- /dev/null +++ b/src/analyze/analyze-capability.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_capabilities(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-cat-config.c b/src/analyze/analyze-cat-config.c new file mode 100644 index 000000000..85ed8b01e --- /dev/null +++ b/src/analyze/analyze-cat-config.c @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-cat-config.h" +#include "conf-files.h" +#include "def.h" +#include "nulstr-util.h" +#include "path-util.h" +#include "pretty-print.h" +#include "strv.h" + +int verb_cat_config(int argc, char *argv[], void *userdata) { + char **list; + int r; + + pager_open(arg_pager_flags); + + list = strv_skip(argv, 1); + STRV_FOREACH(arg, list) { + const char *t = NULL; + + if (arg != list) + print_separator(); + + if (path_is_absolute(*arg)) { + const char *dir; + + NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) { + t = path_startswith(*arg, dir); + if (t) + break; + } + + if (!t) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Path %s does not start with any known prefix.", *arg); + } else + t = *arg; + + r = conf_files_cat(arg_root, t); + if (r < 0) + return r; + } + + return 0; +} diff --git a/src/analyze/analyze-cat-config.h b/src/analyze/analyze-cat-config.h new file mode 100644 index 000000000..64e87a3a6 --- /dev/null +++ b/src/analyze/analyze-cat-config.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_cat_config(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-condition.c b/src/analyze/analyze-condition.c index f57bb27b9..011066f14 100644 --- a/src/analyze/analyze-condition.c +++ b/src/analyze/analyze-condition.c @@ -2,8 +2,9 @@ #include +#include "analyze.h" #include "analyze-condition.h" -#include "analyze-verify.h" +#include "analyze-verify-util.h" #include "condition.h" #include "conf-parser.h" #include "load-fragment.h" @@ -73,7 +74,7 @@ static int log_helper(void *userdata, int level, int error, const char *file, in return r; } -int verify_conditions(char **lines, UnitFileScope scope, const char *unit, const char *root) { +static int verify_conditions(char **lines, LookupScope scope, const char *unit, const char *root) { _cleanup_(manager_freep) Manager *m = NULL; Unit *u; int r, q = 1; @@ -113,8 +114,6 @@ int verify_conditions(char **lines, UnitFileScope scope, const char *unit, const if (r < 0) return r; } else { - char **line; - r = unit_new_for_name(m, sizeof(Service), "test.service", &u); if (r < 0) return log_error_errno(r, "Failed to create test.service: %m"); @@ -136,3 +135,7 @@ int verify_conditions(char **lines, UnitFileScope scope, const char *unit, const return r > 0 && q > 0 ? 0 : -EIO; } + +int verb_condition(int argc, char *argv[], void *userdata) { + return verify_conditions(strv_skip(argv, 1), arg_scope, arg_unit, arg_root); +} diff --git a/src/analyze/analyze-condition.h b/src/analyze/analyze-condition.h index 9ebd205b6..28ef51a45 100644 --- a/src/analyze/analyze-condition.h +++ b/src/analyze/analyze-condition.h @@ -1,6 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include "install.h" - -int verify_conditions(char **lines, UnitFileScope scope, const char *unit, const char *root); +int verb_condition(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-critical-chain.c b/src/analyze/analyze-critical-chain.c new file mode 100644 index 000000000..3a6b77705 --- /dev/null +++ b/src/analyze/analyze-critical-chain.c @@ -0,0 +1,235 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-critical-chain.h" +#include "analyze-time-data.h" +#include "strv.h" +#include "copy.h" +#include "path-util.h" +#include "terminal-util.h" +#include "sort-util.h" +#include "special.h" +#include "bus-error.h" + +static int list_dependencies_print( + const char *name, + unsigned level, + unsigned branches, + bool last, + UnitTimes *times, + BootTimes *boot) { + + for (unsigned i = level; i != 0; i--) + printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE)); + + printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH)); + + if (times) { + if (times->time > 0) + printf("%s%s @%s +%s%s", ansi_highlight_red(), name, + FORMAT_TIMESPAN(times->activating - boot->userspace_time, USEC_PER_MSEC), + FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal()); + else if (times->activated > boot->userspace_time) + printf("%s @%s", name, FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC)); + else + printf("%s", name); + } else + printf("%s", name); + printf("\n"); + + return 0; +} + +static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) { + _cleanup_free_ char *path = NULL; + + assert(bus); + assert(name); + assert(deps); + + path = unit_dbus_path_from_name(name); + if (!path) + return -ENOMEM; + + return bus_get_unit_property_strv(bus, path, "After", deps); +} + +static Hashmap *unit_times_hashmap; + +static int list_dependencies_compare(char *const *a, char *const *b) { + usec_t usa = 0, usb = 0; + UnitTimes *times; + + times = hashmap_get(unit_times_hashmap, *a); + if (times) + usa = times->activated; + times = hashmap_get(unit_times_hashmap, *b); + if (times) + usb = times->activated; + + return CMP(usb, usa); +} + +static bool times_in_range(const UnitTimes *times, const BootTimes *boot) { + return times && times->activated > 0 && times->activated <= boot->finish_time; +} + +static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units, unsigned branches) { + _cleanup_strv_free_ char **deps = NULL; + int r; + usec_t service_longest = 0; + int to_print = 0; + UnitTimes *times; + BootTimes *boot; + + if (strv_extend(units, name)) + return log_oom(); + + r = list_dependencies_get_dependencies(bus, name, &deps); + if (r < 0) + return r; + + typesafe_qsort(deps, strv_length(deps), list_dependencies_compare); + + r = acquire_boot_times(bus, &boot); + if (r < 0) + return r; + + STRV_FOREACH(c, deps) { + times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */ + if (times_in_range(times, boot) && times->activated >= service_longest) + service_longest = times->activated; + } + + if (service_longest == 0) + return r; + + STRV_FOREACH(c, deps) { + times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */ + if (times_in_range(times, boot) && service_longest - times->activated <= arg_fuzz) + to_print++; + } + + if (!to_print) + return r; + + STRV_FOREACH(c, deps) { + times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */ + if (!times_in_range(times, boot) || service_longest - times->activated > arg_fuzz) + continue; + + to_print--; + + r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot); + if (r < 0) + return r; + + if (strv_contains(*units, *c)) { + r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0), + true, NULL, boot); + if (r < 0) + return r; + continue; + } + + r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (to_print ? 1 : 0)); + if (r < 0) + return r; + + if (to_print == 0) + break; + } + return 0; +} + +static int list_dependencies(sd_bus *bus, const char *name) { + _cleanup_strv_free_ char **units = NULL; + UnitTimes *times; + int r; + const char *id; + _cleanup_free_ char *path = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + BootTimes *boot; + + assert(bus); + + path = unit_dbus_path_from_name(name); + if (!path) + return -ENOMEM; + + r = sd_bus_get_property( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "Id", + &error, + &reply, + "s"); + if (r < 0) + return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "s", &id); + if (r < 0) + return bus_log_parse_error(r); + + times = hashmap_get(unit_times_hashmap, id); + + r = acquire_boot_times(bus, &boot); + if (r < 0) + return r; + + if (times) { + if (times->time) + printf("%s%s +%s%s\n", ansi_highlight_red(), id, + FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal()); + else if (times->activated > boot->userspace_time) + printf("%s @%s\n", id, + FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC)); + else + printf("%s\n", id); + } + + return list_dependencies_one(bus, name, 0, &units, 0); +} + +int verb_critical_chain(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL; + Hashmap *h; + int n, r; + + r = acquire_bus(&bus, NULL); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + n = acquire_time_data(bus, ×); + if (n <= 0) + return n; + + h = hashmap_new(&string_hash_ops); + if (!h) + return log_oom(); + + for (UnitTimes *u = times; u->has_data; u++) { + r = hashmap_put(h, u->name, u); + if (r < 0) + return log_error_errno(r, "Failed to add entry to hashmap: %m"); + } + unit_times_hashmap = h; + + pager_open(arg_pager_flags); + + puts("The time when unit became active or started is printed after the \"@\" character.\n" + "The time the unit took to start is printed after the \"+\" character.\n"); + + if (argc > 1) + STRV_FOREACH(name, strv_skip(argv, 1)) + list_dependencies(bus, *name); + else + list_dependencies(bus, SPECIAL_DEFAULT_TARGET); + + h = hashmap_free(h); + return 0; +} diff --git a/src/analyze/analyze-critical-chain.h b/src/analyze/analyze-critical-chain.h new file mode 100644 index 000000000..844249c91 --- /dev/null +++ b/src/analyze/analyze-critical-chain.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_critical_chain(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-dot.c b/src/analyze/analyze-dot.c new file mode 100644 index 000000000..13bea4598 --- /dev/null +++ b/src/analyze/analyze-dot.c @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-dot.h" +#include "bus-error.h" +#include "bus-locator.h" +#include "bus-unit-util.h" +#include "glob-util.h" +#include "terminal-util.h" + +static int graph_one_property( + sd_bus *bus, + const UnitInfo *u, + const char *prop, + const char *color, + char *patterns[], + char *from_patterns[], + char *to_patterns[]) { + + _cleanup_strv_free_ char **units = NULL; + bool match_patterns; + int r; + + assert(u); + assert(prop); + assert(color); + + match_patterns = strv_fnmatch(patterns, u->id); + + if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id)) + return 0; + + r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units); + if (r < 0) + return r; + + STRV_FOREACH(unit, units) { + bool match_patterns2; + + match_patterns2 = strv_fnmatch(patterns, *unit); + + if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit)) + continue; + + if (!strv_isempty(patterns) && !match_patterns && !match_patterns2) + continue; + + printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color); + } + + return 0; +} + +static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) { + int r; + + assert(bus); + assert(u); + + if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) { + r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + } + + if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) { + r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns); + if (r < 0) + return r; + } + + return 0; +} + +static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { + _cleanup_strv_free_ char **expanded_patterns = NULL; + int r; + + STRV_FOREACH(pattern, patterns) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *unit = NULL, *unit_id = NULL; + + if (strv_extend(&expanded_patterns, *pattern) < 0) + return log_oom(); + + if (string_is_glob(*pattern)) + continue; + + unit = unit_dbus_path_from_name(*pattern); + if (!unit) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + unit, + "org.freedesktop.systemd1.Unit", + "Id", + &error, + &unit_id); + if (r < 0) + return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); + + if (!streq(*pattern, unit_id)) { + if (strv_extend(&expanded_patterns, unit_id) < 0) + return log_oom(); + } + } + + *ret = TAKE_PTR(expanded_patterns); /* do not free */ + + return 0; +} + +int verb_dot(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_strv_free_ char **expanded_patterns = NULL; + _cleanup_strv_free_ char **expanded_from_patterns = NULL; + _cleanup_strv_free_ char **expanded_to_patterns = NULL; + int r; + UnitInfo u; + + r = acquire_bus(&bus, NULL); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns); + if (r < 0) + return r; + + r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns); + if (r < 0) + return r; + + r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns); + if (r < 0) + return r; + + r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL); + if (r < 0) + log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) + return bus_log_parse_error(r); + + printf("digraph systemd {\n"); + + while ((r = bus_parse_unit_info(reply, &u)) > 0) { + + r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns); + if (r < 0) + return r; + } + if (r < 0) + return bus_log_parse_error(r); + + printf("}\n"); + + log_info(" Color legend: black = Requires\n" + " dark blue = Requisite\n" + " dark grey = Wants\n" + " red = Conflicts\n" + " green = After\n"); + + if (on_tty() && !arg_quiet) + log_notice("-- You probably want to process this output with graphviz' dot tool.\n" + "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n"); + + return 0; +} diff --git a/src/analyze/analyze-dot.h b/src/analyze/analyze-dot.h new file mode 100644 index 000000000..144b43d1e --- /dev/null +++ b/src/analyze/analyze-dot.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_dot(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-dump.c b/src/analyze/analyze-dump.c new file mode 100644 index 000000000..24094ce61 --- /dev/null +++ b/src/analyze/analyze-dump.c @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-bus.h" + +#include "analyze-dump.h" +#include "analyze.h" +#include "bus-error.h" +#include "bus-locator.h" +#include "bus-util.h" +#include "copy.h" + +static int dump_fallback(sd_bus *bus) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + const char *text = NULL; + int r; + + assert(bus); + + r = bus_call_method(bus, bus_systemd_mgr, "Dump", &error, &reply, NULL); + if (r < 0) + return log_error_errno(r, "Failed to issue method call Dump: %s", bus_error_message(&error, r)); + + r = sd_bus_message_read(reply, "s", &text); + if (r < 0) + return bus_log_parse_error(r); + + fputs(text, stdout); + return 0; +} + +int verb_dump(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int fd = -1; + int r; + + r = acquire_bus(&bus, NULL); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + pager_open(arg_pager_flags); + + if (!sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD)) + return dump_fallback(bus); + + r = bus_call_method(bus, bus_systemd_mgr, "DumpByFileDescriptor", &error, &reply, NULL); + if (r < 0) { + /* fall back to Dump if DumpByFileDescriptor is not supported */ + if (!IN_SET(r, -EACCES, -EBADR)) + return log_error_errno(r, "Failed to issue method call DumpByFileDescriptor: %s", + bus_error_message(&error, r)); + + return dump_fallback(bus); + } + + r = sd_bus_message_read(reply, "h", &fd); + if (r < 0) + return bus_log_parse_error(r); + + fflush(stdout); + return copy_bytes(fd, STDOUT_FILENO, UINT64_MAX, 0); +} diff --git a/src/analyze/analyze-dump.h b/src/analyze/analyze-dump.h new file mode 100644 index 000000000..5d6107cb5 --- /dev/null +++ b/src/analyze/analyze-dump.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_dump(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-elf.h b/src/analyze/analyze-elf.h deleted file mode 100644 index e0d4712e5..000000000 --- a/src/analyze/analyze-elf.h +++ /dev/null @@ -1,6 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "json.h" - -int analyze_elf(char **filenames, JsonFormatFlags json_flags); diff --git a/src/analyze/analyze-exit-status.c b/src/analyze/analyze-exit-status.c new file mode 100644 index 000000000..a3f21c60e --- /dev/null +++ b/src/analyze/analyze-exit-status.c @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-exit-status.h" +#include "exit-status.h" +#include "format-table.h" + +int verb_exit_status(int argc, char *argv[], void *userdata) { + _cleanup_(table_unrefp) Table *table = NULL; + int r; + + table = table_new("name", "status", "class"); + if (!table) + return log_oom(); + + r = table_set_align_percent(table, table_get_cell(table, 0, 1), 100); + if (r < 0) + return log_error_errno(r, "Failed to right-align status: %m"); + + if (strv_isempty(strv_skip(argv, 1))) + for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++) { + if (!exit_status_mappings[i].name) + continue; + + r = table_add_many(table, + TABLE_STRING, exit_status_mappings[i].name, + TABLE_INT, (int) i, + TABLE_STRING, exit_status_class(i)); + if (r < 0) + return table_log_add_error(r); + } + else + for (int i = 1; i < argc; i++) { + int status; + + status = exit_status_from_string(argv[i]); + if (status < 0) + return log_error_errno(status, "Invalid exit status \"%s\".", argv[i]); + + assert(status >= 0 && (size_t) status < ELEMENTSOF(exit_status_mappings)); + r = table_add_many(table, + TABLE_STRING, exit_status_mappings[status].name ?: "-", + TABLE_INT, status, + TABLE_STRING, exit_status_class(status) ?: "-"); + if (r < 0) + return table_log_add_error(r); + } + + pager_open(arg_pager_flags); + + return table_print(table, NULL); +} diff --git a/src/analyze/analyze-exit-status.h b/src/analyze/analyze-exit-status.h new file mode 100644 index 000000000..ce14cdbb9 --- /dev/null +++ b/src/analyze/analyze-exit-status.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_exit_status(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-filesystems.c b/src/analyze/analyze-filesystems.c new file mode 100644 index 000000000..66d8397e3 --- /dev/null +++ b/src/analyze/analyze-filesystems.c @@ -0,0 +1,225 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-filesystems.h" +#include "fileio.h" +#include "filesystems.h" +#include "set.h" +#include "strv.h" +#include "terminal-util.h" + +static int load_available_kernel_filesystems(Set **ret) { + _cleanup_set_free_ Set *filesystems = NULL; + _cleanup_free_ char *t = NULL; + int r; + + assert(ret); + + /* Let's read the available filesystems */ + + r = read_virtual_file("/proc/filesystems", SIZE_MAX, &t, NULL); + if (r < 0) + return r; + + for (int i = 0;;) { + _cleanup_free_ char *line = NULL; + const char *p; + + r = string_extract_line(t, i++, &line); + if (r < 0) + return log_oom(); + if (r == 0) + break; + + if (!line) + line = t; + + p = strchr(line, '\t'); + if (!p) + continue; + + p += strspn(p, WHITESPACE); + + r = set_put_strdup(&filesystems, p); + if (r < 0) + return log_error_errno(r, "Failed to add filesystem to list: %m"); + } + + *ret = TAKE_PTR(filesystems); + return 0; +} + +static void filesystem_set_remove(Set *s, const FilesystemSet *set) { + const char *filesystem; + + NULSTR_FOREACH(filesystem, set->value) { + if (filesystem[0] == '@') + continue; + + free(set_remove(s, filesystem)); + } +} + +static void dump_filesystem_set(const FilesystemSet *set) { + const char *filesystem; + int r; + + if (!set) + return; + + printf("%s%s%s\n" + " # %s\n", + ansi_highlight(), + set->name, + ansi_normal(), + set->help); + + NULSTR_FOREACH(filesystem, set->value) { + const statfs_f_type_t *magic; + + if (filesystem[0] == '@') { + printf(" %s%s%s\n", ansi_underline(), filesystem, ansi_normal()); + continue; + } + + r = fs_type_from_string(filesystem, &magic); + assert_se(r >= 0); + + printf(" %s", filesystem); + + for (size_t i = 0; magic[i] != 0; i++) { + const char *primary; + if (i == 0) + printf(" %s(magic: ", ansi_grey()); + else + printf(", "); + + printf("0x%llx", (unsigned long long) magic[i]); + + primary = fs_type_to_string(magic[i]); + if (primary && !streq(primary, filesystem)) + printf("[%s]", primary); + + if (magic[i+1] == 0) + printf(")%s", ansi_normal()); + } + + printf("\n"); + } +} + +int verb_filesystems(int argc, char *argv[], void *userdata) { + bool first = true; + +#if ! HAVE_LIBBPF + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with libbpf support, sorry."); +#endif + + pager_open(arg_pager_flags); + + if (strv_isempty(strv_skip(argv, 1))) { + _cleanup_set_free_ Set *kernel = NULL, *known = NULL; + const char *fs; + int k; + + NULSTR_FOREACH(fs, filesystem_sets[FILESYSTEM_SET_KNOWN].value) + if (set_put_strdup(&known, fs) < 0) + return log_oom(); + + k = load_available_kernel_filesystems(&kernel); + + for (FilesystemGroups i = 0; i < _FILESYSTEM_SET_MAX; i++) { + const FilesystemSet *set = filesystem_sets + i; + if (!first) + puts(""); + + dump_filesystem_set(set); + filesystem_set_remove(kernel, set); + if (i != FILESYSTEM_SET_KNOWN) + filesystem_set_remove(known, set); + first = false; + } + + if (arg_quiet) /* Let's not show the extra stuff in quiet mode */ + return 0; + + if (!set_isempty(known)) { + _cleanup_free_ char **l = NULL; + + printf("\n" + "# %sUngrouped filesystems%s (known but not included in any of the groups except @known):\n", + ansi_highlight(), ansi_normal()); + + l = set_get_strv(known); + if (!l) + return log_oom(); + + strv_sort(l); + + STRV_FOREACH(filesystem, l) { + const statfs_f_type_t *magic; + bool is_primary = false; + + assert_se(fs_type_from_string(*filesystem, &magic) >= 0); + + for (size_t i = 0; magic[i] != 0; i++) { + const char *primary; + + primary = fs_type_to_string(magic[i]); + assert(primary); + + if (streq(primary, *filesystem)) + is_primary = true; + } + + if (!is_primary) { + log_debug("Skipping ungrouped file system '%s', because it's an alias for another one.", *filesystem); + continue; + } + + printf("# %s\n", *filesystem); + } + } + + if (k < 0) { + fputc('\n', stdout); + fflush(stdout); + log_notice_errno(k, "# Not showing unlisted filesystems, couldn't retrieve kernel filesystem list: %m"); + } else if (!set_isempty(kernel)) { + _cleanup_free_ char **l = NULL; + + printf("\n" + "# %sUnlisted filesystems%s (available to the local kernel, but not included in any of the groups listed above):\n", + ansi_highlight(), ansi_normal()); + + l = set_get_strv(kernel); + if (!l) + return log_oom(); + + strv_sort(l); + + STRV_FOREACH(filesystem, l) + printf("# %s\n", *filesystem); + } + } else + STRV_FOREACH(name, strv_skip(argv, 1)) { + const FilesystemSet *set; + + if (!first) + puts(""); + + set = filesystem_set_find(*name); + if (!set) { + /* make sure the error appears below normal output */ + fflush(stdout); + + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "Filesystem set \"%s\" not found.", *name); + } + + dump_filesystem_set(set); + first = false; + } + + return 0; +} diff --git a/src/analyze/analyze-filesystems.h b/src/analyze/analyze-filesystems.h new file mode 100644 index 000000000..09045716d --- /dev/null +++ b/src/analyze/analyze-filesystems.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_filesystems(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-elf.c b/src/analyze/analyze-inspect-elf.c similarity index 95% rename from src/analyze/analyze-elf.c rename to src/analyze/analyze-inspect-elf.c index 741cd20f9..155c611c7 100644 --- a/src/analyze/analyze-elf.c +++ b/src/analyze/analyze-inspect-elf.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "analyze-elf.h" +#include "analyze.h" +#include "analyze-inspect-elf.h" #include "elf-util.h" #include "errno-util.h" #include "fd-util.h" @@ -10,8 +11,7 @@ #include "path-util.h" #include "strv.h" -int analyze_elf(char **filenames, JsonFormatFlags json_flags) { - char **filename; +static int analyze_elf(char **filenames, JsonFormatFlags json_flags) { int r; STRV_FOREACH(filename, filenames) { @@ -126,3 +126,9 @@ int analyze_elf(char **filenames, JsonFormatFlags json_flags) { return 0; } + +int verb_elf_inspection(int argc, char *argv[], void *userdata) { + pager_open(arg_pager_flags); + + return analyze_elf(strv_skip(argv, 1), arg_json_format_flags); +} diff --git a/src/analyze/analyze-inspect-elf.h b/src/analyze/analyze-inspect-elf.h new file mode 100644 index 000000000..a790eae6b --- /dev/null +++ b/src/analyze/analyze-inspect-elf.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_elf_inspection(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-log-control.c b/src/analyze/analyze-log-control.c new file mode 100644 index 000000000..d31081b6a --- /dev/null +++ b/src/analyze/analyze-log-control.c @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-log-control.h" +#include "verb-log-control.h" + +int verb_log_control(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int r; + + assert(IN_SET(argc, 1, 2)); + + r = acquire_bus(&bus, NULL); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + return verb_log_control_common(bus, "org.freedesktop.systemd1", argv[0], argc == 2 ? argv[1] : NULL); +} diff --git a/src/analyze/analyze-log-control.h b/src/analyze/analyze-log-control.h new file mode 100644 index 000000000..350c22861 --- /dev/null +++ b/src/analyze/analyze-log-control.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_log_control(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-plot.c b/src/analyze/analyze-plot.c new file mode 100644 index 000000000..edfdfba14 --- /dev/null +++ b/src/analyze/analyze-plot.c @@ -0,0 +1,395 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-plot.h" +#include "analyze-time-data.h" +#include "bus-error.h" +#include "bus-map-properties.h" +#include "sort-util.h" +#include "version.h" + +#define SCALE_X (0.1 / 1000.0) /* pixels per us */ +#define SCALE_Y (20.0) + +#define svg(...) printf(__VA_ARGS__) + +#define svg_bar(class, x1, x2, y) \ + svg(" \n", \ + (class), \ + SCALE_X * (x1), SCALE_Y * (y), \ + SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0) + +#define svg_text(b, x, y, format, ...) \ + do { \ + svg(" ", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \ + svg(format, ## __VA_ARGS__); \ + svg("\n"); \ + } while (false) + + +typedef struct HostInfo { + char *hostname; + char *kernel_name; + char *kernel_release; + char *kernel_version; + char *os_pretty_name; + char *virtualization; + char *architecture; +} HostInfo; + +static HostInfo* free_host_info(HostInfo *hi) { + if (!hi) + return NULL; + + free(hi->hostname); + free(hi->kernel_name); + free(hi->kernel_release); + free(hi->kernel_version); + free(hi->os_pretty_name); + free(hi->virtualization); + free(hi->architecture); + return mfree(hi); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(HostInfo *, free_host_info); + +static int acquire_host_info(sd_bus *bus, HostInfo **hi) { + static const struct bus_properties_map hostname_map[] = { + { "Hostname", "s", NULL, offsetof(HostInfo, hostname) }, + { "KernelName", "s", NULL, offsetof(HostInfo, kernel_name) }, + { "KernelRelease", "s", NULL, offsetof(HostInfo, kernel_release) }, + { "KernelVersion", "s", NULL, offsetof(HostInfo, kernel_version) }, + { "OperatingSystemPrettyName", "s", NULL, offsetof(HostInfo, os_pretty_name) }, + {} + }; + + static const struct bus_properties_map manager_map[] = { + { "Virtualization", "s", NULL, offsetof(HostInfo, virtualization) }, + { "Architecture", "s", NULL, offsetof(HostInfo, architecture) }, + {} + }; + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *system_bus = NULL; + _cleanup_(free_host_infop) HostInfo *host = NULL; + int r; + + host = new0(HostInfo, 1); + if (!host) + return log_oom(); + + if (arg_scope != LOOKUP_SCOPE_SYSTEM) { + r = bus_connect_transport(arg_transport, arg_host, false, &system_bus); + if (r < 0) { + log_debug_errno(r, "Failed to connect to system bus, ignoring: %m"); + goto manager; + } + } + + r = bus_map_all_properties( + system_bus ?: bus, + "org.freedesktop.hostname1", + "/org/freedesktop/hostname1", + hostname_map, + BUS_MAP_STRDUP, + &error, + NULL, + host); + if (r < 0) { + log_debug_errno(r, "Failed to get host information from systemd-hostnamed, ignoring: %s", + bus_error_message(&error, r)); + sd_bus_error_free(&error); + } + +manager: + r = bus_map_all_properties( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + manager_map, + BUS_MAP_STRDUP, + &error, + NULL, + host); + if (r < 0) + return log_error_errno(r, "Failed to get host information from systemd: %s", + bus_error_message(&error, r)); + + *hi = TAKE_PTR(host); + return 0; +} + +static int compare_unit_start(const UnitTimes *a, const UnitTimes *b) { + return CMP(a->activating, b->activating); +} + +static void svg_graph_box(double height, double begin, double end) { + /* outside box, fill */ + svg("\n", + SCALE_X * (end - begin), + SCALE_Y * height); + + for (long long i = ((long long) (begin / 100000)) * 100000; i <= end; i += 100000) { + /* lines for each second */ + if (i % 5000000 == 0) + svg(" \n" + " %.01fs\n", + SCALE_X * i, + SCALE_X * i, + SCALE_Y * height, + SCALE_X * i, + -5.0, + 0.000001 * i); + else if (i % 1000000 == 0) + svg(" \n" + " %.01fs\n", + SCALE_X * i, + SCALE_X * i, + SCALE_Y * height, + SCALE_X * i, + -5.0, + 0.000001 * i); + else + svg(" \n", + SCALE_X * i, + SCALE_X * i, + SCALE_Y * height); + } +} + +static int plot_unit_times(UnitTimes *u, double width, int y) { + bool b; + + if (!u->name) + return 0; + + svg_bar("activating", u->activating, u->activated, y); + svg_bar("active", u->activated, u->deactivating, y); + svg_bar("deactivating", u->deactivating, u->deactivated, y); + + /* place the text on the left if we have passed the half of the svg width */ + b = u->activating * SCALE_X < width / 2; + if (u->time) + svg_text(b, u->activating, y, "%s (%s)", + u->name, FORMAT_TIMESPAN(u->time, USEC_PER_MSEC)); + else + svg_text(b, u->activating, y, "%s", u->name); + + return 1; +} + +int verb_plot(int argc, char *argv[], void *userdata) { + _cleanup_(free_host_infop) HostInfo *host = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL; + _cleanup_free_ char *pretty_times = NULL; + bool use_full_bus = arg_scope == LOOKUP_SCOPE_SYSTEM; + BootTimes *boot; + UnitTimes *u; + int n, m = 1, y = 0, r; + double width; + + r = acquire_bus(&bus, &use_full_bus); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + n = acquire_boot_times(bus, &boot); + if (n < 0) + return n; + + n = pretty_boot_time(bus, &pretty_times); + if (n < 0) + return n; + + if (use_full_bus || arg_scope != LOOKUP_SCOPE_SYSTEM) { + n = acquire_host_info(bus, &host); + if (n < 0) + return n; + } + + n = acquire_time_data(bus, ×); + if (n <= 0) + return n; + + typesafe_qsort(times, n, compare_unit_start); + + width = SCALE_X * (boot->firmware_time + boot->finish_time); + if (width < 800.0) + width = 800.0; + + if (boot->firmware_time > boot->loader_time) + m++; + if (boot->loader_time > 0) { + m++; + if (width < 1000.0) + width = 1000.0; + } + if (boot->initrd_time > 0) + m++; + if (boot->kernel_done_time > 0) + m++; + + for (u = times; u->has_data; u++) { + double text_start, text_width; + + if (u->activating > boot->finish_time) { + u->name = mfree(u->name); + continue; + } + + /* If the text cannot fit on the left side then + * increase the svg width so it fits on the right. + * TODO: calculate the text width more accurately */ + text_width = 8.0 * strlen(u->name); + text_start = (boot->firmware_time + u->activating) * SCALE_X; + if (text_width > text_start && text_width + text_start > width) + width = text_width + text_start; + + if (u->deactivated > u->activating && + u->deactivated <= boot->finish_time && + u->activated == 0 && u->deactivating == 0) + u->activated = u->deactivating = u->deactivated; + if (u->activated < u->activating || u->activated > boot->finish_time) + u->activated = boot->finish_time; + if (u->deactivating < u->activated || u->deactivating > boot->finish_time) + u->deactivating = boot->finish_time; + if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time) + u->deactivated = boot->finish_time; + m++; + } + + svg("\n" + "\n"); + + svg("\n\n", + 80.0 + width, 150.0 + (m * SCALE_Y) + + 5 * SCALE_Y /* legend */); + + /* write some basic info as a comment, including some help */ + svg("\n" + "\n" + "\n" + "\n" + "\n\n" + "\n\n", GIT_VERSION); + + /* style sheet */ + svg("\n \n\n\n"); + + svg("\n"); + svg("%s", pretty_times); + if (host) + svg("%s %s (%s %s %s) %s %s", + isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name, + strempty(host->hostname), + strempty(host->kernel_name), + strempty(host->kernel_release), + strempty(host->kernel_version), + strempty(host->architecture), + strempty(host->virtualization)); + + svg("\n", 20.0 + (SCALE_X * boot->firmware_time)); + svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time); + + if (boot->firmware_time > 0) { + svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y); + svg_text(true, -(double) boot->firmware_time, y, "firmware"); + y++; + } + if (boot->loader_time > 0) { + svg_bar("loader", -(double) boot->loader_time, 0, y); + svg_text(true, -(double) boot->loader_time, y, "loader"); + y++; + } + if (boot->kernel_done_time > 0) { + svg_bar("kernel", 0, boot->kernel_done_time, y); + svg_text(true, 0, y, "kernel"); + y++; + } + if (boot->initrd_time > 0) { + svg_bar("initrd", boot->initrd_time, boot->userspace_time, y); + if (boot->initrd_security_start_time < boot->initrd_security_finish_time) + svg_bar("security", boot->initrd_security_start_time, boot->initrd_security_finish_time, y); + if (boot->initrd_generators_start_time < boot->initrd_generators_finish_time) + svg_bar("generators", boot->initrd_generators_start_time, boot->initrd_generators_finish_time, y); + if (boot->initrd_unitsload_start_time < boot->initrd_unitsload_finish_time) + svg_bar("unitsload", boot->initrd_unitsload_start_time, boot->initrd_unitsload_finish_time, y); + svg_text(true, boot->initrd_time, y, "initrd"); + y++; + } + + for (u = times; u->has_data; u++) { + if (u->activating >= boot->userspace_time) + break; + + y += plot_unit_times(u, width, y); + } + + svg_bar("active", boot->userspace_time, boot->finish_time, y); + if (boot->security_start_time > 0) + svg_bar("security", boot->security_start_time, boot->security_finish_time, y); + svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y); + svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y); + svg_text(true, boot->userspace_time, y, "systemd"); + y++; + + for (; u->has_data; u++) + y += plot_unit_times(u, width, y); + + svg("\n"); + + /* Legend */ + svg("\n"); + y++; + svg_bar("activating", 0, 300000, y); + svg_text(true, 400000, y, "Activating"); + y++; + svg_bar("active", 0, 300000, y); + svg_text(true, 400000, y, "Active"); + y++; + svg_bar("deactivating", 0, 300000, y); + svg_text(true, 400000, y, "Deactivating"); + y++; + if (boot->security_start_time > 0) { + svg_bar("security", 0, 300000, y); + svg_text(true, 400000, y, "Setting up security module"); + y++; + } + svg_bar("generators", 0, 300000, y); + svg_text(true, 400000, y, "Generators"); + y++; + svg_bar("unitsload", 0, 300000, y); + svg_text(true, 400000, y, "Loading unit files"); + y++; + + svg("\n\n"); + + svg("\n"); + + return 0; +} diff --git a/src/analyze/analyze-plot.h b/src/analyze/analyze-plot.h new file mode 100644 index 000000000..eb2e398b3 --- /dev/null +++ b/src/analyze/analyze-plot.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_plot(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c index 2691dc2c8..aa41751dd 100644 --- a/src/analyze/analyze-security.c +++ b/src/analyze/analyze-security.c @@ -3,6 +3,7 @@ #include #include "af-list.h" +#include "analyze.h" #include "analyze-security.h" #include "analyze-verify.h" #include "bus-error.h" @@ -11,6 +12,8 @@ #include "bus-util.h" #include "copy.h" #include "env-util.h" +#include "fd-util.h" +#include "fileio.h" #include "format-table.h" #include "in-addr-prefix-util.h" #include "locale-util.h" @@ -1902,7 +1905,7 @@ static int assess(const SecurityInfo *info, name = info->id; printf("\n%s %sOverall exposure level for %s%s: %s%" PRIu64 ".%" PRIu64 " %s%s %s\n", - special_glyph(SPECIAL_GLYPH_ARROW), + special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), ansi_highlight(), name, ansi_normal(), @@ -2643,7 +2646,7 @@ static int offline_security_check(Unit *u, static int offline_security_checks(char **filenames, JsonVariant *policy, - UnitFileScope scope, + LookupScope scope, bool check_man, bool run_generators, unsigned threshold, @@ -2663,7 +2666,6 @@ static int offline_security_checks(char **filenames, _cleanup_free_ char *var = NULL; int r, k; size_t count = 0; - char **filename; if (strv_isempty(filenames)) return 0; @@ -2753,10 +2755,10 @@ static int offline_security_checks(char **filenames, return r; } -int analyze_security(sd_bus *bus, +static int analyze_security(sd_bus *bus, char **units, JsonVariant *policy, - UnitFileScope scope, + LookupScope scope, bool check_man, bool run_generators, bool offline, @@ -2786,7 +2788,6 @@ int analyze_security(sd_bus *bus, _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_strv_free_ char **list = NULL; size_t n = 0; - char **i; r = sd_bus_call_method( bus, @@ -2838,9 +2839,7 @@ int analyze_security(sd_bus *bus, ret = r; } - } else { - char **i; - + } else STRV_FOREACH(i, units) { _cleanup_free_ char *mangled = NULL, *instance = NULL; const char *name; @@ -2872,7 +2871,6 @@ int analyze_security(sd_bus *bus, if (r < 0 && ret >= 0) ret = r; } - } if (overview_table) { if (!FLAGS_SET(flags, ANALYZE_SECURITY_SHORT)) { @@ -2886,3 +2884,51 @@ int analyze_security(sd_bus *bus, } return ret; } + +int verb_security(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(json_variant_unrefp) JsonVariant *policy = NULL; + int r; + unsigned line, column; + + if (!arg_offline) { + r = acquire_bus(&bus, NULL); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + } + + pager_open(arg_pager_flags); + + if (arg_security_policy) { + r = json_parse_file(/*f=*/ NULL, arg_security_policy, /*flags=*/ 0, &policy, &line, &column); + if (r < 0) + return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column); + } else { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *pp = NULL; + + r = search_and_fopen_nulstr("systemd-analyze-security.policy", "re", /*root=*/ NULL, CONF_PATHS_NULSTR("systemd"), &f, &pp); + if (r < 0 && r != -ENOENT) + return r; + + if (f) { + r = json_parse_file(f, pp, /*flags=*/ 0, &policy, &line, &column); + if (r < 0) + return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column); + } + } + + return analyze_security(bus, + strv_skip(argv, 1), + policy, + arg_scope, + arg_man, + arg_generators, + arg_offline, + arg_threshold, + arg_root, + arg_profile, + arg_json_format_flags, + arg_pager_flags, + /*flags=*/ 0); +} diff --git a/src/analyze/analyze-security.h b/src/analyze/analyze-security.h index 07483248e..82f4c7fde 100644 --- a/src/analyze/analyze-security.h +++ b/src/analyze/analyze-security.h @@ -1,30 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include - -#include "sd-bus.h" - -#include "json.h" -#include "pager.h" -#include "unit-file.h" - typedef enum AnalyzeSecurityFlags { ANALYZE_SECURITY_SHORT = 1 << 0, ANALYZE_SECURITY_ONLY_LOADED = 1 << 1, ANALYZE_SECURITY_ONLY_LONG_RUNNING = 1 << 2, } AnalyzeSecurityFlags; -int analyze_security(sd_bus *bus, - char **units, - JsonVariant *policy, - UnitFileScope scope, - bool check_man, - bool run_generators, - bool offline, - unsigned threshold, - const char *root, - const char *profile, - JsonFormatFlags json_format_flags, - PagerFlags pager_flags, - AnalyzeSecurityFlags flags); +int verb_security(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-service-watchdogs.c b/src/analyze/analyze-service-watchdogs.c new file mode 100644 index 000000000..96aed32b5 --- /dev/null +++ b/src/analyze/analyze-service-watchdogs.c @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-service-watchdogs.h" +#include "bus-error.h" +#include "bus-locator.h" +#include "parse-util.h" + +int verb_service_watchdogs(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + int b, r; + + assert(IN_SET(argc, 1, 2)); + assert(argv); + + r = acquire_bus(&bus, NULL); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + if (argc == 1) { + /* get ServiceWatchdogs */ + r = bus_get_property_trivial(bus, bus_systemd_mgr, "ServiceWatchdogs", &error, 'b', &b); + if (r < 0) + return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r)); + + printf("%s\n", yes_no(!!b)); + + } else { + /* set ServiceWatchdogs */ + b = parse_boolean(argv[1]); + if (b < 0) + return log_error_errno(b, "Failed to parse service-watchdogs argument: %m"); + + r = bus_set_property(bus, bus_systemd_mgr, "ServiceWatchdogs", &error, "b", b); + if (r < 0) + return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r)); + } + + return 0; +} diff --git a/src/analyze/analyze-service-watchdogs.h b/src/analyze/analyze-service-watchdogs.h new file mode 100644 index 000000000..2f59f5a3f --- /dev/null +++ b/src/analyze/analyze-service-watchdogs.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_service_watchdogs(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-syscall-filter.c b/src/analyze/analyze-syscall-filter.c new file mode 100644 index 000000000..582a04308 --- /dev/null +++ b/src/analyze/analyze-syscall-filter.c @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze-syscall-filter.h" +#include "analyze.h" +#include "fd-util.h" +#include "fileio.h" +#include "nulstr-util.h" +#include "seccomp-util.h" +#include "set.h" +#include "strv.h" +#include "terminal-util.h" + +#if HAVE_SECCOMP + +static int load_kernel_syscalls(Set **ret) { + _cleanup_set_free_ Set *syscalls = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + + /* Let's read the available system calls from the list of available tracing events. Slightly dirty, + * but good enough for analysis purposes. */ + + f = fopen("/sys/kernel/tracing/available_events", "re"); + if (!f) { + /* We tried the non-debugfs mount point and that didn't work. If it wasn't mounted, maybe the + * old debugfs mount point works? */ + f = fopen("/sys/kernel/debug/tracing/available_events", "re"); + if (!f) + return log_full_errno(IN_SET(errno, EPERM, EACCES, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno, + "Can't read open tracefs' available_events file: %m"); + } + + for (;;) { + _cleanup_free_ char *line = NULL; + const char *e; + + r = read_line(f, LONG_LINE_MAX, &line); + if (r < 0) + return log_error_errno(r, "Failed to read system call list: %m"); + if (r == 0) + break; + + e = startswith(line, "syscalls:sys_enter_"); + if (!e) + continue; + + /* These are named differently inside the kernel than their external name for historical + * reasons. Let's hide them here. */ + if (STR_IN_SET(e, "newuname", "newfstat", "newstat", "newlstat", "sysctl")) + continue; + + r = set_put_strdup(&syscalls, e); + if (r < 0) + return log_error_errno(r, "Failed to add system call to list: %m"); + } + + *ret = TAKE_PTR(syscalls); + return 0; +} + +static void syscall_set_remove(Set *s, const SyscallFilterSet *set) { + const char *syscall; + + if (!set) + return; + + NULSTR_FOREACH(syscall, set->value) { + if (syscall[0] == '@') + continue; + + free(set_remove(s, syscall)); + } +} + +static void dump_syscall_filter(const SyscallFilterSet *set) { + const char *syscall; + + printf("%s%s%s\n" + " # %s\n", + ansi_highlight(), + set->name, + ansi_normal(), + set->help); + + NULSTR_FOREACH(syscall, set->value) + printf(" %s%s%s\n", syscall[0] == '@' ? ansi_underline() : "", syscall, ansi_normal()); +} + +int verb_syscall_filters(int argc, char *argv[], void *userdata) { + bool first = true; + + pager_open(arg_pager_flags); + + if (strv_isempty(strv_skip(argv, 1))) { + _cleanup_set_free_ Set *kernel = NULL, *known = NULL; + const char *sys; + int k = 0; /* explicit initialization to appease gcc */ + + NULSTR_FOREACH(sys, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value) + if (set_put_strdup(&known, sys) < 0) + return log_oom(); + + if (!arg_quiet) + k = load_kernel_syscalls(&kernel); + + for (int i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) { + const SyscallFilterSet *set = syscall_filter_sets + i; + if (!first) + puts(""); + + dump_syscall_filter(set); + syscall_set_remove(kernel, set); + if (i != SYSCALL_FILTER_SET_KNOWN) + syscall_set_remove(known, set); + first = false; + } + + if (arg_quiet) /* Let's not show the extra stuff in quiet mode */ + return 0; + + if (!set_isempty(known)) { + _cleanup_free_ char **l = NULL; + + printf("\n" + "# %sUngrouped System Calls%s (known but not included in any of the groups except @known):\n", + ansi_highlight(), ansi_normal()); + + l = set_get_strv(known); + if (!l) + return log_oom(); + + strv_sort(l); + + STRV_FOREACH(syscall, l) + printf("# %s\n", *syscall); + } + + if (k < 0) { + fputc('\n', stdout); + fflush(stdout); + if (!arg_quiet) + log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m"); + } else if (!set_isempty(kernel)) { + _cleanup_free_ char **l = NULL; + + printf("\n" + "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n", + ansi_highlight(), ansi_normal()); + + l = set_get_strv(kernel); + if (!l) + return log_oom(); + + strv_sort(l); + + STRV_FOREACH(syscall, l) + printf("# %s\n", *syscall); + } + } else + STRV_FOREACH(name, strv_skip(argv, 1)) { + const SyscallFilterSet *set; + + if (!first) + puts(""); + + set = syscall_filter_set_find(*name); + if (!set) { + /* make sure the error appears below normal output */ + fflush(stdout); + + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "Filter set \"%s\" not found.", *name); + } + + dump_syscall_filter(set); + first = false; + } + + return 0; +} + +#else +int verb_syscall_filters(int argc, char *argv[], void *userdata) { + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with syscall filters, sorry."); +} +#endif diff --git a/src/analyze/analyze-syscall-filter.h b/src/analyze/analyze-syscall-filter.h new file mode 100644 index 000000000..3a1af85a6 --- /dev/null +++ b/src/analyze/analyze-syscall-filter.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_syscall_filters(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-time-data.c b/src/analyze/analyze-time-data.c new file mode 100644 index 000000000..aa42d6932 --- /dev/null +++ b/src/analyze/analyze-time-data.c @@ -0,0 +1,297 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-time-data.h" +#include "bus-error.h" +#include "bus-locator.h" +#include "bus-map-properties.h" +#include "bus-unit-util.h" +#include "special.h" + +static void subtract_timestamp(usec_t *a, usec_t b) { + assert(a); + + if (*a > 0) { + assert(*a >= b); + *a -= b; + } +} + +int acquire_boot_times(sd_bus *bus, BootTimes **ret) { + static const struct bus_properties_map property_map[] = { + { "FirmwareTimestampMonotonic", "t", NULL, offsetof(BootTimes, firmware_time) }, + { "LoaderTimestampMonotonic", "t", NULL, offsetof(BootTimes, loader_time) }, + { "KernelTimestamp", "t", NULL, offsetof(BootTimes, kernel_time) }, + { "InitRDTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_time) }, + { "UserspaceTimestampMonotonic", "t", NULL, offsetof(BootTimes, userspace_time) }, + { "FinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, finish_time) }, + { "SecurityStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_start_time) }, + { "SecurityFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_finish_time) }, + { "GeneratorsStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_start_time) }, + { "GeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_finish_time) }, + { "UnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, unitsload_start_time) }, + { "UnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, unitsload_finish_time) }, + { "InitRDSecurityStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_security_start_time) }, + { "InitRDSecurityFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_security_finish_time) }, + { "InitRDGeneratorsStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_generators_start_time) }, + { "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_generators_finish_time) }, + { "InitRDUnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_start_time) }, + { "InitRDUnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_finish_time) }, + {}, + }; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + static BootTimes times; + static bool cached = false; + int r; + + if (cached) + goto finish; + + assert_cc(sizeof(usec_t) == sizeof(uint64_t)); + + r = bus_map_all_properties( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + property_map, + BUS_MAP_STRDUP, + &error, + NULL, + ×); + if (r < 0) + return log_error_errno(r, "Failed to get timestamp properties: %s", bus_error_message(&error, r)); + + if (times.finish_time <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINPROGRESS), + "Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n" + "Please try again later.\n" + "Hint: Use 'systemctl%s list-jobs' to see active jobs", + times.finish_time, + arg_scope == LOOKUP_SCOPE_SYSTEM ? "" : " --user"); + + if (arg_scope == LOOKUP_SCOPE_SYSTEM && times.security_start_time > 0) { + /* security_start_time is set when systemd is not running under container environment. */ + if (times.initrd_time > 0) + times.kernel_done_time = times.initrd_time; + else + times.kernel_done_time = times.userspace_time; + } else { + /* + * User-instance-specific or container-system-specific timestamps processing + * (see comment to reverse_offset in BootTimes). + */ + times.reverse_offset = times.userspace_time; + + times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = + times.userspace_time = times.security_start_time = times.security_finish_time = 0; + + subtract_timestamp(×.finish_time, times.reverse_offset); + + subtract_timestamp(×.generators_start_time, times.reverse_offset); + subtract_timestamp(×.generators_finish_time, times.reverse_offset); + + subtract_timestamp(×.unitsload_start_time, times.reverse_offset); + subtract_timestamp(×.unitsload_finish_time, times.reverse_offset); + } + + cached = true; + +finish: + *ret = × + return 0; +} + +static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + int r; + + assert(bus); + assert(path); + assert(interface); + assert(property); + assert(val); + + r = sd_bus_get_property_trivial( + bus, + "org.freedesktop.systemd1", + path, + interface, + property, + &error, + 't', val); + if (r < 0) + return log_error_errno(r, "Failed to parse reply: %s", bus_error_message(&error, r)); + + return 0; +} + +int pretty_boot_time(sd_bus *bus, char **ret) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *path = NULL, *unit_id = NULL, *text = NULL; + usec_t activated_time = USEC_INFINITY; + BootTimes *t; + int r; + + r = acquire_boot_times(bus, &t); + if (r < 0) + return r; + + path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET); + if (!path) + return log_oom(); + + r = sd_bus_get_property_string( + bus, + "org.freedesktop.systemd1", + path, + "org.freedesktop.systemd1.Unit", + "Id", + &error, + &unit_id); + if (r < 0) + log_warning_errno(r, "default.target doesn't seem to exist, ignoring: %s", bus_error_message(&error, r)); + + r = bus_get_uint64_property(bus, path, + "org.freedesktop.systemd1.Unit", + "ActiveEnterTimestampMonotonic", + &activated_time); + if (r < 0) + log_warning_errno(r, "Could not get time to reach default.target, ignoring: %m"); + + text = strdup("Startup finished in "); + if (!text) + return log_oom(); + + if (t->firmware_time > 0 && !strextend(&text, FORMAT_TIMESPAN(t->firmware_time - t->loader_time, USEC_PER_MSEC), " (firmware) + ")) + return log_oom(); + if (t->loader_time > 0 && !strextend(&text, FORMAT_TIMESPAN(t->loader_time, USEC_PER_MSEC), " (loader) + ")) + return log_oom(); + if (t->kernel_done_time > 0 && !strextend(&text, FORMAT_TIMESPAN(t->kernel_done_time, USEC_PER_MSEC), " (kernel) + ")) + return log_oom(); + if (t->initrd_time > 0 && !strextend(&text, FORMAT_TIMESPAN(t->userspace_time - t->initrd_time, USEC_PER_MSEC), " (initrd) + ")) + return log_oom(); + + if (!strextend(&text, FORMAT_TIMESPAN(t->finish_time - t->userspace_time, USEC_PER_MSEC), " (userspace) ")) + return log_oom(); + + if (t->kernel_done_time > 0) + if (!strextend(&text, "= ", FORMAT_TIMESPAN(t->firmware_time + t->finish_time, USEC_PER_MSEC), " ")) + return log_oom(); + + if (unit_id && timestamp_is_set(activated_time)) { + usec_t base = t->userspace_time > 0 ? t->userspace_time : t->reverse_offset; + + if (!strextend(&text, "\n", unit_id, " reached after ", FORMAT_TIMESPAN(activated_time - base, USEC_PER_MSEC), " in userspace.")) + return log_oom(); + + } else if (unit_id && activated_time == 0) { + + if (!strextend(&text, "\n", unit_id, " was never reached.")) + return log_oom(); + + } else if (unit_id && activated_time == USEC_INFINITY) { + + if (!strextend(&text, "\nCould not get time to reach ", unit_id, ".")) + return log_oom(); + + } else if (!unit_id) { + + if (!strextend(&text, "\ncould not find default.target.")) + return log_oom(); + } + + *ret = TAKE_PTR(text); + return 0; +} + +UnitTimes* unit_times_free_array(UnitTimes *t) { + if (!t) + return NULL; + + for (UnitTimes *p = t; p->has_data; p++) + free(p->name); + + return mfree(t); +} + +int acquire_time_data(sd_bus *bus, UnitTimes **out) { + static const struct bus_properties_map property_map[] = { + { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activating) }, + { "ActiveEnterTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activated) }, + { "ActiveExitTimestampMonotonic", "t", NULL, offsetof(UnitTimes, deactivating) }, + { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(UnitTimes, deactivated) }, + {}, + }; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(unit_times_free_arrayp) UnitTimes *unit_times = NULL; + BootTimes *boot_times = NULL; + size_t c = 0; + UnitInfo u; + int r; + + r = acquire_boot_times(bus, &boot_times); + if (r < 0) + return r; + + r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL); + if (r < 0) + return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); + + r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = bus_parse_unit_info(reply, &u)) > 0) { + UnitTimes *t; + + if (!GREEDY_REALLOC(unit_times, c + 2)) + return log_oom(); + + unit_times[c + 1].has_data = false; + t = &unit_times[c]; + t->name = NULL; + + assert_cc(sizeof(usec_t) == sizeof(uint64_t)); + + r = bus_map_all_properties( + bus, + "org.freedesktop.systemd1", + u.unit_path, + property_map, + BUS_MAP_STRDUP, + &error, + NULL, + t); + if (r < 0) + return log_error_errno(r, "Failed to get timestamp properties of unit %s: %s", + u.id, bus_error_message(&error, r)); + + subtract_timestamp(&t->activating, boot_times->reverse_offset); + subtract_timestamp(&t->activated, boot_times->reverse_offset); + subtract_timestamp(&t->deactivating, boot_times->reverse_offset); + subtract_timestamp(&t->deactivated, boot_times->reverse_offset); + + if (t->activated >= t->activating) + t->time = t->activated - t->activating; + else if (t->deactivated >= t->activating) + t->time = t->deactivated - t->activating; + else + t->time = 0; + + if (t->activating == 0) + continue; + + t->name = strdup(u.id); + if (!t->name) + return log_oom(); + + t->has_data = true; + c++; + } + if (r < 0) + return bus_log_parse_error(r); + + *out = TAKE_PTR(unit_times); + return c; +} diff --git a/src/analyze/analyze-time-data.h b/src/analyze/analyze-time-data.h new file mode 100644 index 000000000..0082de213 --- /dev/null +++ b/src/analyze/analyze-time-data.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "time-util.h" + +typedef struct BootTimes { + usec_t firmware_time; + usec_t loader_time; + usec_t kernel_time; + usec_t kernel_done_time; + usec_t initrd_time; + usec_t userspace_time; + usec_t finish_time; + usec_t security_start_time; + usec_t security_finish_time; + usec_t generators_start_time; + usec_t generators_finish_time; + usec_t unitsload_start_time; + usec_t unitsload_finish_time; + usec_t initrd_security_start_time; + usec_t initrd_security_finish_time; + usec_t initrd_generators_start_time; + usec_t initrd_generators_finish_time; + usec_t initrd_unitsload_start_time; + usec_t initrd_unitsload_finish_time; + + /* + * If we're analyzing the user instance, all timestamps will be offset by its own start-up timestamp, + * which may be arbitrarily big. With "plot", this causes arbitrarily wide output SVG files which + * almost completely consist of empty space. Thus we cancel out this offset. + * + * This offset is subtracted from times above by acquire_boot_times(), but it still needs to be + * subtracted from unit-specific timestamps (so it is stored here for reference). + */ + usec_t reverse_offset; +} BootTimes; + +typedef struct UnitTimes { + bool has_data; + char *name; + usec_t activating; + usec_t activated; + usec_t deactivated; + usec_t deactivating; + usec_t time; +} UnitTimes; + +int acquire_boot_times(sd_bus *bus, BootTimes **ret); +int pretty_boot_time(sd_bus *bus, char **ret); + +UnitTimes* unit_times_free_array(UnitTimes *t); +DEFINE_TRIVIAL_CLEANUP_FUNC(UnitTimes*, unit_times_free_array); + +int acquire_time_data(sd_bus *bus, UnitTimes **out); diff --git a/src/analyze/analyze-time.c b/src/analyze/analyze-time.c new file mode 100644 index 000000000..e5744a218 --- /dev/null +++ b/src/analyze/analyze-time.c @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-time.h" +#include "analyze-time-data.h" + +int verb_time(int argc, char *argv[], void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_free_ char *buf = NULL; + int r; + + r = acquire_bus(&bus, NULL); + if (r < 0) + return bus_log_connect_error(r, arg_transport); + + r = pretty_boot_time(bus, &buf); + if (r < 0) + return r; + + puts(buf); + return 0; +} diff --git a/src/analyze/analyze-time.h b/src/analyze/analyze-time.h new file mode 100644 index 000000000..a8f8575c3 --- /dev/null +++ b/src/analyze/analyze-time.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_time(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-timespan.c b/src/analyze/analyze-timespan.c new file mode 100644 index 000000000..f244ace04 --- /dev/null +++ b/src/analyze/analyze-timespan.c @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-timespan.h" +#include "calendarspec.h" +#include "format-table.h" +#include "glyph-util.h" +#include "strv.h" +#include "terminal-util.h" + +int verb_timespan(int argc, char *argv[], void *userdata) { + STRV_FOREACH(input_timespan, strv_skip(argv, 1)) { + _cleanup_(table_unrefp) Table *table = NULL; + usec_t output_usecs; + TableCell *cell; + int r; + + r = parse_time(*input_timespan, &output_usecs, USEC_PER_SEC); + if (r < 0) { + log_error_errno(r, "Failed to parse time span '%s': %m", *input_timespan); + time_parsing_hint(*input_timespan, /* calendar= */ true, /* timestamp= */ true, /* timespan= */ false); + return r; + } + + table = table_new("name", "value"); + if (!table) + return log_oom(); + + table_set_header(table, false); + + assert_se(cell = table_get_cell(table, 0, 0)); + r = table_set_ellipsize_percent(table, cell, 100); + if (r < 0) + return r; + + r = table_set_align_percent(table, cell, 100); + if (r < 0) + return r; + + assert_se(cell = table_get_cell(table, 0, 1)); + r = table_set_ellipsize_percent(table, cell, 100); + if (r < 0) + return r; + + r = table_add_many(table, + TABLE_STRING, "Original:", + TABLE_STRING, *input_timespan); + if (r < 0) + return table_log_add_error(r); + + r = table_add_cell_stringf(table, NULL, "%ss:", special_glyph(SPECIAL_GLYPH_MU)); + if (r < 0) + return table_log_add_error(r); + + r = table_add_many(table, + TABLE_UINT64, output_usecs, + TABLE_STRING, "Human:", + TABLE_TIMESPAN, output_usecs, + TABLE_SET_COLOR, ansi_highlight()); + if (r < 0) + return table_log_add_error(r); + + r = table_print(table, NULL); + if (r < 0) + return r; + + if (input_timespan[1]) + putchar('\n'); + } + + return 0; +} diff --git a/src/analyze/analyze-timespan.h b/src/analyze/analyze-timespan.h new file mode 100644 index 000000000..46d229560 --- /dev/null +++ b/src/analyze/analyze-timespan.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_timespan(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-timestamp.c b/src/analyze/analyze-timestamp.c new file mode 100644 index 000000000..ddf34ab75 --- /dev/null +++ b/src/analyze/analyze-timestamp.c @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-timestamp.h" +#include "format-table.h" +#include "terminal-util.h" + +static int test_timestamp_one(const char *p) { + _cleanup_(table_unrefp) Table *table = NULL; + TableCell *cell; + usec_t usec; + int r; + + r = parse_timestamp(p, &usec); + if (r < 0) { + log_error_errno(r, "Failed to parse \"%s\": %m", p); + time_parsing_hint(p, /* calendar= */ true, /* timestamp= */ false, /* timespan= */ true); + return r; + } + + table = table_new("name", "value"); + if (!table) + return log_oom(); + + table_set_header(table, false); + + assert_se(cell = table_get_cell(table, 0, 0)); + r = table_set_ellipsize_percent(table, cell, 100); + if (r < 0) + return r; + + r = table_set_align_percent(table, cell, 100); + if (r < 0) + return r; + + assert_se(cell = table_get_cell(table, 0, 1)); + r = table_set_ellipsize_percent(table, cell, 100); + if (r < 0) + return r; + + r = table_add_many(table, + TABLE_STRING, "Original form:", + TABLE_STRING, p, + TABLE_STRING, "Normalized form:", + TABLE_TIMESTAMP, usec, + TABLE_SET_COLOR, ansi_highlight_blue()); + if (r < 0) + return table_log_add_error(r); + + if (!in_utc_timezone()) { + r = table_add_many(table, + TABLE_STRING, "(in UTC):", + TABLE_TIMESTAMP_UTC, usec); + if (r < 0) + return table_log_add_error(r); + } + + r = table_add_cell(table, NULL, TABLE_STRING, "UNIX seconds:"); + if (r < 0) + return table_log_add_error(r); + + if (usec % USEC_PER_SEC == 0) + r = table_add_cell_stringf(table, NULL, "@%"PRI_USEC, + usec / USEC_PER_SEC); + else + r = table_add_cell_stringf(table, NULL, "@%"PRI_USEC".%06"PRI_USEC"", + usec / USEC_PER_SEC, + usec % USEC_PER_SEC); + if (r < 0) + return r; + + r = table_add_many(table, + TABLE_STRING, "From now:", + TABLE_TIMESTAMP_RELATIVE, usec); + if (r < 0) + return table_log_add_error(r); + + return table_print(table, NULL); +} + +int verb_timestamp(int argc, char *argv[], void *userdata) { + int ret = 0, r; + + STRV_FOREACH(p, strv_skip(argv, 1)) { + r = test_timestamp_one(*p); + if (ret == 0 && r < 0) + ret = r; + + if (*(p + 1)) + putchar('\n'); + } + + return ret; +} diff --git a/src/analyze/analyze-timestamp.h b/src/analyze/analyze-timestamp.h new file mode 100644 index 000000000..43e4b57d2 --- /dev/null +++ b/src/analyze/analyze-timestamp.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_timestamp(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-unit-files.c b/src/analyze/analyze-unit-files.c new file mode 100644 index 000000000..f07ec3e56 --- /dev/null +++ b/src/analyze/analyze-unit-files.c @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-unit-files.h" +#include "path-lookup.h" +#include "strv.h" + +static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int flags) { + STRV_FOREACH(s, strv) + if (strv_fnmatch_or_empty(patterns, *s, flags)) + return true; + + return false; +} + +int verb_unit_files(int argc, char *argv[], void *userdata) { + _cleanup_hashmap_free_ Hashmap *unit_ids = NULL, *unit_names = NULL; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; + char **patterns = strv_skip(argv, 1); + const char *k, *dst; + char **v; + int r; + + r = lookup_paths_init_or_warn(&lp, arg_scope, 0, NULL); + if (r < 0) + return r; + + r = unit_file_build_name_map(&lp, NULL, &unit_ids, &unit_names, NULL); + if (r < 0) + return log_error_errno(r, "unit_file_build_name_map() failed: %m"); + + HASHMAP_FOREACH_KEY(dst, k, unit_ids) { + if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) && + !strv_fnmatch_or_empty(patterns, dst, FNM_NOESCAPE)) + continue; + + printf("ids: %s → %s\n", k, dst); + } + + HASHMAP_FOREACH_KEY(v, k, unit_names) { + if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) && + !strv_fnmatch_strv_or_empty(patterns, v, FNM_NOESCAPE)) + continue; + + _cleanup_free_ char *j = strv_join(v, ", "); + printf("aliases: %s ← %s\n", k, j); + } + + return 0; +} diff --git a/src/analyze/analyze-unit-files.h b/src/analyze/analyze-unit-files.h new file mode 100644 index 000000000..c193fd827 --- /dev/null +++ b/src/analyze/analyze-unit-files.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_unit_files(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-unit-paths.c b/src/analyze/analyze-unit-paths.c new file mode 100644 index 000000000..6fa1527dd --- /dev/null +++ b/src/analyze/analyze-unit-paths.c @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "analyze.h" +#include "analyze-unit-paths.h" +#include "path-lookup.h" +#include "strv.h" + +int verb_unit_paths(int argc, char *argv[], void *userdata) { + _cleanup_(lookup_paths_free) LookupPaths paths = {}; + int r; + + r = lookup_paths_init_or_warn(&paths, arg_scope, 0, NULL); + if (r < 0) + return r; + + STRV_FOREACH(p, paths.search_path) + puts(*p); + + return 0; +} diff --git a/src/analyze/analyze-unit-paths.h b/src/analyze/analyze-unit-paths.h new file mode 100644 index 000000000..b8d46e87d --- /dev/null +++ b/src/analyze/analyze-unit-paths.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +int verb_unit_paths(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze-verify-util.c b/src/analyze/analyze-verify-util.c new file mode 100644 index 000000000..531144ba5 --- /dev/null +++ b/src/analyze/analyze-verify-util.c @@ -0,0 +1,347 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "all-units.h" +#include "alloc-util.h" +#include "analyze-verify-util.h" +#include "bus-error.h" +#include "bus-util.h" +#include "log.h" +#include "manager.h" +#include "pager.h" +#include "path-util.h" +#include "string-table.h" +#include "strv.h" +#include "unit-name.h" +#include "unit-serialize.h" + +static void log_syntax_callback(const char *unit, int level, void *userdata) { + Set **s = userdata; + int r; + + assert(userdata); + assert(unit); + + if (level > LOG_WARNING) + return; + + if (*s == POINTER_MAX) + return; + + r = set_put_strdup(s, unit); + if (r < 0) { + set_free_free(*s); + *s = POINTER_MAX; + } +} + +int verify_prepare_filename(const char *filename, char **ret) { + int r; + const char *name; + _cleanup_free_ char *abspath = NULL; + _cleanup_free_ char *dir = NULL; + _cleanup_free_ char *with_instance = NULL; + char *c; + + assert(filename); + assert(ret); + + r = path_make_absolute_cwd(filename, &abspath); + if (r < 0) + return r; + + name = basename(abspath); + if (!unit_name_is_valid(name, UNIT_NAME_ANY)) + return -EINVAL; + + if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { + r = unit_name_replace_instance(name, "i", &with_instance); + if (r < 0) + return r; + } + + dir = dirname_malloc(abspath); + if (!dir) + return -ENOMEM; + + c = path_join(dir, with_instance ?: name); + if (!c) + return -ENOMEM; + + *ret = c; + return 0; +} + +int verify_generate_path(char **var, char **filenames) { + _cleanup_strv_free_ char **ans = NULL; + const char *old; + int r; + + STRV_FOREACH(filename, filenames) { + char *t; + + t = dirname_malloc(*filename); + if (!t) + return -ENOMEM; + + r = strv_consume(&ans, t); + if (r < 0) + return r; + } + + assert_se(strv_uniq(ans)); + + /* First, prepend our directories. Second, if some path was specified, use that, and + * otherwise use the defaults. Any duplicates will be filtered out in path-lookup.c. + * Treat explicit empty path to mean that nothing should be appended. + */ + old = getenv("SYSTEMD_UNIT_PATH"); + if (!streq_ptr(old, "")) { + if (!old) + old = ":"; + + r = strv_extend(&ans, old); + if (r < 0) + return r; + } + + *var = strv_join(ans, ":"); + if (!*var) + return -ENOMEM; + + return 0; +} + +static int verify_socket(Unit *u) { + Unit *service; + int r; + + assert(u); + + if (u->type != UNIT_SOCKET) + return 0; + + r = socket_load_service_unit(SOCKET(u), -1, &service); + if (r < 0) + return log_unit_error_errno(u, r, "service unit for the socket cannot be loaded: %m"); + + if (service->load_state != UNIT_LOADED) + return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), + "service %s not loaded, socket cannot be started.", service->id); + + log_unit_debug(u, "using service unit %s.", service->id); + return 0; +} + +int verify_executable(Unit *u, const ExecCommand *exec, const char *root) { + int r; + + if (!exec) + return 0; + + if (exec->flags & EXEC_COMMAND_IGNORE_FAILURE) + return 0; + + r = find_executable_full(exec->path, root, NULL, false, NULL, NULL); + if (r < 0) + return log_unit_error_errno(u, r, "Command %s is not executable: %m", exec->path); + + return 0; +} + +static int verify_executables(Unit *u, const char *root) { + ExecCommand *exec; + int r = 0, k; + unsigned i; + + assert(u); + + exec = u->type == UNIT_SOCKET ? SOCKET(u)->control_command : + u->type == UNIT_MOUNT ? MOUNT(u)->control_command : + u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL; + k = verify_executable(u, exec, root); + if (k < 0 && r == 0) + r = k; + + if (u->type == UNIT_SERVICE) + for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) { + k = verify_executable(u, SERVICE(u)->exec_command[i], root); + if (k < 0 && r == 0) + r = k; + } + + if (u->type == UNIT_SOCKET) + for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) { + k = verify_executable(u, SOCKET(u)->exec_command[i], root); + if (k < 0 && r == 0) + r = k; + } + + return r; +} + +static int verify_documentation(Unit *u, bool check_man) { + int r = 0, k; + + STRV_FOREACH(p, u->documentation) { + log_unit_debug(u, "Found documentation item: %s", *p); + + if (check_man && startswith(*p, "man:")) { + k = show_man_page(*p + 4, true); + if (k != 0) { + if (k < 0) + log_unit_error_errno(u, k, "Can't show %s: %m", *p + 4); + else { + log_unit_error(u, "Command 'man %s' failed with code %d", *p + 4, k); + k = -ENOEXEC; + } + if (r == 0) + r = k; + } + } + } + + /* Check remote URLs? */ + + return r; +} + +static int verify_unit(Unit *u, bool check_man, const char *root) { + _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; + int r, k; + + assert(u); + + if (DEBUG_LOGGING) + unit_dump(u, stdout, "\t"); + + log_unit_debug(u, "Creating %s/start job", u->id); + r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, NULL, &err, NULL); + if (r < 0) + log_unit_error_errno(u, r, "Failed to create %s/start: %s", u->id, bus_error_message(&err, r)); + + k = verify_socket(u); + if (k < 0 && r == 0) + r = k; + + k = verify_executables(u, root); + if (k < 0 && r == 0) + r = k; + + k = verify_documentation(u, check_man); + if (k < 0 && r == 0) + r = k; + + return r; +} + +static void set_destroy_ignore_pointer_max(Set** s) { + if (*s == POINTER_MAX) + return; + set_free_free(*s); +} + +int verify_units(char **filenames, LookupScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root) { + const ManagerTestRunFlags flags = + MANAGER_TEST_RUN_MINIMAL | + MANAGER_TEST_RUN_ENV_GENERATORS | + (recursive_errors == RECURSIVE_ERRORS_NO) * MANAGER_TEST_RUN_IGNORE_DEPENDENCIES | + run_generators * MANAGER_TEST_RUN_GENERATORS; + + _cleanup_(manager_freep) Manager *m = NULL; + _cleanup_(set_destroy_ignore_pointer_max) Set *s = NULL; + _unused_ _cleanup_(clear_log_syntax_callback) dummy_t dummy; + Unit *units[strv_length(filenames)]; + _cleanup_free_ char *var = NULL; + int r, k, i, count = 0; + + if (strv_isempty(filenames)) + return 0; + + /* Allow systemd-analyze to hook in a callback function so that it can get + * all the required log data from the function itself without having to rely + * on a global set variable for the same */ + set_log_syntax_callback(log_syntax_callback, &s); + + /* set the path */ + r = verify_generate_path(&var, filenames); + if (r < 0) + return log_error_errno(r, "Failed to generate unit load path: %m"); + + assert_se(set_unit_path(var) >= 0); + + r = manager_new(scope, flags, &m); + if (r < 0) + return log_error_errno(r, "Failed to initialize manager: %m"); + + log_debug("Starting manager..."); + + r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root); + if (r < 0) + return r; + + manager_clear_jobs(m); + + log_debug("Loading remaining units from the command line..."); + + STRV_FOREACH(filename, filenames) { + _cleanup_free_ char *prepared = NULL; + + log_debug("Handling %s...", *filename); + + k = verify_prepare_filename(*filename, &prepared); + if (k < 0) { + log_error_errno(k, "Failed to prepare filename %s: %m", *filename); + if (r == 0) + r = k; + continue; + } + + k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]); + if (k < 0) { + if (r == 0) + r = k; + continue; + } + + count++; + } + + for (i = 0; i < count; i++) { + k = verify_unit(units[i], check_man, root); + if (k < 0 && r == 0) + r = k; + } + + if (s == POINTER_MAX) + return log_oom(); + + if (set_isempty(s) || r != 0) + return r; + + /* If all previous verifications succeeded, then either the recursive parsing of all the + * associated dependencies with RECURSIVE_ERRORS_YES or the parsing of the specified unit file + * with RECURSIVE_ERRORS_NO must have yielded a syntax warning and hence, a non-empty set. */ + if (IN_SET(recursive_errors, RECURSIVE_ERRORS_YES, RECURSIVE_ERRORS_NO)) + return -ENOTRECOVERABLE; + + /* If all previous verifications succeeded, then the non-empty set could have resulted from + * a syntax warning encountered during the recursive parsing of the specified unit file and + * its direct dependencies. Hence, search for any of the filenames in the set and if found, + * return a non-zero process exit status. */ + if (recursive_errors == RECURSIVE_ERRORS_ONE) + STRV_FOREACH(filename, filenames) + if (set_contains(s, basename(*filename))) + return -ENOTRECOVERABLE; + + return 0; +} + +static const char* const recursive_errors_table[_RECURSIVE_ERRORS_MAX] = { + [RECURSIVE_ERRORS_NO] = "no", + [RECURSIVE_ERRORS_YES] = "yes", + [RECURSIVE_ERRORS_ONE] = "one", +}; + +DEFINE_STRING_TABLE_LOOKUP(recursive_errors, RecursiveErrors); diff --git a/src/analyze/analyze-verify-util.h b/src/analyze/analyze-verify-util.h new file mode 100644 index 000000000..385d635e3 --- /dev/null +++ b/src/analyze/analyze-verify-util.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "execute.h" +#include "path-lookup.h" + +typedef enum RecursiveErrors { + RECURSIVE_ERRORS_YES, /* Look for errors in all associated units */ + RECURSIVE_ERRORS_NO, /* Don't look for errors in any but the selected unit */ + RECURSIVE_ERRORS_ONE, /* Look for errors in the selected unit and its direct dependencies */ + _RECURSIVE_ERRORS_MAX, + _RECURSIVE_ERRORS_INVALID = -EINVAL, +} RecursiveErrors; + +int verify_generate_path(char **var, char **filenames); +int verify_prepare_filename(const char *filename, char **ret); +int verify_executable(Unit *u, const ExecCommand *exec, const char *root); +int verify_units(char **filenames, LookupScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root); + +const char* recursive_errors_to_string(RecursiveErrors i) _const_; +RecursiveErrors recursive_errors_from_string(const char *s) _pure_; diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index 943a1f27d..35e4e1eb3 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -1,351 +1,70 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include - -#include "alloc-util.h" -#include "all-units.h" +#include "analyze.h" #include "analyze-verify.h" -#include "bus-error.h" -#include "bus-util.h" -#include "log.h" -#include "manager.h" -#include "pager.h" -#include "path-util.h" -#include "string-table.h" -#include "strv.h" -#include "unit-name.h" -#include "unit-serialize.h" +#include "analyze-verify-util.h" +#include "copy.h" +#include "rm-rf.h" +#include "tmpfile-util.h" -static void log_syntax_callback(const char *unit, int level, void *userdata) { - Set **s = userdata; +static int process_aliases(char *argv[], char *tempdir, char ***ret) { + _cleanup_strv_free_ char **filenames = NULL; int r; - assert(userdata); - assert(unit); - - if (level > LOG_WARNING) - return; - - if (*s == POINTER_MAX) - return; - - r = set_put_strdup(s, unit); - if (r < 0) { - set_free_free(*s); - *s = POINTER_MAX; - } -} - -int verify_prepare_filename(const char *filename, char **ret) { - int r; - const char *name; - _cleanup_free_ char *abspath = NULL; - _cleanup_free_ char *dir = NULL; - _cleanup_free_ char *with_instance = NULL; - char *c; - - assert(filename); + assert(argv); + assert(tempdir); assert(ret); - r = path_make_absolute_cwd(filename, &abspath); - if (r < 0) - return r; + STRV_FOREACH(filename, strv_skip(argv, 1)) { + _cleanup_free_ char *src = NULL, *dst = NULL, *base = NULL; + const char *parse_arg; - name = basename(abspath); - if (!unit_name_is_valid(name, UNIT_NAME_ANY)) - return -EINVAL; - - if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) { - r = unit_name_replace_instance(name, "i", &with_instance); + parse_arg = *filename; + r = extract_first_word(&parse_arg, &src, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE); if (r < 0) return r; - } - dir = dirname_malloc(abspath); - if (!dir) - return -ENOMEM; + if (!parse_arg) { + r = strv_consume(&filenames, TAKE_PTR(src)); + if (r < 0) + return r; - c = path_join(dir, with_instance ?: name); - if (!c) - return -ENOMEM; + continue; + } - *ret = c; - return 0; -} + r = path_extract_filename(parse_arg, &base); + if (r < 0) + return r; -int verify_generate_path(char **var, char **filenames) { - const char *old; - char **filename; - - _cleanup_strv_free_ char **ans = NULL; - int r; - - STRV_FOREACH(filename, filenames) { - char *t; - - t = dirname_malloc(*filename); - if (!t) + dst = path_join(tempdir, base); + if (!dst) return -ENOMEM; - r = strv_consume(&ans, t); + r = copy_file(src, dst, 0, 0644, 0, 0, COPY_REFLINK); + if (r < 0) + return r; + + r = strv_consume(&filenames, TAKE_PTR(dst)); if (r < 0) return r; } - assert_se(strv_uniq(ans)); - - /* First, prepend our directories. Second, if some path was specified, use that, and - * otherwise use the defaults. Any duplicates will be filtered out in path-lookup.c. - * Treat explicit empty path to mean that nothing should be appended. - */ - old = getenv("SYSTEMD_UNIT_PATH"); - if (!streq_ptr(old, "")) { - if (!old) - old = ":"; - - r = strv_extend(&ans, old); - if (r < 0) - return r; - } - - *var = strv_join(ans, ":"); - if (!*var) - return -ENOMEM; - + *ret = TAKE_PTR(filenames); return 0; } -static int verify_socket(Unit *u) { - Unit *service; +int verb_verify(int argc, char *argv[], void *userdata) { + _cleanup_strv_free_ char **filenames = NULL; + _cleanup_(rm_rf_physical_and_freep) char *tempdir = NULL; int r; - assert(u); - - if (u->type != UNIT_SOCKET) - return 0; - - r = socket_load_service_unit(SOCKET(u), -1, &service); + r = mkdtemp_malloc("/tmp/systemd-analyze-XXXXXX", &tempdir); if (r < 0) - return log_unit_error_errno(u, r, "service unit for the socket cannot be loaded: %m"); + return log_error_errno(r, "Failed to setup working directory: %m"); - if (service->load_state != UNIT_LOADED) - return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), - "service %s not loaded, socket cannot be started.", service->id); - - log_unit_debug(u, "using service unit %s.", service->id); - return 0; -} - -int verify_executable(Unit *u, const ExecCommand *exec, const char *root) { - int r; - - if (!exec) - return 0; - - if (exec->flags & EXEC_COMMAND_IGNORE_FAILURE) - return 0; - - r = find_executable_full(exec->path, root, NULL, false, NULL, NULL); + r = process_aliases(argv, tempdir, &filenames); if (r < 0) - return log_unit_error_errno(u, r, "Command %s is not executable: %m", exec->path); + return log_error_errno(r, "Couldn't process aliases: %m"); - return 0; + return verify_units(filenames, arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root); } - -static int verify_executables(Unit *u, const char *root) { - ExecCommand *exec; - int r = 0, k; - unsigned i; - - assert(u); - - exec = u->type == UNIT_SOCKET ? SOCKET(u)->control_command : - u->type == UNIT_MOUNT ? MOUNT(u)->control_command : - u->type == UNIT_SWAP ? SWAP(u)->control_command : NULL; - k = verify_executable(u, exec, root); - if (k < 0 && r == 0) - r = k; - - if (u->type == UNIT_SERVICE) - for (i = 0; i < ELEMENTSOF(SERVICE(u)->exec_command); i++) { - k = verify_executable(u, SERVICE(u)->exec_command[i], root); - if (k < 0 && r == 0) - r = k; - } - - if (u->type == UNIT_SOCKET) - for (i = 0; i < ELEMENTSOF(SOCKET(u)->exec_command); i++) { - k = verify_executable(u, SOCKET(u)->exec_command[i], root); - if (k < 0 && r == 0) - r = k; - } - - return r; -} - -static int verify_documentation(Unit *u, bool check_man) { - char **p; - int r = 0, k; - - STRV_FOREACH(p, u->documentation) { - log_unit_debug(u, "Found documentation item: %s", *p); - - if (check_man && startswith(*p, "man:")) { - k = show_man_page(*p + 4, true); - if (k != 0) { - if (k < 0) - log_unit_error_errno(u, k, "Can't show %s: %m", *p + 4); - else { - log_unit_error(u, "Command 'man %s' failed with code %d", *p + 4, k); - k = -ENOEXEC; - } - if (r == 0) - r = k; - } - } - } - - /* Check remote URLs? */ - - return r; -} - -static int verify_unit(Unit *u, bool check_man, const char *root) { - _cleanup_(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; - int r, k; - - assert(u); - - if (DEBUG_LOGGING) - unit_dump(u, stdout, "\t"); - - log_unit_debug(u, "Creating %s/start job", u->id); - r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, NULL, &err, NULL); - if (r < 0) - log_unit_error_errno(u, r, "Failed to create %s/start: %s", u->id, bus_error_message(&err, r)); - - k = verify_socket(u); - if (k < 0 && r == 0) - r = k; - - k = verify_executables(u, root); - if (k < 0 && r == 0) - r = k; - - k = verify_documentation(u, check_man); - if (k < 0 && r == 0) - r = k; - - return r; -} - -static void set_destroy_ignore_pointer_max(Set** s) { - if (*s == POINTER_MAX) - return; - set_free_free(*s); -} - -int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root) { - const ManagerTestRunFlags flags = - MANAGER_TEST_RUN_MINIMAL | - MANAGER_TEST_RUN_ENV_GENERATORS | - (recursive_errors == RECURSIVE_ERRORS_NO) * MANAGER_TEST_RUN_IGNORE_DEPENDENCIES | - run_generators * MANAGER_TEST_RUN_GENERATORS; - - _cleanup_(manager_freep) Manager *m = NULL; - _cleanup_(set_destroy_ignore_pointer_max) Set *s = NULL; - _unused_ _cleanup_(clear_log_syntax_callback) dummy_t dummy; - Unit *units[strv_length(filenames)]; - _cleanup_free_ char *var = NULL; - int r, k, i, count = 0; - char **filename; - - if (strv_isempty(filenames)) - return 0; - - /* Allow systemd-analyze to hook in a callback function so that it can get - * all the required log data from the function itself without having to rely - * on a global set variable for the same */ - set_log_syntax_callback(log_syntax_callback, &s); - - /* set the path */ - r = verify_generate_path(&var, filenames); - if (r < 0) - return log_error_errno(r, "Failed to generate unit load path: %m"); - - assert_se(set_unit_path(var) >= 0); - - r = manager_new(scope, flags, &m); - if (r < 0) - return log_error_errno(r, "Failed to initialize manager: %m"); - - log_debug("Starting manager..."); - - r = manager_startup(m, /* serialization= */ NULL, /* fds= */ NULL, root); - if (r < 0) - return r; - - manager_clear_jobs(m); - - log_debug("Loading remaining units from the command line..."); - - STRV_FOREACH(filename, filenames) { - _cleanup_free_ char *prepared = NULL; - - log_debug("Handling %s...", *filename); - - k = verify_prepare_filename(*filename, &prepared); - if (k < 0) { - log_error_errno(k, "Failed to prepare filename %s: %m", *filename); - if (r == 0) - r = k; - continue; - } - - k = manager_load_startable_unit_or_warn(m, NULL, prepared, &units[count]); - if (k < 0) { - if (r == 0) - r = k; - continue; - } - - count++; - } - - for (i = 0; i < count; i++) { - k = verify_unit(units[i], check_man, root); - if (k < 0 && r == 0) - r = k; - } - - if (s == POINTER_MAX) - return log_oom(); - - if (set_isempty(s) || r != 0) - return r; - - /* If all previous verifications succeeded, then either the recursive parsing of all the - * associated dependencies with RECURSIVE_ERRORS_YES or the parsing of the specified unit file - * with RECURSIVE_ERRORS_NO must have yielded a syntax warning and hence, a non-empty set. */ - if (IN_SET(recursive_errors, RECURSIVE_ERRORS_YES, RECURSIVE_ERRORS_NO)) - return -ENOTRECOVERABLE; - - /* If all previous verifications succeeded, then the non-empty set could have resulted from - * a syntax warning encountered during the recursive parsing of the specified unit file and - * its direct dependencies. Hence, search for any of the filenames in the set and if found, - * return a non-zero process exit status. */ - if (recursive_errors == RECURSIVE_ERRORS_ONE) - STRV_FOREACH(filename, filenames) - if (set_contains(s, basename(*filename))) - return -ENOTRECOVERABLE; - - return 0; -} - -static const char* const recursive_errors_table[_RECURSIVE_ERRORS_MAX] = { - [RECURSIVE_ERRORS_NO] = "no", - [RECURSIVE_ERRORS_YES] = "yes", - [RECURSIVE_ERRORS_ONE] = "one", -}; - -DEFINE_STRING_TABLE_LOOKUP(recursive_errors, RecursiveErrors); diff --git a/src/analyze/analyze-verify.h b/src/analyze/analyze-verify.h index 47b78a815..4892c9aa4 100644 --- a/src/analyze/analyze-verify.h +++ b/src/analyze/analyze-verify.h @@ -1,23 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include - -#include "execute.h" -#include "path-lookup.h" - -typedef enum RecursiveErrors { - RECURSIVE_ERRORS_YES, /* Look for errors in all associated units */ - RECURSIVE_ERRORS_NO, /* Don't look for errors in any but the selected unit */ - RECURSIVE_ERRORS_ONE, /* Look for errors in the selected unit and its direct dependencies */ - _RECURSIVE_ERRORS_MAX, - _RECURSIVE_ERRORS_INVALID = -EINVAL, -} RecursiveErrors; - -int verify_generate_path(char **var, char **filenames); -int verify_prepare_filename(const char *filename, char **ret); -int verify_executable(Unit *u, const ExecCommand *exec, const char *root); -int verify_units(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, RecursiveErrors recursive_errors, const char *root); - -const char* recursive_errors_to_string(RecursiveErrors i) _const_; -RecursiveErrors recursive_errors_from_string(const char *s) _pure_; +int verb_verify(int argc, char *argv[], void *userdata); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index a1908ff44..905ff2c27 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -12,9 +12,29 @@ #include "sd-bus.h" #include "alloc-util.h" +#include "analyze.h" +#include "analyze-blame.h" +#include "analyze-calendar.h" +#include "analyze-capability.h" +#include "analyze-cat-config.h" #include "analyze-condition.h" -#include "analyze-elf.h" +#include "analyze-critical-chain.h" +#include "analyze-dot.h" +#include "analyze-dump.h" +#include "analyze-exit-status.h" +#include "analyze-filesystems.h" +#include "analyze-inspect-elf.h" +#include "analyze-log-control.h" +#include "analyze-plot.h" #include "analyze-security.h" +#include "analyze-service-watchdogs.h" +#include "analyze-syscall-filter.h" +#include "analyze-time.h" +#include "analyze-time-data.h" +#include "analyze-timespan.h" +#include "analyze-timestamp.h" +#include "analyze-unit-files.h" +#include "analyze-unit-paths.h" #include "analyze-verify.h" #include "bus-error.h" #include "bus-locator.h" @@ -63,50 +83,27 @@ #include "verbs.h" #include "version.h" -#define SCALE_X (0.1 / 1000.0) /* pixels per us */ -#define SCALE_Y (20.0) - -#define svg(...) printf(__VA_ARGS__) - -#define svg_bar(class, x1, x2, y) \ - svg(" \n", \ - (class), \ - SCALE_X * (x1), SCALE_Y * (y), \ - SCALE_X * ((x2) - (x1)), SCALE_Y - 1.0) - -#define svg_text(b, x, y, format, ...) \ - do { \ - svg(" ", (b) ? "left" : "right", SCALE_X * (x) + (b ? 5.0 : -5.0), SCALE_Y * (y) + 14.0); \ - svg(format, ## __VA_ARGS__); \ - svg("\n"); \ - } while (false) - -static enum dot { - DEP_ALL, - DEP_ORDER, - DEP_REQUIRE -} arg_dot = DEP_ALL; -static char **arg_dot_from_patterns = NULL; -static char **arg_dot_to_patterns = NULL; -static usec_t arg_fuzz = 0; -static PagerFlags arg_pager_flags = 0; -static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; -static const char *arg_host = NULL; -static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; -static RecursiveErrors arg_recursive_errors = RECURSIVE_ERRORS_YES; -static bool arg_man = true; -static bool arg_generators = false; -static char *arg_root = NULL; +DotMode arg_dot = DEP_ALL; +char **arg_dot_from_patterns = NULL, **arg_dot_to_patterns = NULL; +usec_t arg_fuzz = 0; +PagerFlags arg_pager_flags = 0; +BusTransport arg_transport = BUS_TRANSPORT_LOCAL; +const char *arg_host = NULL; +LookupScope arg_scope = LOOKUP_SCOPE_SYSTEM; +RecursiveErrors arg_recursive_errors = RECURSIVE_ERRORS_YES; +bool arg_man = true; +bool arg_generators = false; +char *arg_root = NULL; static char *arg_image = NULL; -static char *arg_security_policy = NULL; -static bool arg_offline = false; -static unsigned arg_threshold = 100; -static unsigned arg_iterations = 1; -static usec_t arg_base_time = USEC_INFINITY; -static char *arg_unit = NULL; -static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; -static bool arg_quiet = false; -static char *arg_profile = NULL; +char *arg_security_policy = NULL; +bool arg_offline = false; +unsigned arg_threshold = 100; +unsigned arg_iterations = 1; +usec_t arg_base_time = USEC_INFINITY; +char *arg_unit = NULL; +JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; +bool arg_quiet = false; +char *arg_profile = NULL; STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep); @@ -116,62 +113,8 @@ STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep); STATIC_DESTRUCTOR_REGISTER(arg_unit, freep); STATIC_DESTRUCTOR_REGISTER(arg_profile, freep); -typedef struct BootTimes { - usec_t firmware_time; - usec_t loader_time; - usec_t kernel_time; - usec_t kernel_done_time; - usec_t initrd_time; - usec_t userspace_time; - usec_t finish_time; - usec_t security_start_time; - usec_t security_finish_time; - usec_t generators_start_time; - usec_t generators_finish_time; - usec_t unitsload_start_time; - usec_t unitsload_finish_time; - usec_t initrd_security_start_time; - usec_t initrd_security_finish_time; - usec_t initrd_generators_start_time; - usec_t initrd_generators_finish_time; - usec_t initrd_unitsload_start_time; - usec_t initrd_unitsload_finish_time; - - /* - * If we're analyzing the user instance, all timestamps will be offset - * by its own start-up timestamp, which may be arbitrarily big. - * With "plot", this causes arbitrarily wide output SVG files which almost - * completely consist of empty space. Thus we cancel out this offset. - * - * This offset is subtracted from times above by acquire_boot_times(), - * but it still needs to be subtracted from unit-specific timestamps - * (so it is stored here for reference). - */ - usec_t reverse_offset; -} BootTimes; - -typedef struct UnitTimes { - bool has_data; - char *name; - usec_t activating; - usec_t activated; - usec_t deactivated; - usec_t deactivating; - usec_t time; -} UnitTimes; - -typedef struct HostInfo { - char *hostname; - char *kernel_name; - char *kernel_release; - char *kernel_version; - char *os_pretty_name; - char *virtualization; - char *architecture; -} HostInfo; - -static int acquire_bus(sd_bus **bus, bool *use_full_bus) { - bool user = arg_scope != UNIT_FILE_SYSTEM; +int acquire_bus(sd_bus **bus, bool *use_full_bus) { + bool user = arg_scope != LOOKUP_SCOPE_SYSTEM; int r; if (use_full_bus && *use_full_bus) { @@ -185,32 +128,7 @@ static int acquire_bus(sd_bus **bus, bool *use_full_bus) { return bus_connect_transport_systemd(arg_transport, arg_host, user, bus); } -static int bus_get_uint64_property(sd_bus *bus, const char *path, const char *interface, const char *property, uint64_t *val) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - int r; - - assert(bus); - assert(path); - assert(interface); - assert(property); - assert(val); - - r = sd_bus_get_property_trivial( - bus, - "org.freedesktop.systemd1", - path, - interface, - property, - &error, - 't', val); - - if (r < 0) - return log_error_errno(r, "Failed to parse reply: %s", bus_error_message(&error, r)); - - return 0; -} - -static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) { +int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; @@ -233,1802 +151,7 @@ static int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char return 0; } -static int compare_unit_start(const UnitTimes *a, const UnitTimes *b) { - return CMP(a->activating, b->activating); -} - -static int process_aliases(char *argv[], char *tempdir, char ***ret) { - _cleanup_strv_free_ char **filenames = NULL; - char **filename; - int r; - - assert(argv); - assert(tempdir); - assert(ret); - - STRV_FOREACH(filename, strv_skip(argv, 1)) { - _cleanup_free_ char *src = NULL, *dst = NULL, *base = NULL; - const char *parse_arg; - - parse_arg = *filename; - r = extract_first_word(&parse_arg, &src, ":", EXTRACT_DONT_COALESCE_SEPARATORS|EXTRACT_RETAIN_ESCAPE); - if (r < 0) - return r; - - if (!parse_arg) { - r = strv_consume(&filenames, TAKE_PTR(src)); - if (r < 0) - return r; - - continue; - } - - r = path_extract_filename(parse_arg, &base); - if (r < 0) - return r; - - dst = path_join(tempdir, base); - if (!dst) - return -ENOMEM; - - r = copy_file(src, dst, 0, 0644, 0, 0, COPY_REFLINK); - if (r < 0) - return r; - - r = strv_consume(&filenames, TAKE_PTR(dst)); - if (r < 0) - return r; - } - - *ret = TAKE_PTR(filenames); - return 0; -} - -static UnitTimes* unit_times_free_array(UnitTimes *t) { - for (UnitTimes *p = t; p && p->has_data; p++) - free(p->name); - return mfree(t); -} -DEFINE_TRIVIAL_CLEANUP_FUNC(UnitTimes*, unit_times_free_array); - -static void subtract_timestamp(usec_t *a, usec_t b) { - assert(a); - - if (*a > 0) { - assert(*a >= b); - *a -= b; - } -} - -static int acquire_boot_times(sd_bus *bus, BootTimes **bt) { - static const struct bus_properties_map property_map[] = { - { "FirmwareTimestampMonotonic", "t", NULL, offsetof(BootTimes, firmware_time) }, - { "LoaderTimestampMonotonic", "t", NULL, offsetof(BootTimes, loader_time) }, - { "KernelTimestamp", "t", NULL, offsetof(BootTimes, kernel_time) }, - { "InitRDTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_time) }, - { "UserspaceTimestampMonotonic", "t", NULL, offsetof(BootTimes, userspace_time) }, - { "FinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, finish_time) }, - { "SecurityStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_start_time) }, - { "SecurityFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, security_finish_time) }, - { "GeneratorsStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_start_time) }, - { "GeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, generators_finish_time) }, - { "UnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, unitsload_start_time) }, - { "UnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, unitsload_finish_time) }, - { "InitRDSecurityStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_security_start_time) }, - { "InitRDSecurityFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_security_finish_time) }, - { "InitRDGeneratorsStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_generators_start_time) }, - { "InitRDGeneratorsFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_generators_finish_time) }, - { "InitRDUnitsLoadStartTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_start_time) }, - { "InitRDUnitsLoadFinishTimestampMonotonic", "t", NULL, offsetof(BootTimes, initrd_unitsload_finish_time) }, - {}, - }; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - static BootTimes times; - static bool cached = false; - int r; - - if (cached) - goto finish; - - assert_cc(sizeof(usec_t) == sizeof(uint64_t)); - - r = bus_map_all_properties( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - property_map, - BUS_MAP_STRDUP, - &error, - NULL, - ×); - if (r < 0) - return log_error_errno(r, "Failed to get timestamp properties: %s", bus_error_message(&error, r)); - - if (times.finish_time <= 0) - return log_error_errno(SYNTHETIC_ERRNO(EINPROGRESS), - "Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n" - "Please try again later.\n" - "Hint: Use 'systemctl%s list-jobs' to see active jobs", - times.finish_time, - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user"); - - if (arg_scope == UNIT_FILE_SYSTEM && times.security_start_time > 0) { - /* security_start_time is set when systemd is not running under container environment. */ - if (times.initrd_time > 0) - times.kernel_done_time = times.initrd_time; - else - times.kernel_done_time = times.userspace_time; - } else { - /* - * User-instance-specific or container-system-specific timestamps processing - * (see comment to reverse_offset in BootTimes). - */ - times.reverse_offset = times.userspace_time; - - times.firmware_time = times.loader_time = times.kernel_time = times.initrd_time = - times.userspace_time = times.security_start_time = times.security_finish_time = 0; - - subtract_timestamp(×.finish_time, times.reverse_offset); - - subtract_timestamp(×.generators_start_time, times.reverse_offset); - subtract_timestamp(×.generators_finish_time, times.reverse_offset); - - subtract_timestamp(×.unitsload_start_time, times.reverse_offset); - subtract_timestamp(×.unitsload_finish_time, times.reverse_offset); - } - - cached = true; - -finish: - *bt = × - return 0; -} - -static HostInfo* free_host_info(HostInfo *hi) { - if (!hi) - return NULL; - - free(hi->hostname); - free(hi->kernel_name); - free(hi->kernel_release); - free(hi->kernel_version); - free(hi->os_pretty_name); - free(hi->virtualization); - free(hi->architecture); - return mfree(hi); -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(HostInfo *, free_host_info); - -static int acquire_time_data(sd_bus *bus, UnitTimes **out) { - static const struct bus_properties_map property_map[] = { - { "InactiveExitTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activating) }, - { "ActiveEnterTimestampMonotonic", "t", NULL, offsetof(UnitTimes, activated) }, - { "ActiveExitTimestampMonotonic", "t", NULL, offsetof(UnitTimes, deactivating) }, - { "InactiveEnterTimestampMonotonic", "t", NULL, offsetof(UnitTimes, deactivated) }, - {}, - }; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(unit_times_free_arrayp) UnitTimes *unit_times = NULL; - BootTimes *boot_times = NULL; - size_t c = 0; - UnitInfo u; - int r; - - r = acquire_boot_times(bus, &boot_times); - if (r < 0) - return r; - - r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL); - if (r < 0) - return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); - if (r < 0) - return bus_log_parse_error(r); - - while ((r = bus_parse_unit_info(reply, &u)) > 0) { - UnitTimes *t; - - if (!GREEDY_REALLOC(unit_times, c + 2)) - return log_oom(); - - unit_times[c + 1].has_data = false; - t = &unit_times[c]; - t->name = NULL; - - assert_cc(sizeof(usec_t) == sizeof(uint64_t)); - - r = bus_map_all_properties( - bus, - "org.freedesktop.systemd1", - u.unit_path, - property_map, - BUS_MAP_STRDUP, - &error, - NULL, - t); - if (r < 0) - return log_error_errno(r, "Failed to get timestamp properties of unit %s: %s", - u.id, bus_error_message(&error, r)); - - subtract_timestamp(&t->activating, boot_times->reverse_offset); - subtract_timestamp(&t->activated, boot_times->reverse_offset); - subtract_timestamp(&t->deactivating, boot_times->reverse_offset); - subtract_timestamp(&t->deactivated, boot_times->reverse_offset); - - if (t->activated >= t->activating) - t->time = t->activated - t->activating; - else if (t->deactivated >= t->activating) - t->time = t->deactivated - t->activating; - else - t->time = 0; - - if (t->activating == 0) - continue; - - t->name = strdup(u.id); - if (!t->name) - return log_oom(); - - t->has_data = true; - c++; - } - if (r < 0) - return bus_log_parse_error(r); - - *out = TAKE_PTR(unit_times); - return c; -} - -static int acquire_host_info(sd_bus *bus, HostInfo **hi) { - static const struct bus_properties_map hostname_map[] = { - { "Hostname", "s", NULL, offsetof(HostInfo, hostname) }, - { "KernelName", "s", NULL, offsetof(HostInfo, kernel_name) }, - { "KernelRelease", "s", NULL, offsetof(HostInfo, kernel_release) }, - { "KernelVersion", "s", NULL, offsetof(HostInfo, kernel_version) }, - { "OperatingSystemPrettyName", "s", NULL, offsetof(HostInfo, os_pretty_name) }, - {} - }; - - static const struct bus_properties_map manager_map[] = { - { "Virtualization", "s", NULL, offsetof(HostInfo, virtualization) }, - { "Architecture", "s", NULL, offsetof(HostInfo, architecture) }, - {} - }; - - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *system_bus = NULL; - _cleanup_(free_host_infop) HostInfo *host = NULL; - int r; - - host = new0(HostInfo, 1); - if (!host) - return log_oom(); - - if (arg_scope != UNIT_FILE_SYSTEM) { - r = bus_connect_transport(arg_transport, arg_host, false, &system_bus); - if (r < 0) { - log_debug_errno(r, "Failed to connect to system bus, ignoring: %m"); - goto manager; - } - } - - r = bus_map_all_properties( - system_bus ?: bus, - "org.freedesktop.hostname1", - "/org/freedesktop/hostname1", - hostname_map, - BUS_MAP_STRDUP, - &error, - NULL, - host); - if (r < 0) { - log_debug_errno(r, "Failed to get host information from systemd-hostnamed, ignoring: %s", - bus_error_message(&error, r)); - sd_bus_error_free(&error); - } - -manager: - r = bus_map_all_properties( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - manager_map, - BUS_MAP_STRDUP, - &error, - NULL, - host); - if (r < 0) - return log_error_errno(r, "Failed to get host information from systemd: %s", - bus_error_message(&error, r)); - - *hi = TAKE_PTR(host); - return 0; -} - -static int pretty_boot_time(sd_bus *bus, char **_buf) { - BootTimes *t; - static char buf[4096]; - size_t size; - char *ptr; - int r; - usec_t activated_time = USEC_INFINITY; - _cleanup_free_ char *path = NULL, *unit_id = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - - r = acquire_boot_times(bus, &t); - if (r < 0) - return r; - - path = unit_dbus_path_from_name(SPECIAL_DEFAULT_TARGET); - if (!path) - return log_oom(); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "Id", - &error, - &unit_id); - if (r < 0) { - log_error_errno(r, "default.target doesn't seem to exist: %s", bus_error_message(&error, r)); - unit_id = NULL; - } - - r = bus_get_uint64_property(bus, path, - "org.freedesktop.systemd1.Unit", - "ActiveEnterTimestampMonotonic", - &activated_time); - if (r < 0) { - log_info_errno(r, "Could not get time to reach default.target, ignoring: %m"); - activated_time = USEC_INFINITY; - } - - ptr = buf; - size = sizeof(buf); - - size = strpcpyf(&ptr, size, "Startup finished in "); - if (t->firmware_time > 0) - size = strpcpyf(&ptr, size, "%s (firmware) + ", FORMAT_TIMESPAN(t->firmware_time - t->loader_time, USEC_PER_MSEC)); - if (t->loader_time > 0) - size = strpcpyf(&ptr, size, "%s (loader) + ", FORMAT_TIMESPAN(t->loader_time, USEC_PER_MSEC)); - if (t->kernel_done_time > 0) - size = strpcpyf(&ptr, size, "%s (kernel) + ", FORMAT_TIMESPAN(t->kernel_done_time, USEC_PER_MSEC)); - if (t->initrd_time > 0) - size = strpcpyf(&ptr, size, "%s (initrd) + ", FORMAT_TIMESPAN(t->userspace_time - t->initrd_time, USEC_PER_MSEC)); - - size = strpcpyf(&ptr, size, "%s (userspace) ", FORMAT_TIMESPAN(t->finish_time - t->userspace_time, USEC_PER_MSEC)); - if (t->kernel_done_time > 0) - strpcpyf(&ptr, size, "= %s ", FORMAT_TIMESPAN(t->firmware_time + t->finish_time, USEC_PER_MSEC)); - - if (unit_id && timestamp_is_set(activated_time)) { - usec_t base = t->userspace_time > 0 ? t->userspace_time : t->reverse_offset; - - size = strpcpyf(&ptr, size, "\n%s reached after %s in userspace", unit_id, - FORMAT_TIMESPAN(activated_time - base, USEC_PER_MSEC)); - } else if (unit_id && activated_time == 0) - size = strpcpyf(&ptr, size, "\n%s was never reached", unit_id); - else if (unit_id && activated_time == USEC_INFINITY) - size = strpcpyf(&ptr, size, "\nCould not get time to reach %s.", unit_id); - else if (!unit_id) - size = strpcpyf(&ptr, size, "\ncould not find default.target"); - - ptr = strdup(buf); - if (!ptr) - return log_oom(); - - *_buf = ptr; - return 0; -} - -static void svg_graph_box(double height, double begin, double end) { - /* outside box, fill */ - svg("\n", - SCALE_X * (end - begin), - SCALE_Y * height); - - for (long long i = ((long long) (begin / 100000)) * 100000; i <= end; i += 100000) { - /* lines for each second */ - if (i % 5000000 == 0) - svg(" \n" - " %.01fs\n", - SCALE_X * i, - SCALE_X * i, - SCALE_Y * height, - SCALE_X * i, - -5.0, - 0.000001 * i); - else if (i % 1000000 == 0) - svg(" \n" - " %.01fs\n", - SCALE_X * i, - SCALE_X * i, - SCALE_Y * height, - SCALE_X * i, - -5.0, - 0.000001 * i); - else - svg(" \n", - SCALE_X * i, - SCALE_X * i, - SCALE_Y * height); - } -} - -static int plot_unit_times(UnitTimes *u, double width, int y) { - bool b; - - if (!u->name) - return 0; - - svg_bar("activating", u->activating, u->activated, y); - svg_bar("active", u->activated, u->deactivating, y); - svg_bar("deactivating", u->deactivating, u->deactivated, y); - - /* place the text on the left if we have passed the half of the svg width */ - b = u->activating * SCALE_X < width / 2; - if (u->time) - svg_text(b, u->activating, y, "%s (%s)", - u->name, FORMAT_TIMESPAN(u->time, USEC_PER_MSEC)); - else - svg_text(b, u->activating, y, "%s", u->name); - - return 1; -} - -static int analyze_plot(int argc, char *argv[], void *userdata) { - _cleanup_(free_host_infop) HostInfo *host = NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL; - _cleanup_free_ char *pretty_times = NULL; - bool use_full_bus = arg_scope == UNIT_FILE_SYSTEM; - BootTimes *boot; - UnitTimes *u; - int n, m = 1, y = 0, r; - double width; - - r = acquire_bus(&bus, &use_full_bus); - if (r < 0) - return bus_log_connect_error(r, arg_transport); - - n = acquire_boot_times(bus, &boot); - if (n < 0) - return n; - - n = pretty_boot_time(bus, &pretty_times); - if (n < 0) - return n; - - if (use_full_bus || arg_scope != UNIT_FILE_SYSTEM) { - n = acquire_host_info(bus, &host); - if (n < 0) - return n; - } - - n = acquire_time_data(bus, ×); - if (n <= 0) - return n; - - typesafe_qsort(times, n, compare_unit_start); - - width = SCALE_X * (boot->firmware_time + boot->finish_time); - if (width < 800.0) - width = 800.0; - - if (boot->firmware_time > boot->loader_time) - m++; - if (boot->loader_time > 0) { - m++; - if (width < 1000.0) - width = 1000.0; - } - if (boot->initrd_time > 0) - m++; - if (boot->kernel_done_time > 0) - m++; - - for (u = times; u->has_data; u++) { - double text_start, text_width; - - if (u->activating > boot->finish_time) { - u->name = mfree(u->name); - continue; - } - - /* If the text cannot fit on the left side then - * increase the svg width so it fits on the right. - * TODO: calculate the text width more accurately */ - text_width = 8.0 * strlen(u->name); - text_start = (boot->firmware_time + u->activating) * SCALE_X; - if (text_width > text_start && text_width + text_start > width) - width = text_width + text_start; - - if (u->deactivated > u->activating && - u->deactivated <= boot->finish_time && - u->activated == 0 && u->deactivating == 0) - u->activated = u->deactivating = u->deactivated; - if (u->activated < u->activating || u->activated > boot->finish_time) - u->activated = boot->finish_time; - if (u->deactivating < u->activated || u->deactivating > boot->finish_time) - u->deactivating = boot->finish_time; - if (u->deactivated < u->deactivating || u->deactivated > boot->finish_time) - u->deactivated = boot->finish_time; - m++; - } - - svg("\n" - "\n"); - - svg("\n\n", - 80.0 + width, 150.0 + (m * SCALE_Y) + - 5 * SCALE_Y /* legend */); - - /* write some basic info as a comment, including some help */ - svg("\n" - "\n" - "\n" - "\n" - "\n\n" - "\n\n", GIT_VERSION); - - /* style sheet */ - svg("\n \n\n\n"); - - svg("\n"); - svg("%s", pretty_times); - if (host) - svg("%s %s (%s %s %s) %s %s", - isempty(host->os_pretty_name) ? "Linux" : host->os_pretty_name, - strempty(host->hostname), - strempty(host->kernel_name), - strempty(host->kernel_release), - strempty(host->kernel_version), - strempty(host->architecture), - strempty(host->virtualization)); - - svg("\n", 20.0 + (SCALE_X * boot->firmware_time)); - svg_graph_box(m, -(double) boot->firmware_time, boot->finish_time); - - if (boot->firmware_time > 0) { - svg_bar("firmware", -(double) boot->firmware_time, -(double) boot->loader_time, y); - svg_text(true, -(double) boot->firmware_time, y, "firmware"); - y++; - } - if (boot->loader_time > 0) { - svg_bar("loader", -(double) boot->loader_time, 0, y); - svg_text(true, -(double) boot->loader_time, y, "loader"); - y++; - } - if (boot->kernel_done_time > 0) { - svg_bar("kernel", 0, boot->kernel_done_time, y); - svg_text(true, 0, y, "kernel"); - y++; - } - if (boot->initrd_time > 0) { - svg_bar("initrd", boot->initrd_time, boot->userspace_time, y); - if (boot->initrd_security_start_time < boot->initrd_security_finish_time) - svg_bar("security", boot->initrd_security_start_time, boot->initrd_security_finish_time, y); - if (boot->initrd_generators_start_time < boot->initrd_generators_finish_time) - svg_bar("generators", boot->initrd_generators_start_time, boot->initrd_generators_finish_time, y); - if (boot->initrd_unitsload_start_time < boot->initrd_unitsload_finish_time) - svg_bar("unitsload", boot->initrd_unitsload_start_time, boot->initrd_unitsload_finish_time, y); - svg_text(true, boot->initrd_time, y, "initrd"); - y++; - } - - for (u = times; u->has_data; u++) { - if (u->activating >= boot->userspace_time) - break; - - y += plot_unit_times(u, width, y); - } - - svg_bar("active", boot->userspace_time, boot->finish_time, y); - if (boot->security_start_time > 0) - svg_bar("security", boot->security_start_time, boot->security_finish_time, y); - svg_bar("generators", boot->generators_start_time, boot->generators_finish_time, y); - svg_bar("unitsload", boot->unitsload_start_time, boot->unitsload_finish_time, y); - svg_text(true, boot->userspace_time, y, "systemd"); - y++; - - for (; u->has_data; u++) - y += plot_unit_times(u, width, y); - - svg("\n"); - - /* Legend */ - svg("\n"); - y++; - svg_bar("activating", 0, 300000, y); - svg_text(true, 400000, y, "Activating"); - y++; - svg_bar("active", 0, 300000, y); - svg_text(true, 400000, y, "Active"); - y++; - svg_bar("deactivating", 0, 300000, y); - svg_text(true, 400000, y, "Deactivating"); - y++; - if (boot->security_start_time > 0) { - svg_bar("security", 0, 300000, y); - svg_text(true, 400000, y, "Setting up security module"); - y++; - } - svg_bar("generators", 0, 300000, y); - svg_text(true, 400000, y, "Generators"); - y++; - svg_bar("unitsload", 0, 300000, y); - svg_text(true, 400000, y, "Loading unit files"); - y++; - - svg("\n\n"); - - svg("\n"); - - return 0; -} - -static int list_dependencies_print( - const char *name, - unsigned level, - unsigned branches, - bool last, - UnitTimes *times, - BootTimes *boot) { - - for (unsigned i = level; i != 0; i--) - printf("%s", special_glyph(branches & (1 << (i-1)) ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE)); - - printf("%s", special_glyph(last ? SPECIAL_GLYPH_TREE_RIGHT : SPECIAL_GLYPH_TREE_BRANCH)); - - if (times) { - if (times->time > 0) - printf("%s%s @%s +%s%s", ansi_highlight_red(), name, - FORMAT_TIMESPAN(times->activating - boot->userspace_time, USEC_PER_MSEC), - FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal()); - else if (times->activated > boot->userspace_time) - printf("%s @%s", name, FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC)); - else - printf("%s", name); - } else - printf("%s", name); - printf("\n"); - - return 0; -} - -static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) { - _cleanup_free_ char *path = NULL; - - assert(bus); - assert(name); - assert(deps); - - path = unit_dbus_path_from_name(name); - if (!path) - return -ENOMEM; - - return bus_get_unit_property_strv(bus, path, "After", deps); -} - -static Hashmap *unit_times_hashmap; - -static int list_dependencies_compare(char *const *a, char *const *b) { - usec_t usa = 0, usb = 0; - UnitTimes *times; - - times = hashmap_get(unit_times_hashmap, *a); - if (times) - usa = times->activated; - times = hashmap_get(unit_times_hashmap, *b); - if (times) - usb = times->activated; - - return CMP(usb, usa); -} - -static bool times_in_range(const UnitTimes *times, const BootTimes *boot) { - return times && times->activated > 0 && times->activated <= boot->finish_time; -} - -static int list_dependencies_one(sd_bus *bus, const char *name, unsigned level, char ***units, unsigned branches) { - _cleanup_strv_free_ char **deps = NULL; - char **c; - int r; - usec_t service_longest = 0; - int to_print = 0; - UnitTimes *times; - BootTimes *boot; - - if (strv_extend(units, name)) - return log_oom(); - - r = list_dependencies_get_dependencies(bus, name, &deps); - if (r < 0) - return r; - - typesafe_qsort(deps, strv_length(deps), list_dependencies_compare); - - r = acquire_boot_times(bus, &boot); - if (r < 0) - return r; - - STRV_FOREACH(c, deps) { - times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */ - if (times_in_range(times, boot) && times->activated >= service_longest) - service_longest = times->activated; - } - - if (service_longest == 0) - return r; - - STRV_FOREACH(c, deps) { - times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */ - if (times_in_range(times, boot) && service_longest - times->activated <= arg_fuzz) - to_print++; - } - - if (!to_print) - return r; - - STRV_FOREACH(c, deps) { - times = hashmap_get(unit_times_hashmap, *c); /* lgtm [cpp/inconsistent-null-check] */ - if (!times_in_range(times, boot) || service_longest - times->activated > arg_fuzz) - continue; - - to_print--; - - r = list_dependencies_print(*c, level, branches, to_print == 0, times, boot); - if (r < 0) - return r; - - if (strv_contains(*units, *c)) { - r = list_dependencies_print("...", level + 1, (branches << 1) | (to_print ? 1 : 0), - true, NULL, boot); - if (r < 0) - return r; - continue; - } - - r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (to_print ? 1 : 0)); - if (r < 0) - return r; - - if (to_print == 0) - break; - } - return 0; -} - -static int list_dependencies(sd_bus *bus, const char *name) { - _cleanup_strv_free_ char **units = NULL; - UnitTimes *times; - int r; - const char *id; - _cleanup_free_ char *path = NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - BootTimes *boot; - - assert(bus); - - path = unit_dbus_path_from_name(name); - if (!path) - return -ENOMEM; - - r = sd_bus_get_property( - bus, - "org.freedesktop.systemd1", - path, - "org.freedesktop.systemd1.Unit", - "Id", - &error, - &reply, - "s"); - if (r < 0) - return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); - - r = sd_bus_message_read(reply, "s", &id); - if (r < 0) - return bus_log_parse_error(r); - - times = hashmap_get(unit_times_hashmap, id); - - r = acquire_boot_times(bus, &boot); - if (r < 0) - return r; - - if (times) { - if (times->time) - printf("%s%s +%s%s\n", ansi_highlight_red(), id, - FORMAT_TIMESPAN(times->time, USEC_PER_MSEC), ansi_normal()); - else if (times->activated > boot->userspace_time) - printf("%s @%s\n", id, - FORMAT_TIMESPAN(times->activated - boot->userspace_time, USEC_PER_MSEC)); - else - printf("%s\n", id); - } - - return list_dependencies_one(bus, name, 0, &units, 0); -} - -static int analyze_critical_chain(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL; - Hashmap *h; - int n, r; - - r = acquire_bus(&bus, NULL); - if (r < 0) - return bus_log_connect_error(r, arg_transport); - - n = acquire_time_data(bus, ×); - if (n <= 0) - return n; - - h = hashmap_new(&string_hash_ops); - if (!h) - return log_oom(); - - for (UnitTimes *u = times; u->has_data; u++) { - r = hashmap_put(h, u->name, u); - if (r < 0) - return log_error_errno(r, "Failed to add entry to hashmap: %m"); - } - unit_times_hashmap = h; - - pager_open(arg_pager_flags); - - puts("The time when unit became active or started is printed after the \"@\" character.\n" - "The time the unit took to start is printed after the \"+\" character.\n"); - - if (argc > 1) { - char **name; - STRV_FOREACH(name, strv_skip(argv, 1)) - list_dependencies(bus, *name); - } else - list_dependencies(bus, SPECIAL_DEFAULT_TARGET); - - h = hashmap_free(h); - return 0; -} - -static int analyze_blame(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_(unit_times_free_arrayp) UnitTimes *times = NULL; - _cleanup_(table_unrefp) Table *table = NULL; - TableCell *cell; - int n, r; - - r = acquire_bus(&bus, NULL); - if (r < 0) - return bus_log_connect_error(r, arg_transport); - - n = acquire_time_data(bus, ×); - if (n <= 0) - return n; - - table = table_new("time", "unit"); - if (!table) - return log_oom(); - - table_set_header(table, false); - - assert_se(cell = table_get_cell(table, 0, 0)); - r = table_set_ellipsize_percent(table, cell, 100); - if (r < 0) - return r; - - r = table_set_align_percent(table, cell, 100); - if (r < 0) - return r; - - assert_se(cell = table_get_cell(table, 0, 1)); - r = table_set_ellipsize_percent(table, cell, 100); - if (r < 0) - return r; - - r = table_set_sort(table, (size_t) 0); - if (r < 0) - return r; - - r = table_set_reverse(table, 0, true); - if (r < 0) - return r; - - for (UnitTimes *u = times; u->has_data; u++) { - if (u->time <= 0) - continue; - - r = table_add_many(table, - TABLE_TIMESPAN_MSEC, u->time, - TABLE_STRING, u->name); - if (r < 0) - return table_log_add_error(r); - } - - pager_open(arg_pager_flags); - - return table_print(table, NULL); -} - -static int analyze_time(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_free_ char *buf = NULL; - int r; - - r = acquire_bus(&bus, NULL); - if (r < 0) - return bus_log_connect_error(r, arg_transport); - - r = pretty_boot_time(bus, &buf); - if (r < 0) - return r; - - puts(buf); - return 0; -} - -static int graph_one_property( - sd_bus *bus, - const UnitInfo *u, - const char *prop, - const char *color, - char *patterns[], - char *from_patterns[], - char *to_patterns[]) { - - _cleanup_strv_free_ char **units = NULL; - char **unit; - int r; - bool match_patterns; - - assert(u); - assert(prop); - assert(color); - - match_patterns = strv_fnmatch(patterns, u->id); - - if (!strv_isempty(from_patterns) && !match_patterns && !strv_fnmatch(from_patterns, u->id)) - return 0; - - r = bus_get_unit_property_strv(bus, u->unit_path, prop, &units); - if (r < 0) - return r; - - STRV_FOREACH(unit, units) { - bool match_patterns2; - - match_patterns2 = strv_fnmatch(patterns, *unit); - - if (!strv_isempty(to_patterns) && !match_patterns2 && !strv_fnmatch(to_patterns, *unit)) - continue; - - if (!strv_isempty(patterns) && !match_patterns && !match_patterns2) - continue; - - printf("\t\"%s\"->\"%s\" [color=\"%s\"];\n", u->id, *unit, color); - } - - return 0; -} - -static int graph_one(sd_bus *bus, const UnitInfo *u, char *patterns[], char *from_patterns[], char *to_patterns[]) { - int r; - - assert(bus); - assert(u); - - if (IN_SET(arg_dot, DEP_ORDER, DEP_ALL)) { - r = graph_one_property(bus, u, "After", "green", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - } - - if (IN_SET(arg_dot, DEP_REQUIRE, DEP_ALL)) { - r = graph_one_property(bus, u, "Requires", "black", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - r = graph_one_property(bus, u, "Requisite", "darkblue", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - r = graph_one_property(bus, u, "Wants", "grey66", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - r = graph_one_property(bus, u, "Conflicts", "red", patterns, from_patterns, to_patterns); - if (r < 0) - return r; - } - - return 0; -} - -static int expand_patterns(sd_bus *bus, char **patterns, char ***ret) { - _cleanup_strv_free_ char **expanded_patterns = NULL; - char **pattern; - int r; - - STRV_FOREACH(pattern, patterns) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_free_ char *unit = NULL, *unit_id = NULL; - - if (strv_extend(&expanded_patterns, *pattern) < 0) - return log_oom(); - - if (string_is_glob(*pattern)) - continue; - - unit = unit_dbus_path_from_name(*pattern); - if (!unit) - return log_oom(); - - r = sd_bus_get_property_string( - bus, - "org.freedesktop.systemd1", - unit, - "org.freedesktop.systemd1.Unit", - "Id", - &error, - &unit_id); - if (r < 0) - return log_error_errno(r, "Failed to get ID: %s", bus_error_message(&error, r)); - - if (!streq(*pattern, unit_id)) { - if (strv_extend(&expanded_patterns, unit_id) < 0) - return log_oom(); - } - } - - *ret = TAKE_PTR(expanded_patterns); /* do not free */ - - return 0; -} - -static int dot(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_strv_free_ char **expanded_patterns = NULL; - _cleanup_strv_free_ char **expanded_from_patterns = NULL; - _cleanup_strv_free_ char **expanded_to_patterns = NULL; - int r; - UnitInfo u; - - r = acquire_bus(&bus, NULL); - if (r < 0) - return bus_log_connect_error(r, arg_transport); - - r = expand_patterns(bus, strv_skip(argv, 1), &expanded_patterns); - if (r < 0) - return r; - - r = expand_patterns(bus, arg_dot_from_patterns, &expanded_from_patterns); - if (r < 0) - return r; - - r = expand_patterns(bus, arg_dot_to_patterns, &expanded_to_patterns); - if (r < 0) - return r; - - r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL); - if (r < 0) - log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r)); - - r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)"); - if (r < 0) - return bus_log_parse_error(r); - - printf("digraph systemd {\n"); - - while ((r = bus_parse_unit_info(reply, &u)) > 0) { - - r = graph_one(bus, &u, expanded_patterns, expanded_from_patterns, expanded_to_patterns); - if (r < 0) - return r; - } - if (r < 0) - return bus_log_parse_error(r); - - printf("}\n"); - - log_info(" Color legend: black = Requires\n" - " dark blue = Requisite\n" - " dark grey = Wants\n" - " red = Conflicts\n" - " green = After\n"); - - if (on_tty() && !arg_quiet) - log_notice("-- You probably want to process this output with graphviz' dot tool.\n" - "-- Try a shell pipeline like 'systemd-analyze dot | dot -Tsvg > systemd.svg'!\n"); - - return 0; -} - -static int dump_fallback(sd_bus *bus) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - const char *text = NULL; - int r; - - assert(bus); - - r = bus_call_method(bus, bus_systemd_mgr, "Dump", &error, &reply, NULL); - if (r < 0) - return log_error_errno(r, "Failed to issue method call Dump: %s", bus_error_message(&error, r)); - - r = sd_bus_message_read(reply, "s", &text); - if (r < 0) - return bus_log_parse_error(r); - - fputs(text, stdout); - return 0; -} - -static int dump(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int fd = -1; - int r; - - r = acquire_bus(&bus, NULL); - if (r < 0) - return bus_log_connect_error(r, arg_transport); - - pager_open(arg_pager_flags); - - if (!sd_bus_can_send(bus, SD_BUS_TYPE_UNIX_FD)) - return dump_fallback(bus); - - r = bus_call_method(bus, bus_systemd_mgr, "DumpByFileDescriptor", &error, &reply, NULL); - if (r < 0) { - /* fall back to Dump if DumpByFileDescriptor is not supported */ - if (!IN_SET(r, -EACCES, -EBADR)) - return log_error_errno(r, "Failed to issue method call DumpByFileDescriptor: %s", - bus_error_message(&error, r)); - - return dump_fallback(bus); - } - - r = sd_bus_message_read(reply, "h", &fd); - if (r < 0) - return bus_log_parse_error(r); - - fflush(stdout); - return copy_bytes(fd, STDOUT_FILENO, UINT64_MAX, 0); -} - -static int cat_config(int argc, char *argv[], void *userdata) { - char **arg, **list; - int r; - - pager_open(arg_pager_flags); - - list = strv_skip(argv, 1); - STRV_FOREACH(arg, list) { - const char *t = NULL; - - if (arg != list) - print_separator(); - - if (path_is_absolute(*arg)) { - const char *dir; - - NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) { - t = path_startswith(*arg, dir); - if (t) - break; - } - - if (!t) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Path %s does not start with any known prefix.", *arg); - } else - t = *arg; - - r = conf_files_cat(arg_root, t); - if (r < 0) - return r; - } - - return 0; -} - -static int verb_log_control(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int r; - - assert(IN_SET(argc, 1, 2)); - - r = acquire_bus(&bus, NULL); - if (r < 0) - return bus_log_connect_error(r, arg_transport); - - return verb_log_control_common(bus, "org.freedesktop.systemd1", argv[0], argc == 2 ? argv[1] : NULL); -} - -static bool strv_fnmatch_strv_or_empty(char* const* patterns, char **strv, int flags) { - char **s; - STRV_FOREACH(s, strv) - if (strv_fnmatch_or_empty(patterns, *s, flags)) - return true; - - return false; -} - -static int do_unit_files(int argc, char *argv[], void *userdata) { - _cleanup_(lookup_paths_free) LookupPaths lp = {}; - _cleanup_hashmap_free_ Hashmap *unit_ids = NULL; - _cleanup_hashmap_free_ Hashmap *unit_names = NULL; - char **patterns = strv_skip(argv, 1); - const char *k, *dst; - char **v; - int r; - - r = lookup_paths_init(&lp, arg_scope, 0, NULL); - if (r < 0) - return log_error_errno(r, "lookup_paths_init() failed: %m"); - - r = unit_file_build_name_map(&lp, NULL, &unit_ids, &unit_names, NULL); - if (r < 0) - return log_error_errno(r, "unit_file_build_name_map() failed: %m"); - - HASHMAP_FOREACH_KEY(dst, k, unit_ids) { - if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) && - !strv_fnmatch_or_empty(patterns, dst, FNM_NOESCAPE)) - continue; - - printf("ids: %s → %s\n", k, dst); - } - - HASHMAP_FOREACH_KEY(v, k, unit_names) { - if (!strv_fnmatch_or_empty(patterns, k, FNM_NOESCAPE) && - !strv_fnmatch_strv_or_empty(patterns, v, FNM_NOESCAPE)) - continue; - - _cleanup_free_ char *j = strv_join(v, ", "); - printf("aliases: %s ← %s\n", k, j); - } - - return 0; -} - -static int dump_unit_paths(int argc, char *argv[], void *userdata) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; - int r; - char **p; - - r = lookup_paths_init(&paths, arg_scope, 0, NULL); - if (r < 0) - return log_error_errno(r, "lookup_paths_init() failed: %m"); - - STRV_FOREACH(p, paths.search_path) - puts(*p); - - return 0; -} - -static int dump_exit_status(int argc, char *argv[], void *userdata) { - _cleanup_(table_unrefp) Table *table = NULL; - int r; - - table = table_new("name", "status", "class"); - if (!table) - return log_oom(); - - r = table_set_align_percent(table, table_get_cell(table, 0, 1), 100); - if (r < 0) - return log_error_errno(r, "Failed to right-align status: %m"); - - if (strv_isempty(strv_skip(argv, 1))) - for (size_t i = 0; i < ELEMENTSOF(exit_status_mappings); i++) { - if (!exit_status_mappings[i].name) - continue; - - r = table_add_many(table, - TABLE_STRING, exit_status_mappings[i].name, - TABLE_INT, (int) i, - TABLE_STRING, exit_status_class(i)); - if (r < 0) - return table_log_add_error(r); - } - else - for (int i = 1; i < argc; i++) { - int status; - - status = exit_status_from_string(argv[i]); - if (status < 0) - return log_error_errno(status, "Invalid exit status \"%s\".", argv[i]); - - assert(status >= 0 && (size_t) status < ELEMENTSOF(exit_status_mappings)); - r = table_add_many(table, - TABLE_STRING, exit_status_mappings[status].name ?: "-", - TABLE_INT, status, - TABLE_STRING, exit_status_class(status) ?: "-"); - if (r < 0) - return table_log_add_error(r); - } - - pager_open(arg_pager_flags); - - return table_print(table, NULL); -} - -static int dump_capabilities(int argc, char *argv[], void *userdata) { - _cleanup_(table_unrefp) Table *table = NULL; - unsigned last_cap; - int r; - - table = table_new("name", "number"); - if (!table) - return log_oom(); - - (void) table_set_align_percent(table, table_get_cell(table, 0, 1), 100); - - /* Determine the maximum of the last cap known by the kernel and by us */ - last_cap = MAX((unsigned) CAP_LAST_CAP, cap_last_cap()); - - if (strv_isempty(strv_skip(argv, 1))) - for (unsigned c = 0; c <= last_cap; c++) { - r = table_add_many(table, - TABLE_STRING, capability_to_name(c) ?: "cap_???", - TABLE_UINT, c); - if (r < 0) - return table_log_add_error(r); - } - else { - for (int i = 1; i < argc; i++) { - int c; - - c = capability_from_name(argv[i]); - if (c < 0 || (unsigned) c > last_cap) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Capability \"%s\" not known.", argv[i]); - - r = table_add_many(table, - TABLE_STRING, capability_to_name(c) ?: "cap_???", - TABLE_UINT, (unsigned) c); - if (r < 0) - return table_log_add_error(r); - } - - (void) table_set_sort(table, (size_t) 1); - } - - pager_open(arg_pager_flags); - - return table_print(table, NULL); -} - -#if HAVE_SECCOMP - -static int load_kernel_syscalls(Set **ret) { - _cleanup_set_free_ Set *syscalls = NULL; - _cleanup_fclose_ FILE *f = NULL; - int r; - - /* Let's read the available system calls from the list of available tracing events. Slightly dirty, - * but good enough for analysis purposes. */ - - f = fopen("/sys/kernel/tracing/available_events", "re"); - if (!f) { - /* We tried the non-debugfs mount point and that didn't work. If it wasn't mounted, maybe the - * old debugfs mount point works? */ - f = fopen("/sys/kernel/debug/tracing/available_events", "re"); - if (!f) - return log_full_errno(IN_SET(errno, EPERM, EACCES, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno, - "Can't read open tracefs' available_events file: %m"); - } - - for (;;) { - _cleanup_free_ char *line = NULL; - const char *e; - - r = read_line(f, LONG_LINE_MAX, &line); - if (r < 0) - return log_error_errno(r, "Failed to read system call list: %m"); - if (r == 0) - break; - - e = startswith(line, "syscalls:sys_enter_"); - if (!e) - continue; - - /* These are named differently inside the kernel than their external name for historical - * reasons. Let's hide them here. */ - if (STR_IN_SET(e, "newuname", "newfstat", "newstat", "newlstat", "sysctl")) - continue; - - r = set_put_strdup(&syscalls, e); - if (r < 0) - return log_error_errno(r, "Failed to add system call to list: %m"); - } - - *ret = TAKE_PTR(syscalls); - return 0; -} - -static void syscall_set_remove(Set *s, const SyscallFilterSet *set) { - const char *syscall; - - if (!set) - return; - - NULSTR_FOREACH(syscall, set->value) { - if (syscall[0] == '@') - continue; - - free(set_remove(s, syscall)); - } -} - -static void dump_syscall_filter(const SyscallFilterSet *set) { - const char *syscall; - - printf("%s%s%s\n" - " # %s\n", - ansi_highlight(), - set->name, - ansi_normal(), - set->help); - - NULSTR_FOREACH(syscall, set->value) - printf(" %s%s%s\n", syscall[0] == '@' ? ansi_underline() : "", syscall, ansi_normal()); -} - -static int dump_syscall_filters(int argc, char *argv[], void *userdata) { - bool first = true; - - pager_open(arg_pager_flags); - - if (strv_isempty(strv_skip(argv, 1))) { - _cleanup_set_free_ Set *kernel = NULL, *known = NULL; - const char *sys; - int k = 0; /* explicit initialization to appease gcc */ - - NULSTR_FOREACH(sys, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value) - if (set_put_strdup(&known, sys) < 0) - return log_oom(); - - if (!arg_quiet) - k = load_kernel_syscalls(&kernel); - - for (int i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) { - const SyscallFilterSet *set = syscall_filter_sets + i; - if (!first) - puts(""); - - dump_syscall_filter(set); - syscall_set_remove(kernel, set); - if (i != SYSCALL_FILTER_SET_KNOWN) - syscall_set_remove(known, set); - first = false; - } - - if (arg_quiet) /* Let's not show the extra stuff in quiet mode */ - return 0; - - if (!set_isempty(known)) { - _cleanup_free_ char **l = NULL; - char **syscall; - - printf("\n" - "# %sUngrouped System Calls%s (known but not included in any of the groups except @known):\n", - ansi_highlight(), ansi_normal()); - - l = set_get_strv(known); - if (!l) - return log_oom(); - - strv_sort(l); - - STRV_FOREACH(syscall, l) - printf("# %s\n", *syscall); - } - - if (k < 0) { - fputc('\n', stdout); - fflush(stdout); - if (!arg_quiet) - log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m"); - } else if (!set_isempty(kernel)) { - _cleanup_free_ char **l = NULL; - char **syscall; - - printf("\n" - "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n", - ansi_highlight(), ansi_normal()); - - l = set_get_strv(kernel); - if (!l) - return log_oom(); - - strv_sort(l); - - STRV_FOREACH(syscall, l) - printf("# %s\n", *syscall); - } - } else { - char **name; - - STRV_FOREACH(name, strv_skip(argv, 1)) { - const SyscallFilterSet *set; - - if (!first) - puts(""); - - set = syscall_filter_set_find(*name); - if (!set) { - /* make sure the error appears below normal output */ - fflush(stdout); - - return log_error_errno(SYNTHETIC_ERRNO(ENOENT), - "Filter set \"%s\" not found.", *name); - } - - dump_syscall_filter(set); - first = false; - } - } - - return 0; -} - -#else -static int dump_syscall_filters(int argc, char *argv[], void *userdata) { - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with syscall filters, sorry."); -} -#endif - -static int load_available_kernel_filesystems(Set **ret) { - _cleanup_set_free_ Set *filesystems = NULL; - _cleanup_free_ char *t = NULL; - int r; - - assert(ret); - - /* Let's read the available filesystems */ - - r = read_virtual_file("/proc/filesystems", SIZE_MAX, &t, NULL); - if (r < 0) - return r; - - for (int i = 0;;) { - _cleanup_free_ char *line = NULL; - const char *p; - - r = string_extract_line(t, i++, &line); - if (r < 0) - return log_oom(); - if (r == 0) - break; - - if (!line) - line = t; - - p = strchr(line, '\t'); - if (!p) - continue; - - p += strspn(p, WHITESPACE); - - r = set_put_strdup(&filesystems, p); - if (r < 0) - return log_error_errno(r, "Failed to add filesystem to list: %m"); - } - - *ret = TAKE_PTR(filesystems); - return 0; -} - -static void filesystem_set_remove(Set *s, const FilesystemSet *set) { - const char *filesystem; - - NULSTR_FOREACH(filesystem, set->value) { - if (filesystem[0] == '@') - continue; - - free(set_remove(s, filesystem)); - } -} - -static void dump_filesystem_set(const FilesystemSet *set) { - const char *filesystem; - int r; - - if (!set) - return; - - printf("%s%s%s\n" - " # %s\n", - ansi_highlight(), - set->name, - ansi_normal(), - set->help); - - NULSTR_FOREACH(filesystem, set->value) { - const statfs_f_type_t *magic; - - if (filesystem[0] == '@') { - printf(" %s%s%s\n", ansi_underline(), filesystem, ansi_normal()); - continue; - } - - r = fs_type_from_string(filesystem, &magic); - assert_se(r >= 0); - - printf(" %s", filesystem); - - for (size_t i = 0; magic[i] != 0; i++) { - const char *primary; - if (i == 0) - printf(" %s(magic: ", ansi_grey()); - else - printf(", "); - - printf("0x%llx", (unsigned long long) magic[i]); - - primary = fs_type_to_string(magic[i]); - if (primary && !streq(primary, filesystem)) - printf("[%s]", primary); - - if (magic[i+1] == 0) - printf(")%s", ansi_normal()); - } - - printf("\n"); - } -} - -static int dump_filesystems(int argc, char *argv[], void *userdata) { - bool first = true; - -#if ! HAVE_LIBBPF - return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with libbpf support, sorry."); -#endif - - pager_open(arg_pager_flags); - - if (strv_isempty(strv_skip(argv, 1))) { - _cleanup_set_free_ Set *kernel = NULL, *known = NULL; - const char *fs; - int k; - - NULSTR_FOREACH(fs, filesystem_sets[FILESYSTEM_SET_KNOWN].value) - if (set_put_strdup(&known, fs) < 0) - return log_oom(); - - k = load_available_kernel_filesystems(&kernel); - - for (FilesystemGroups i = 0; i < _FILESYSTEM_SET_MAX; i++) { - const FilesystemSet *set = filesystem_sets + i; - if (!first) - puts(""); - - dump_filesystem_set(set); - filesystem_set_remove(kernel, set); - if (i != FILESYSTEM_SET_KNOWN) - filesystem_set_remove(known, set); - first = false; - } - - if (arg_quiet) /* Let's not show the extra stuff in quiet mode */ - return 0; - - if (!set_isempty(known)) { - _cleanup_free_ char **l = NULL; - char **filesystem; - - printf("\n" - "# %sUngrouped filesystems%s (known but not included in any of the groups except @known):\n", - ansi_highlight(), ansi_normal()); - - l = set_get_strv(known); - if (!l) - return log_oom(); - - strv_sort(l); - - STRV_FOREACH(filesystem, l) { - const statfs_f_type_t *magic; - bool is_primary = false; - - assert_se(fs_type_from_string(*filesystem, &magic) >= 0); - - for (size_t i = 0; magic[i] != 0; i++) { - const char *primary; - - primary = fs_type_to_string(magic[i]); - assert(primary); - - if (streq(primary, *filesystem)) - is_primary = true; - } - - if (!is_primary) { - log_debug("Skipping ungrouped file system '%s', because it's an alias for another one.", *filesystem); - continue; - } - - printf("# %s\n", *filesystem); - } - } - - if (k < 0) { - fputc('\n', stdout); - fflush(stdout); - log_notice_errno(k, "# Not showing unlisted filesystems, couldn't retrieve kernel filesystem list: %m"); - } else if (!set_isempty(kernel)) { - _cleanup_free_ char **l = NULL; - char **filesystem; - - printf("\n" - "# %sUnlisted filesystems%s (available to the local kernel, but not included in any of the groups listed above):\n", - ansi_highlight(), ansi_normal()); - - l = set_get_strv(kernel); - if (!l) - return log_oom(); - - strv_sort(l); - - STRV_FOREACH(filesystem, l) - printf("# %s\n", *filesystem); - } - } else { - char **name; - - STRV_FOREACH(name, strv_skip(argv, 1)) { - const FilesystemSet *set; - - if (!first) - puts(""); - - set = filesystem_set_find(*name); - if (!set) { - /* make sure the error appears below normal output */ - fflush(stdout); - - return log_error_errno(SYNTHETIC_ERRNO(ENOENT), - "Filesystem set \"%s\" not found.", *name); - } - - dump_filesystem_set(set); - first = false; - } - } - - return 0; -} - -static void parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan) { +void time_parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan) { if (calendar && calendar_spec_from_string(p, NULL) >= 0) log_notice("Hint: this expression is a valid calendar specification. " "Use 'systemd-analyze calendar \"%s\"' instead?", p); @@ -2040,406 +163,6 @@ static void parsing_hint(const char *p, bool calendar, bool timestamp, bool time "Use 'systemd-analyze timespan \"%s\"' instead?", p); } -static int dump_timespan(int argc, char *argv[], void *userdata) { - char **input_timespan; - - STRV_FOREACH(input_timespan, strv_skip(argv, 1)) { - _cleanup_(table_unrefp) Table *table = NULL; - usec_t output_usecs; - TableCell *cell; - int r; - - r = parse_time(*input_timespan, &output_usecs, USEC_PER_SEC); - if (r < 0) { - log_error_errno(r, "Failed to parse time span '%s': %m", *input_timespan); - parsing_hint(*input_timespan, true, true, false); - return r; - } - - table = table_new("name", "value"); - if (!table) - return log_oom(); - - table_set_header(table, false); - - assert_se(cell = table_get_cell(table, 0, 0)); - r = table_set_ellipsize_percent(table, cell, 100); - if (r < 0) - return r; - - r = table_set_align_percent(table, cell, 100); - if (r < 0) - return r; - - assert_se(cell = table_get_cell(table, 0, 1)); - r = table_set_ellipsize_percent(table, cell, 100); - if (r < 0) - return r; - - r = table_add_many(table, - TABLE_STRING, "Original:", - TABLE_STRING, *input_timespan); - if (r < 0) - return table_log_add_error(r); - - r = table_add_cell_stringf(table, NULL, "%ss:", special_glyph(SPECIAL_GLYPH_MU)); - if (r < 0) - return table_log_add_error(r); - - r = table_add_many(table, - TABLE_UINT64, output_usecs, - TABLE_STRING, "Human:", - TABLE_TIMESPAN, output_usecs, - TABLE_SET_COLOR, ansi_highlight()); - if (r < 0) - return table_log_add_error(r); - - r = table_print(table, NULL); - if (r < 0) - return r; - - if (input_timespan[1]) - putchar('\n'); - } - - return 0; -} - -static int test_timestamp_one(const char *p) { - _cleanup_(table_unrefp) Table *table = NULL; - TableCell *cell; - usec_t usec; - int r; - - r = parse_timestamp(p, &usec); - if (r < 0) { - log_error_errno(r, "Failed to parse \"%s\": %m", p); - parsing_hint(p, true, false, true); - return r; - } - - table = table_new("name", "value"); - if (!table) - return log_oom(); - - table_set_header(table, false); - - assert_se(cell = table_get_cell(table, 0, 0)); - r = table_set_ellipsize_percent(table, cell, 100); - if (r < 0) - return r; - - r = table_set_align_percent(table, cell, 100); - if (r < 0) - return r; - - assert_se(cell = table_get_cell(table, 0, 1)); - r = table_set_ellipsize_percent(table, cell, 100); - if (r < 0) - return r; - - r = table_add_many(table, - TABLE_STRING, "Original form:", - TABLE_STRING, p, - TABLE_STRING, "Normalized form:", - TABLE_TIMESTAMP, usec, - TABLE_SET_COLOR, ansi_highlight_blue()); - if (r < 0) - return table_log_add_error(r); - - if (!in_utc_timezone()) { - r = table_add_many(table, - TABLE_STRING, "(in UTC):", - TABLE_TIMESTAMP_UTC, usec); - if (r < 0) - return table_log_add_error(r); - } - - r = table_add_cell(table, NULL, TABLE_STRING, "UNIX seconds:"); - if (r < 0) - return table_log_add_error(r); - - if (usec % USEC_PER_SEC == 0) - r = table_add_cell_stringf(table, NULL, "@%"PRI_USEC, - usec / USEC_PER_SEC); - else - r = table_add_cell_stringf(table, NULL, "@%"PRI_USEC".%06"PRI_USEC"", - usec / USEC_PER_SEC, - usec % USEC_PER_SEC); - if (r < 0) - return r; - - r = table_add_many(table, - TABLE_STRING, "From now:", - TABLE_TIMESTAMP_RELATIVE, usec); - if (r < 0) - return table_log_add_error(r); - - return table_print(table, NULL); -} - -static int test_timestamp(int argc, char *argv[], void *userdata) { - int ret = 0, r; - char **p; - - STRV_FOREACH(p, strv_skip(argv, 1)) { - r = test_timestamp_one(*p); - if (ret == 0 && r < 0) - ret = r; - - if (*(p + 1)) - putchar('\n'); - } - - return ret; -} - -static int test_calendar_one(usec_t n, const char *p) { - _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL; - _cleanup_(table_unrefp) Table *table = NULL; - _cleanup_free_ char *t = NULL; - TableCell *cell; - int r; - - r = calendar_spec_from_string(p, &spec); - if (r < 0) { - log_error_errno(r, "Failed to parse calendar specification '%s': %m", p); - parsing_hint(p, false, true, true); - return r; - } - - r = calendar_spec_to_string(spec, &t); - if (r < 0) - return log_error_errno(r, "Failed to format calendar specification '%s': %m", p); - - table = table_new("name", "value"); - if (!table) - return log_oom(); - - table_set_header(table, false); - - assert_se(cell = table_get_cell(table, 0, 0)); - r = table_set_ellipsize_percent(table, cell, 100); - if (r < 0) - return r; - - r = table_set_align_percent(table, cell, 100); - if (r < 0) - return r; - - assert_se(cell = table_get_cell(table, 0, 1)); - r = table_set_ellipsize_percent(table, cell, 100); - if (r < 0) - return r; - - if (!streq(t, p)) { - r = table_add_many(table, - TABLE_STRING, "Original form:", - TABLE_STRING, p); - if (r < 0) - return table_log_add_error(r); - } - - r = table_add_many(table, - TABLE_STRING, "Normalized form:", - TABLE_STRING, t); - if (r < 0) - return table_log_add_error(r); - - for (unsigned i = 0; i < arg_iterations; i++) { - usec_t next; - - r = calendar_spec_next_usec(spec, n, &next); - if (r == -ENOENT) { - if (i == 0) { - r = table_add_many(table, - TABLE_STRING, "Next elapse:", - TABLE_STRING, "never", - TABLE_SET_COLOR, ansi_highlight_yellow()); - if (r < 0) - return table_log_add_error(r); - } - break; - } - if (r < 0) - return log_error_errno(r, "Failed to determine next elapse for '%s': %m", p); - - if (i == 0) { - r = table_add_many(table, - TABLE_STRING, "Next elapse:", - TABLE_TIMESTAMP, next, - TABLE_SET_COLOR, ansi_highlight_blue()); - if (r < 0) - return table_log_add_error(r); - } else { - int k = DECIMAL_STR_WIDTH(i + 1); - - if (k < 8) - k = 8 - k; - else - k = 0; - - r = table_add_cell_stringf(table, NULL, "Iter. #%u:", i+1); - if (r < 0) - return table_log_add_error(r); - - r = table_add_many(table, - TABLE_TIMESTAMP, next, - TABLE_SET_COLOR, ansi_highlight_blue()); - if (r < 0) - return table_log_add_error(r); - } - - if (!in_utc_timezone()) { - r = table_add_many(table, - TABLE_STRING, "(in UTC):", - TABLE_TIMESTAMP_UTC, next); - if (r < 0) - return table_log_add_error(r); - } - - r = table_add_many(table, - TABLE_STRING, "From now:", - TABLE_TIMESTAMP_RELATIVE, next); - if (r < 0) - return table_log_add_error(r); - - n = next; - } - - return table_print(table, NULL); -} - -static int test_calendar(int argc, char *argv[], void *userdata) { - int ret = 0, r; - char **p; - usec_t n; - - if (arg_base_time != USEC_INFINITY) - n = arg_base_time; - else - n = now(CLOCK_REALTIME); /* We want to use the same "base" for all expressions */ - - STRV_FOREACH(p, strv_skip(argv, 1)) { - r = test_calendar_one(n, *p); - if (ret == 0 && r < 0) - ret = r; - - if (*(p + 1)) - putchar('\n'); - } - - return ret; -} - -static int service_watchdogs(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - int b, r; - - assert(IN_SET(argc, 1, 2)); - assert(argv); - - r = acquire_bus(&bus, NULL); - if (r < 0) - return bus_log_connect_error(r, arg_transport); - - if (argc == 1) { - /* get ServiceWatchdogs */ - r = bus_get_property_trivial(bus, bus_systemd_mgr, "ServiceWatchdogs", &error, 'b', &b); - if (r < 0) - return log_error_errno(r, "Failed to get service-watchdog state: %s", bus_error_message(&error, r)); - - printf("%s\n", yes_no(!!b)); - - } else { - /* set ServiceWatchdogs */ - b = parse_boolean(argv[1]); - if (b < 0) - return log_error_errno(b, "Failed to parse service-watchdogs argument: %m"); - - r = bus_set_property(bus, bus_systemd_mgr, "ServiceWatchdogs", &error, "b", b); - if (r < 0) - return log_error_errno(r, "Failed to set service-watchdog state: %s", bus_error_message(&error, r)); - } - - return 0; -} - -static int do_condition(int argc, char *argv[], void *userdata) { - return verify_conditions(strv_skip(argv, 1), arg_scope, arg_unit, arg_root); -} - -static int do_verify(int argc, char *argv[], void *userdata) { - _cleanup_strv_free_ char **filenames = NULL; - _cleanup_(rm_rf_physical_and_freep) char *tempdir = NULL; - int r; - - r = mkdtemp_malloc("/tmp/systemd-analyze-XXXXXX", &tempdir); - if (r < 0) - return log_error_errno(r, "Failed to setup working directory: %m"); - - r = process_aliases(argv, tempdir, &filenames); - if (r < 0) - return log_error_errno(r, "Couldn't process aliases: %m"); - - return verify_units(filenames, arg_scope, arg_man, arg_generators, arg_recursive_errors, arg_root); -} - -static int do_security(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - _cleanup_(json_variant_unrefp) JsonVariant *policy = NULL; - int r; - unsigned line, column; - - if (!arg_offline) { - r = acquire_bus(&bus, NULL); - if (r < 0) - return bus_log_connect_error(r, arg_transport); - } - - pager_open(arg_pager_flags); - - if (arg_security_policy) { - r = json_parse_file(/*f=*/ NULL, arg_security_policy, /*flags=*/ 0, &policy, &line, &column); - if (r < 0) - return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column); - } else { - _cleanup_fclose_ FILE *f = NULL; - _cleanup_free_ char *pp = NULL; - - r = search_and_fopen_nulstr("systemd-analyze-security.policy", "re", /*root=*/ NULL, CONF_PATHS_NULSTR("systemd"), &f, &pp); - if (r < 0 && r != -ENOENT) - return r; - - if (f) { - r = json_parse_file(f, pp, /*flags=*/ 0, &policy, &line, &column); - if (r < 0) - return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column); - } - } - - return analyze_security(bus, - strv_skip(argv, 1), - policy, - arg_scope, - arg_man, - arg_generators, - arg_offline, - arg_threshold, - arg_root, - arg_profile, - arg_json_format_flags, - arg_pager_flags, - /*flags=*/ 0); -} - -static int do_elf_inspection(int argc, char *argv[], void *userdata) { - pager_open(arg_pager_flags); - - return analyze_elf(strv_skip(argv, 1), arg_json_format_flags); -} - static int help(int argc, char *argv[], void *userdata) { _cleanup_free_ char *link = NULL, *dot_link = NULL; int r; @@ -2628,15 +351,15 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_SYSTEM: - arg_scope = UNIT_FILE_SYSTEM; + arg_scope = LOOKUP_SCOPE_SYSTEM; break; case ARG_USER: - arg_scope = UNIT_FILE_USER; + arg_scope = LOOKUP_SCOPE_USER; break; case ARG_GLOBAL: - arg_scope = UNIT_FILE_GLOBAL; + arg_scope = LOOKUP_SCOPE_GLOBAL; break; case ARG_ORDER: @@ -2771,18 +494,18 @@ static int parse_argv(int argc, char *argv[]) { if (arg_json_format_flags != JSON_FORMAT_OFF && !STRPTR_IN_SET(argv[optind], "security", "inspect-elf")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Option --json= is only supported for security right now."); + "Option --json= is only supported for security and inspect-elf right now."); if (arg_threshold != 100 && !streq_ptr(argv[optind], "security")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --threshold= is only supported for security right now."); - if (arg_scope == UNIT_FILE_GLOBAL && + if (arg_scope == LOOKUP_SCOPE_GLOBAL && !STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --global only makes sense with verbs dot, unit-paths, verify."); - if (streq_ptr(argv[optind], "cat-config") && arg_scope == UNIT_FILE_USER) + if (streq_ptr(argv[optind], "cat-config") && arg_scope == LOOKUP_SCOPE_USER) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --user is not supported for cat-config right now."); @@ -2818,34 +541,35 @@ static int run(int argc, char *argv[]) { static const Verb verbs[] = { { "help", VERB_ANY, VERB_ANY, 0, help }, - { "time", VERB_ANY, 1, VERB_DEFAULT, analyze_time }, - { "blame", VERB_ANY, 1, 0, analyze_blame }, - { "critical-chain", VERB_ANY, VERB_ANY, 0, analyze_critical_chain }, - { "plot", VERB_ANY, 1, 0, analyze_plot }, - { "dot", VERB_ANY, VERB_ANY, 0, dot }, - /* The following seven verbs are deprecated */ + { "time", VERB_ANY, 1, VERB_DEFAULT, verb_time }, + { "blame", VERB_ANY, 1, 0, verb_blame }, + { "critical-chain", VERB_ANY, VERB_ANY, 0, verb_critical_chain }, + { "plot", VERB_ANY, 1, 0, verb_plot }, + { "dot", VERB_ANY, VERB_ANY, 0, verb_dot }, + /* ↓ The following seven verbs are deprecated, from here … ↓ */ { "log-level", VERB_ANY, 2, 0, verb_log_control }, { "log-target", VERB_ANY, 2, 0, verb_log_control }, { "set-log-level", 2, 2, 0, verb_log_control }, { "get-log-level", VERB_ANY, 1, 0, verb_log_control }, { "set-log-target", 2, 2, 0, verb_log_control }, { "get-log-target", VERB_ANY, 1, 0, verb_log_control }, - { "service-watchdogs", VERB_ANY, 2, 0, service_watchdogs }, - { "dump", VERB_ANY, 1, 0, dump }, - { "cat-config", 2, VERB_ANY, 0, cat_config }, - { "unit-files", VERB_ANY, VERB_ANY, 0, do_unit_files }, - { "unit-paths", 1, 1, 0, dump_unit_paths }, - { "exit-status", VERB_ANY, VERB_ANY, 0, dump_exit_status }, - { "syscall-filter", VERB_ANY, VERB_ANY, 0, dump_syscall_filters }, - { "capability", VERB_ANY, VERB_ANY, 0, dump_capabilities }, - { "filesystems", VERB_ANY, VERB_ANY, 0, dump_filesystems }, - { "condition", VERB_ANY, VERB_ANY, 0, do_condition }, - { "verify", 2, VERB_ANY, 0, do_verify }, - { "calendar", 2, VERB_ANY, 0, test_calendar }, - { "timestamp", 2, VERB_ANY, 0, test_timestamp }, - { "timespan", 2, VERB_ANY, 0, dump_timespan }, - { "security", VERB_ANY, VERB_ANY, 0, do_security }, - { "inspect-elf", 2, VERB_ANY, 0, do_elf_inspection }, + { "service-watchdogs", VERB_ANY, 2, 0, verb_service_watchdogs }, + /* ↑ … until here ↑ */ + { "dump", VERB_ANY, 1, 0, verb_dump }, + { "cat-config", 2, VERB_ANY, 0, verb_cat_config }, + { "unit-files", VERB_ANY, VERB_ANY, 0, verb_unit_files }, + { "unit-paths", 1, 1, 0, verb_unit_paths }, + { "exit-status", VERB_ANY, VERB_ANY, 0, verb_exit_status }, + { "syscall-filter", VERB_ANY, VERB_ANY, 0, verb_syscall_filters }, + { "capability", VERB_ANY, VERB_ANY, 0, verb_capabilities }, + { "filesystems", VERB_ANY, VERB_ANY, 0, verb_filesystems }, + { "condition", VERB_ANY, VERB_ANY, 0, verb_condition }, + { "verify", 2, VERB_ANY, 0, verb_verify }, + { "calendar", 2, VERB_ANY, 0, verb_calendar }, + { "timestamp", 2, VERB_ANY, 0, verb_timestamp }, + { "timespan", 2, VERB_ANY, 0, verb_timespan }, + { "security", VERB_ANY, VERB_ANY, 0, verb_security }, + { "inspect-elf", 2, VERB_ANY, 0, verb_elf_inspection }, {} }; diff --git a/src/analyze/analyze.h b/src/analyze/analyze.h new file mode 100644 index 000000000..da12058c4 --- /dev/null +++ b/src/analyze/analyze.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "analyze-verify-util.h" +#include "bus-util.h" +#include "json.h" +#include "pager.h" +#include "time-util.h" +#include "unit-file.h" + +typedef enum DotMode { + DEP_ALL, + DEP_ORDER, + DEP_REQUIRE, +} DotMode; + +extern DotMode arg_dot; +extern char **arg_dot_from_patterns, **arg_dot_to_patterns; +extern usec_t arg_fuzz; +extern PagerFlags arg_pager_flags; +extern BusTransport arg_transport; +extern const char *arg_host; +extern LookupScope arg_scope; +extern RecursiveErrors arg_recursive_errors; +extern bool arg_man; +extern bool arg_generators; +extern char *arg_root; +extern char *arg_security_policy; +extern bool arg_offline; +extern unsigned arg_threshold; +extern unsigned arg_iterations; +extern usec_t arg_base_time; +extern char *arg_unit; +extern JsonFormatFlags arg_json_format_flags; +extern bool arg_quiet; +extern char *arg_profile; + +int acquire_bus(sd_bus **bus, bool *use_full_bus); + +int bus_get_unit_property_strv(sd_bus *bus, const char *path, const char *property, char ***strv); + +void time_parsing_hint(const char *p, bool calendar, bool timestamp, bool timespan); diff --git a/src/analyze/meson.build b/src/analyze/meson.build index 2713c9d3e..f0cfbb195 100644 --- a/src/analyze/meson.build +++ b/src/analyze/meson.build @@ -1,20 +1,60 @@ # SPDX-License-Identifier: LGPL-2.1-or-later systemd_analyze_sources = files( - 'analyze.c', + 'analyze-blame.c', + 'analyze-blame.h', + 'analyze-calendar.c', + 'analyze-calendar.h', + 'analyze-capability.c', + 'analyze-capability.h', + 'analyze-cat-config.c', + 'analyze-cat-config.h', 'analyze-condition.c', 'analyze-condition.h', - 'analyze-elf.c', - 'analyze-elf.h', + 'analyze-critical-chain.c', + 'analyze-critical-chain.h', + 'analyze-dot.c', + 'analyze-dot.h', + 'analyze-dump.c', + 'analyze-dump.h', + 'analyze-exit-status.c', + 'analyze-exit-status.h', + 'analyze-filesystems.c', + 'analyze-filesystems.h', + 'analyze-inspect-elf.c', + 'analyze-inspect-elf.h', + 'analyze-log-control.c', + 'analyze-log-control.h', + 'analyze-plot.c', + 'analyze-plot.h', + 'analyze-security.c', + 'analyze-security.h', + 'analyze-service-watchdogs.c', + 'analyze-service-watchdogs.h', + 'analyze-syscall-filter.c', + 'analyze-syscall-filter.h', + 'analyze-time.c', + 'analyze-time.h', + 'analyze-time-data.c', + 'analyze-time-data.h', + 'analyze-timespan.c', + 'analyze-timespan.h', + 'analyze-timestamp.c', + 'analyze-timestamp.h', + 'analyze-unit-files.c', + 'analyze-unit-files.h', + 'analyze-unit-paths.c', + 'analyze-unit-paths.h', 'analyze-verify.c', 'analyze-verify.h', - 'analyze-security.c', - 'analyze-security.h') + 'analyze-verify-util.c', + 'analyze-verify-util.h', + 'analyze.c') tests += [ - [['src/analyze/test-verify.c', - 'src/analyze/analyze-verify.c', - 'src/analyze/analyze-verify.h'], + [files('test-verify.c', + 'analyze-verify-util.c', + 'analyze-verify-util.h'), [libcore, libshared], [], diff --git a/src/analyze/test-verify.c b/src/analyze/test-verify.c index eaf5e0b39..d37e54bca 100644 --- a/src/analyze/test-verify.c +++ b/src/analyze/test-verify.c @@ -1,8 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "analyze-verify.h" + +#include "analyze-verify-util.h" #include "tests.h" -static void test_verify_nonexistent(void) { +TEST(verify_nonexistent) { /* Negative cases */ assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/non/existent"}, NULL) == 0); assert_se(verify_executable(NULL, &(ExecCommand) {.path = (char*) "/non/existent"}, NULL) < 0); @@ -12,8 +13,4 @@ static void test_verify_nonexistent(void) { assert_se(verify_executable(NULL, &(ExecCommand) {.flags = EXEC_COMMAND_IGNORE_FAILURE, .path = (char*) "/bin/echo"}, NULL) == 0); } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_DEBUG); - - test_verify_nonexistent(); -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/ask-password/ask-password.c b/src/ask-password/ask-password.c index a100679af..093533182 100644 --- a/src/ask-password/ask-password.c +++ b/src/ask-password/ask-password.c @@ -223,7 +223,6 @@ static int parse_argv(int argc, char *argv[]) { static int run(int argc, char *argv[]) { _cleanup_strv_free_erase_ char **l = NULL; usec_t timeout; - char **p; int r; log_show_color(true); diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h index 65d517561..57f1b186e 100644 --- a/src/basic/alloc-util.h +++ b/src/basic/alloc-util.h @@ -13,6 +13,7 @@ #endif typedef void (*free_func_t)(void *p); +typedef void* (*mfree_func_t)(void *p); /* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than * proceeding and smashing the stack limits. Note that by default RLIMIT_STACK is 8M on Linux. */ diff --git a/src/basic/architecture.h b/src/basic/architecture.h index 0b91c69c0..cb4c79b84 100644 --- a/src/basic/architecture.h +++ b/src/basic/architecture.h @@ -199,9 +199,16 @@ int uname_architecture(void); # define LIB_ARCH_TUPLE "sh4a-linux-gnu" # endif #elif defined(__loongarch64) -# pragma message "Please update the Arch tuple of loongarch64 after psABI is stable" -# define native_architecture() ARCHITECTURE_LOONGARCH64 -# define LIB_ARCH_TUPLE "loongarch64-linux-gnu" +# define native_architecture() ARCHITECTURE_LOONGARCH64 +# if defined(__loongarch_double_float) +# define LIB_ARCH_TUPLE "loongarch64-linux-gnuf64" +# elif defined(__loongarch_single_float) +# define LIB_ARCH_TUPLE "loongarch64-linux-gnuf32" +# elif defined(__loongarch_soft_float) +# define LIB_ARCH_TUPLE "loongarch64-linux-gnusf" +# else +# error "Unrecognized loongarch architecture variant" +# endif #elif defined(__m68k__) # define native_architecture() ARCHITECTURE_M68K # define LIB_ARCH_TUPLE "m68k-linux-gnu" diff --git a/src/basic/build.c b/src/basic/build.c index e34d23ade..4a15f901d 100644 --- a/src/basic/build.c +++ b/src/basic/build.c @@ -160,6 +160,12 @@ const char* const systemd_features = " -QRENCODE" #endif +#if HAVE_TPM2 + " +TPM2" +#else + " -TPM2" +#endif + /* compressors */ #if HAVE_BZIP2 diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index a626ecf2e..95bf177a6 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -462,14 +462,11 @@ int cg_kill_recursive( } static const char *controller_to_dirname(const char *controller) { - const char *e; - assert(controller); - /* Converts a controller name to the directory name below - * /sys/fs/cgroup/ we want to mount it to. Effectively, this - * just cuts off the name= prefixed used for named - * hierarchies, if it is specified. */ + /* Converts a controller name to the directory name below /sys/fs/cgroup/ we want to mount it + * to. Effectively, this just cuts off the name= prefixed used for named hierarchies, if it is + * specified. */ if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { if (cg_hybrid_unified() > 0) @@ -478,18 +475,14 @@ static const char *controller_to_dirname(const char *controller) { controller = SYSTEMD_CGROUP_CONTROLLER_LEGACY; } - e = startswith(controller, "name="); - if (e) - return e; - - return controller; + return startswith(controller, "name=") ?: controller; } -static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **fs) { +static int join_path_legacy(const char *controller, const char *path, const char *suffix, char **ret) { const char *dn; char *t = NULL; - assert(fs); + assert(ret); assert(controller); dn = controller_to_dirname(controller); @@ -505,14 +498,14 @@ static int join_path_legacy(const char *controller, const char *path, const char if (!t) return -ENOMEM; - *fs = t; + *ret = t; return 0; } -static int join_path_unified(const char *path, const char *suffix, char **fs) { +static int join_path_unified(const char *path, const char *suffix, char **ret) { char *t; - assert(fs); + assert(ret); if (isempty(path) && isempty(suffix)) t = strdup("/sys/fs/cgroup"); @@ -525,34 +518,34 @@ static int join_path_unified(const char *path, const char *suffix, char **fs) { if (!t) return -ENOMEM; - *fs = t; + *ret = t; return 0; } -int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) { +int cg_get_path(const char *controller, const char *path, const char *suffix, char **ret) { int r; - assert(fs); + assert(ret); if (!controller) { char *t; - /* If no controller is specified, we return the path - * *below* the controllers, without any prefix. */ + /* If no controller is specified, we return the path *below* the controllers, without any + * prefix. */ - if (!path && !suffix) + if (isempty(path) && isempty(suffix)) return -EINVAL; - if (!suffix) + if (isempty(suffix)) t = strdup(path); - else if (!path) + else if (isempty(path)) t = strdup(suffix); else t = path_join(path, suffix); if (!t) return -ENOMEM; - *fs = path_simplify(t); + *ret = path_simplify(t); return 0; } @@ -563,13 +556,13 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch if (r < 0) return r; if (r > 0) - r = join_path_unified(path, suffix, fs); + r = join_path_unified(path, suffix, ret); else - r = join_path_legacy(controller, path, suffix, fs); + r = join_path_legacy(controller, path, suffix, ret); if (r < 0) return r; - path_simplify(*fs); + path_simplify(*ret); return 0; } @@ -1695,6 +1688,30 @@ int cg_slice_to_path(const char *unit, char **ret) { return 0; } +int cg_is_threaded(const char *controller, const char *path) { + _cleanup_free_ char *fs = NULL, *contents = NULL; + _cleanup_strv_free_ char **v = NULL; + int r; + + r = cg_get_path(controller, path, "cgroup.type", &fs); + if (r < 0) + return r; + + r = read_full_virtual_file(fs, &contents, NULL); + if (r == -ENOENT) + return false; /* Assume no. */ + if (r < 0) + return r; + + v = strv_split(contents, NULL); + if (!v) + return -ENOMEM; + + /* If the cgroup is in the threaded mode, it contains "threaded". + * If one of the parents or siblings is in the threaded mode, it may contain "invalid". */ + return strv_contains(v, "threaded") || strv_contains(v, "invalid"); +} + int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value) { _cleanup_free_ char *p = NULL; int r; diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 21275e9ea..4c413a8d1 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -127,6 +127,20 @@ static inline bool CGROUP_CPU_SHARES_IS_OK(uint64_t x) { (x >= CGROUP_CPU_SHARES_MIN && x <= CGROUP_CPU_SHARES_MAX); } +/* Special values for the special {blkio,io}.bfq.weight attribute */ +#define CGROUP_BFQ_WEIGHT_INVALID UINT64_MAX +#define CGROUP_BFQ_WEIGHT_MIN UINT64_C(1) +#define CGROUP_BFQ_WEIGHT_MAX UINT64_C(1000) +#define CGROUP_BFQ_WEIGHT_DEFAULT UINT64_C(100) + +/* Convert the normal io.weight value to io.bfq.weight */ +static inline uint64_t BFQ_WEIGHT(uint64_t io_weight) { + return + io_weight <= CGROUP_WEIGHT_DEFAULT ? + CGROUP_BFQ_WEIGHT_DEFAULT - (CGROUP_WEIGHT_DEFAULT - io_weight) * (CGROUP_BFQ_WEIGHT_DEFAULT - CGROUP_BFQ_WEIGHT_MIN) / (CGROUP_WEIGHT_DEFAULT - CGROUP_WEIGHT_MIN) : + CGROUP_BFQ_WEIGHT_DEFAULT + (io_weight - CGROUP_WEIGHT_DEFAULT) * (CGROUP_BFQ_WEIGHT_MAX - CGROUP_BFQ_WEIGHT_DEFAULT) / (CGROUP_WEIGHT_MAX - CGROUP_WEIGHT_DEFAULT); +} + /* Special values for the blkio.weight attribute */ #define CGROUP_BLKIO_WEIGHT_INVALID UINT64_MAX #define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10) @@ -191,6 +205,8 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path); int cg_rmdir(const char *controller, const char *path); +int cg_is_threaded(const char *controller, const char *path); + typedef enum { CG_KEY_MODE_GRACEFUL = 1 << 0, } CGroupKeyMode; diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c index 344e7e1bb..61f8b3351 100644 --- a/src/basic/chase-symlinks.c +++ b/src/basic/chase-symlinks.c @@ -41,7 +41,7 @@ static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK), "Detected unsafe path transition %s (owned by %s) %s %s (owned by %s) during canonicalization of %s.", - strna(n1), strna(user_a), special_glyph(SPECIAL_GLYPH_ARROW), strna(n2), strna(user_b), path); + strna(n1), strna(user_a), special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), strna(n2), strna(user_b), path); } static int log_autofs_mount_point(int fd, const char *path, unsigned flags) { diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index 287428b56..82c6dc567 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -145,7 +145,7 @@ static int conf_files_list_strv_internal( _cleanup_hashmap_free_ Hashmap *fh = NULL; _cleanup_set_free_free_ Set *masked = NULL; - char **files, **p; + char **files; int r; assert(ret); @@ -202,11 +202,8 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p int c; c = base_cmp((char* const*) *strv + i, (char* const*) &path); - if (c == 0) { - char **dir; - + if (c == 0) /* Oh, there already is an entry with a matching name (the last component). */ - STRV_FOREACH(dir, dirs) { _cleanup_free_ char *rdir = NULL; char *p1, *p2; @@ -233,7 +230,7 @@ int conf_files_insert(char ***strv, const char *root, char **dirs, const char *p } } - } else if (c > 0) + else if (c > 0) /* Following files have lower priority, let's go insert our * new entry. */ break; diff --git a/src/basic/def.h b/src/basic/def.h index eccee3d3f..ffd462c45 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -57,8 +57,13 @@ #define CONF_PATHS_STRV(n) \ STRV_MAKE(CONF_PATHS(n)) +/* The limit for PID 1 itself (which is not inherited to children) */ #define HIGH_RLIMIT_MEMLOCK (1024ULL*1024ULL*64ULL) +/* Since kernel 5.16 the kernel default limit was raised to 8M. Let's adjust things on old kernels too, and + * in containers so that our children inherit that. */ +#define DEFAULT_RLIMIT_MEMLOCK (1024ULL*1024ULL*8ULL) + #define PLYMOUTH_SOCKET { \ .un.sun_family = AF_UNIX, \ .un.sun_path = "\0/org/freedesktop/plymouthd", \ diff --git a/src/basic/efivars.c b/src/basic/efivars.c index 7a9d1bf64..847b6da1e 100644 --- a/src/basic/efivars.c +++ b/src/basic/efivars.c @@ -142,7 +142,7 @@ int efi_get_variable( return 0; } -int efi_get_variable_string(const char *variable, char **p) { +int efi_get_variable_string(const char *variable, char **ret) { _cleanup_free_ void *s = NULL; size_t ss = 0; int r; @@ -156,7 +156,7 @@ int efi_get_variable_string(const char *variable, char **p) { if (!x) return -ENOMEM; - *p = x; + *ret = x; return 0; } @@ -310,9 +310,17 @@ static int read_flag(const char *variable) { bool is_efi_secure_boot(void) { static int cache = -1; + int r; - if (cache < 0) - cache = read_flag(EFI_GLOBAL_VARIABLE(SecureBoot)); + if (cache < 0) { + r = read_flag(EFI_GLOBAL_VARIABLE(SecureBoot)); + if (r == -ENOENT) + cache = false; + else if (r < 0) + log_debug_errno(r, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m"); + else + cache = r; + } return cache > 0; } @@ -326,7 +334,8 @@ SecureBootMode efi_get_secure_boot_mode(void) { int secure = read_flag(EFI_GLOBAL_VARIABLE(SecureBoot)); if (secure < 0) { if (secure != -ENOENT) - log_debug_errno(secure, "Error reading SecureBoot EFI variable: %m"); + log_debug_errno(secure, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m"); + return (cache = SECURE_BOOT_UNSUPPORTED); } @@ -341,7 +350,7 @@ SecureBootMode efi_get_secure_boot_mode(void) { return (cache = decode_secure_boot_mode(secure, audit > 0, deployed > 0, setup > 0)); } -static int read_efi_options_variable(char **line) { +static int read_efi_options_variable(char **ret) { int r; /* In SecureBoot mode this is probably not what you want. As your cmdline is cryptographically signed @@ -361,7 +370,7 @@ static int read_efi_options_variable(char **line) { return -EPERM; } - r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), line); + r = efi_get_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions), ret); if (r == -ENOENT) return -ENODATA; return r; @@ -379,13 +388,13 @@ int cache_efi_options_variable(void) { WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755); } -int systemd_efi_options_variable(char **line) { +int systemd_efi_options_variable(char **ret) { const char *e; int r; /* Returns the contents of the variable for current boot from the cache. */ - assert(line); + assert(ret); /* For testing purposes it is sometimes useful to be able to override this */ e = secure_getenv("SYSTEMD_EFI_OPTIONS"); @@ -396,11 +405,11 @@ int systemd_efi_options_variable(char **line) { if (!m) return -ENOMEM; - *line = m; + *ret = m; return 0; } - r = read_one_line_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), line); + r = read_one_line_file(EFIVAR_CACHE_PATH(EFI_SYSTEMD_VARIABLE(SystemdOptions)), ret); if (r == -ENOENT) return -ENODATA; return r; @@ -410,7 +419,7 @@ static inline int compare_stat_mtime(const struct stat *a, const struct stat *b) return CMP(timespec_load(&a->st_mtim), timespec_load(&b->st_mtim)); } -int systemd_efi_options_efivarfs_if_newer(char **line) { +int systemd_efi_options_efivarfs_if_newer(char **ret) { struct stat a = {}, b; int r; @@ -424,15 +433,14 @@ int systemd_efi_options_efivarfs_if_newer(char **line) { log_debug("Variable SystemdOptions in evifarfs is newer than in cache."); else { log_debug("Variable SystemdOptions in cache is up to date."); - *line = NULL; + *ret = NULL; return 0; } - r = read_efi_options_variable(line); + r = read_efi_options_variable(ret); if (r < 0) - log_warning_errno(r, "Failed to read SystemdOptions EFI variable: %m"); - if (r == -ENOENT) - return -ENODATA; - return r; + return log_debug_errno(r, "Failed to read SystemdOptions EFI variable: %m"); + + return 0; } #endif diff --git a/src/basic/efivars.h b/src/basic/efivars.h index 494154b36..bafe2d312 100644 --- a/src/basic/efivars.h +++ b/src/basic/efivars.h @@ -19,9 +19,10 @@ #define EFI_VENDOR_GLOBAL_STR SD_ID128_MAKE_UUID_STR(8b,e4,df,61,93,ca,11,d2,aa,0d,00,e0,98,03,2b,8c) #define EFI_VENDOR_SYSTEMD SD_ID128_MAKE(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67) #define EFI_VENDOR_SYSTEMD_STR SD_ID128_MAKE_UUID_STR(8c,f2,64,4b,4b,0b,42,8f,93,87,6d,87,60,50,dc,67) -#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001 -#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002 -#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004 + +#define EFI_VARIABLE_NON_VOLATILE UINT32_C(0x00000001) +#define EFI_VARIABLE_BOOTSERVICE_ACCESS UINT32_C(0x00000002) +#define EFI_VARIABLE_RUNTIME_ACCESS UINT32_C(0x00000004) /* Note that the - naming scheme is an efivarfs convention, i.e. part of the Linux * API file system implementation for EFI. EFI itself processes UIDS in binary form. @@ -42,8 +43,8 @@ #if ENABLE_EFI -int efi_get_variable(const char *variable, uint32_t *attribute, void **value, size_t *size); -int efi_get_variable_string(const char *variable, char **p); +int efi_get_variable(const char *variable, uint32_t *attribute, void **ret_value, size_t *ret_size); +int efi_get_variable_string(const char *variable, char **ret); int efi_set_variable(const char *variable, const void *value, size_t size); int efi_set_variable_string(const char *variable, const char *p); @@ -52,8 +53,8 @@ bool is_efi_secure_boot(void); SecureBootMode efi_get_secure_boot_mode(void); int cache_efi_options_variable(void); -int systemd_efi_options_variable(char **line); -int systemd_efi_options_efivarfs_if_newer(char **line); +int systemd_efi_options_variable(char **ret); +int systemd_efi_options_efivarfs_if_newer(char **ret); #else @@ -61,7 +62,7 @@ static inline int efi_get_variable(const char *variable, uint32_t *attribute, vo return -EOPNOTSUPP; } -static inline int efi_get_variable_string(const char *variable, char **p) { +static inline int efi_get_variable_string(const char *variable, char **ret) { return -EOPNOTSUPP; } diff --git a/src/basic/env-file.c b/src/basic/env-file.c index 599b73bc2..2ab5c7bc0 100644 --- a/src/basic/env-file.c +++ b/src/basic/env-file.c @@ -16,14 +16,12 @@ static int parse_env_file_internal( FILE *f, const char *fname, int (*push) (const char *filename, unsigned line, - const char *key, char *value, void *userdata, int *n_pushed), - void *userdata, - int *n_pushed) { + const char *key, char *value, void *userdata), + void *userdata) { size_t n_key = 0, n_value = 0, last_value_whitespace = SIZE_MAX, last_key_whitespace = SIZE_MAX; _cleanup_free_ char *contents = NULL, *key = NULL, *value = NULL; unsigned line = 1; - char *p; int r; enum { @@ -46,7 +44,7 @@ static int parse_env_file_internal( if (r < 0) return r; - for (p = contents; *p; p++) { + for (char *p = contents; *p; p++) { char c = *p; switch (state) { @@ -100,7 +98,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -143,7 +141,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -262,7 +260,7 @@ static int parse_env_file_internal( if (last_key_whitespace != SIZE_MAX) key[last_key_whitespace] = 0; - r = push(fname, line, key, value, userdata, n_pushed); + r = push(fname, line, key, value, userdata); if (r < 0) return r; @@ -300,8 +298,7 @@ static int check_utf8ness_and_warn( static int parse_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { const char *k; va_list aq, *ap = userdata; @@ -323,9 +320,6 @@ static int parse_env_file_push( free(*v); *v = value; - if (n_pushed) - (*n_pushed)++; - return 1; } } @@ -341,16 +335,13 @@ int parse_env_filev( const char *fname, va_list ap) { - int r, n_pushed = 0; + int r; va_list aq; va_copy(aq, ap); - r = parse_env_file_internal(f, fname, parse_env_file_push, &aq, &n_pushed); + r = parse_env_file_internal(f, fname, parse_env_file_push, &aq); va_end(aq); - if (r < 0) - return r; - - return n_pushed; + return r; } int parse_env_file_sentinel( @@ -371,8 +362,7 @@ int parse_env_file_sentinel( static int load_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { char ***m = userdata; char *p; int r; @@ -389,78 +379,69 @@ static int load_env_file_push( if (r < 0) return r; - if (n_pushed) - (*n_pushed)++; - free(value); return 0; } int load_env_file(FILE *f, const char *fname, char ***rl) { - char **m = NULL; + _cleanup_strv_free_ char **m = NULL; int r; - r = parse_env_file_internal(f, fname, load_env_file_push, &m, NULL); - if (r < 0) { - strv_free(m); + r = parse_env_file_internal(f, fname, load_env_file_push, &m); + if (r < 0) return r; - } - *rl = m; + *rl = TAKE_PTR(m); return 0; } static int load_env_file_push_pairs( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { - char ***m = userdata; + void *userdata) { + + char ***m = ASSERT_PTR(userdata); int r; r = check_utf8ness_and_warn(filename, line, key, value); if (r < 0) return r; + /* Check if the key is present */ + for (char **t = *m; t && *t; t += 2) + if (streq(t[0], key)) { + if (value) + return free_and_replace(t[1], value); + else + return free_and_strdup(t+1, ""); + } + r = strv_extend(m, key); if (r < 0) - return -ENOMEM; + return r; - if (!value) { - r = strv_extend(m, ""); - if (r < 0) - return -ENOMEM; - } else { - r = strv_push(m, value); - if (r < 0) - return r; - } - - if (n_pushed) - (*n_pushed)++; - - return 0; + if (value) + return strv_push(m, value); + else + return strv_extend(m, ""); } int load_env_file_pairs(FILE *f, const char *fname, char ***rl) { - char **m = NULL; + _cleanup_strv_free_ char **m = NULL; int r; - r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m, NULL); - if (r < 0) { - strv_free(m); + r = parse_env_file_internal(f, fname, load_env_file_push_pairs, &m); + if (r < 0) return r; - } - *rl = m; + *rl = TAKE_PTR(m); return 0; } static int merge_env_file_push( const char *filename, unsigned line, const char *key, char *value, - void *userdata, - int *n_pushed) { + void *userdata) { char ***env = userdata; char *expanded_value; @@ -489,7 +470,7 @@ static int merge_env_file_push( log_debug("%s:%u: setting %s=%s", filename, line, key, value); - return load_env_file_push(filename, line, key, value, env, n_pushed); + return load_env_file_push(filename, line, key, value, env); } int merge_env_file( @@ -501,7 +482,7 @@ int merge_env_file( * plus "extended" substitutions, unlike other exported parsing functions. */ - return parse_env_file_internal(f, fname, merge_env_file_push, env, NULL); + return parse_env_file_internal(f, fname, merge_env_file_push, env); } static void write_env_var(FILE *f, const char *v) { @@ -538,7 +519,6 @@ static void write_env_var(FILE *f, const char *v) { int write_env_file(const char *fname, char **l) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; - char **i; int r; assert(fname); diff --git a/src/basic/env-util.c b/src/basic/env-util.c index 885967e7f..b60c9f9fd 100644 --- a/src/basic/env-util.c +++ b/src/basic/env-util.c @@ -96,8 +96,6 @@ bool env_assignment_is_valid(const char *e) { } bool strv_env_is_valid(char **e) { - char **p, **q; - STRV_FOREACH(p, e) { size_t k; @@ -115,8 +113,6 @@ bool strv_env_is_valid(char **e) { } bool strv_env_name_is_valid(char **l) { - char **p; - STRV_FOREACH(p, l) { if (!env_name_is_valid(*p)) return false; @@ -129,8 +125,6 @@ bool strv_env_name_is_valid(char **l) { } bool strv_env_name_or_assignment_is_valid(char **l) { - char **p; - STRV_FOREACH(p, l) { if (!env_assignment_is_valid(*p) && !env_name_is_valid(*p)) return false; @@ -272,7 +266,7 @@ static bool env_entry_has_name(const char *entry, const char *name) { char **strv_env_delete(char **x, size_t n_lists, ...) { size_t n, i = 0; - char **k, **r; + char **r; va_list ap; /* Deletes every entry from x that is mentioned in the other @@ -287,7 +281,7 @@ char **strv_env_delete(char **x, size_t n_lists, ...) { STRV_FOREACH(k, x) { va_start(ap, n_lists); for (size_t v = 0; v < n_lists; v++) { - char **l, **j; + char **l; l = va_arg(ap, char**); STRV_FOREACH(j, l) @@ -379,7 +373,6 @@ char **strv_env_unset_many(char **l, ...) { int strv_env_replace_consume(char ***l, char *p) { const char *t, *name; - char **f; int r; assert(p); @@ -467,8 +460,6 @@ int strv_env_assign(char ***l, const char *key, const char *value) { } char *strv_env_get_n(char **l, const char *name, size_t k, unsigned flags) { - char **i; - assert(name); if (k <= 0) @@ -496,7 +487,7 @@ char *strv_env_get(char **l, const char *name) { } char *strv_env_pairs_get(char **l, const char *name) { - char **key, **value, *result = NULL; + char *result = NULL; assert(name); @@ -508,7 +499,6 @@ char *strv_env_pairs_get(char **l, const char *name) { } char **strv_env_clean_with_callback(char **e, void (*invalid_callback)(const char *p, void *userdata), void *userdata) { - char **p, **q; int k = 0; STRV_FOREACH(p, e) { @@ -702,7 +692,7 @@ char *replace_env_n(const char *format, size_t n, char **env, unsigned flags) { } char **replace_env_argv(char **argv, char **env) { - char **ret, **i; + char **ret; size_t k = 0, l = 0; l = strv_length(argv); @@ -832,7 +822,6 @@ int setenv_systemd_exec_pid(bool update_only) { int getenv_path_list(const char *name, char ***ret_paths) { _cleanup_strv_free_ char **l = NULL; const char *e; - char **p; int r; assert(name); @@ -868,19 +857,36 @@ int getenv_path_list(const char *name, char ***ret_paths) { return 1; } -int unsetenv_erase(const char *name) { - char *p; +int getenv_steal_erase(const char *name, char **ret) { + _cleanup_(erase_and_freep) char *a = NULL; + char *e; assert(name); - p = getenv(name); - if (!p) - return 0; + /* Reads an environment variable, makes a copy of it, erases its memory in the environment block and removes + * it from there. Usecase: reading passwords from the env block (which is a bad idea, but useful for + * testing, and given that people are likely going to misuse this, be thorough) */ - string_erase(p); + e = getenv(name); + if (!e) { + if (ret) + *ret = NULL; + return 0; + } + + if (ret) { + a = strdup(e); + if (!a) + return -ENOMEM; + } + + string_erase(e); if (unsetenv(name) < 0) return -errno; + if (ret) + *ret = TAKE_PTR(a); + return 1; } diff --git a/src/basic/env-util.h b/src/basic/env-util.h index 38bfc8a3f..2bf0603f2 100644 --- a/src/basic/env-util.h +++ b/src/basic/env-util.h @@ -69,4 +69,4 @@ int setenv_systemd_exec_pid(bool update_only); * PATH-like colon-separated absolute paths */ int getenv_path_list(const char *name, char ***ret_paths); -int unsetenv_erase(const char *name); +int getenv_steal_erase(const char *name, char **ret); diff --git a/src/basic/errno-to-name.awk b/src/basic/errno-to-name.awk index 6b18a90e1..844212448 100644 --- a/src/basic/errno-to-name.awk +++ b/src/basic/errno-to-name.awk @@ -3,7 +3,7 @@ BEGIN{ print "static const char* const errno_names[] = { " } -!/EDEADLOCK/ && !/EWOULDBLOCK/ && !/ENOTSUP/ { +!/(EDEADLOCK|EWOULDBLOCK|ENOTSUP)/ { printf " [%s] = \"%s\",\n", $1, $1 } END{ diff --git a/src/basic/errno-util.h b/src/basic/errno-util.h index 09abf0b75..648de50eb 100644 --- a/src/basic/errno-util.h +++ b/src/basic/errno-util.h @@ -138,10 +138,18 @@ static inline bool ERRNO_IS_PRIVILEGE(int r) { EPERM); } -/* Three difference errors for "not enough disk space" */ +/* Three different errors for "not enough disk space" */ static inline bool ERRNO_IS_DISK_SPACE(int r) { return IN_SET(abs(r), ENOSPC, EDQUOT, EFBIG); } + +/* Three different errors for "this device does not quite exist" */ +static inline bool ERRNO_IS_DEVICE_ABSENT(int r) { + return IN_SET(abs(r), + ENODEV, + ENXIO, + ENOENT); +} diff --git a/src/basic/escape.c b/src/basic/escape.c index ce57fcc76..1cb7ced54 100644 --- a/src/basic/escape.c +++ b/src/basic/escape.c @@ -549,7 +549,6 @@ char* quote_command_line(char **argv, ShellEscapeFlags flags) { assert(argv); - char **a; STRV_FOREACH(a, argv) { _cleanup_free_ char *t = NULL; diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 3866e8767..c02b52992 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -417,14 +417,11 @@ int same_fd(int a, int b) { assert(a >= 0); assert(b >= 0); - /* Compares two file descriptors. Note that semantics are - * quite different depending on whether we have kcmp() or we - * don't. If we have kcmp() this will only return true for - * dup()ed file descriptors, but not otherwise. If we don't - * have kcmp() this will also return true for two fds of the same - * file, created by separate open() calls. Since we use this - * call mostly for filtering out duplicates in the fd store - * this difference hopefully doesn't matter too much. */ + /* Compares two file descriptors. Note that semantics are quite different depending on whether we + * have kcmp() or we don't. If we have kcmp() this will only return true for dup()ed file + * descriptors, but not otherwise. If we don't have kcmp() this will also return true for two fds of + * the same file, created by separate open() calls. Since we use this call mostly for filtering out + * duplicates in the fd store this difference hopefully doesn't matter too much. */ if (a == b) return true; @@ -436,7 +433,7 @@ int same_fd(int a, int b) { return true; if (r > 0) return false; - if (!IN_SET(errno, ENOSYS, EACCES, EPERM)) + if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno)) return -errno; /* We don't have kcmp(), use fstat() instead. */ @@ -446,23 +443,17 @@ int same_fd(int a, int b) { if (fstat(b, &stb) < 0) return -errno; - if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT)) + if (!stat_inode_same(&sta, &stb)) return false; - /* We consider all device fds different, since two device fds - * might refer to quite different device contexts even though - * they share the same inode and backing dev_t. */ + /* We consider all device fds different, since two device fds might refer to quite different device + * contexts even though they share the same inode and backing dev_t. */ if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode)) return false; - if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino) - return false; - - /* The fds refer to the same inode on disk, let's also check - * if they have the same fd flags. This is useful to - * distinguish the read and write side of a pipe created with - * pipe(). */ + /* The fds refer to the same inode on disk, let's also check if they have the same fd flags. This is + * useful to distinguish the read and write side of a pipe created with pipe(). */ fa = fcntl(a, F_GETFL); if (fa < 0) return -errno; diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 8d92da327..e7b670ab2 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1042,8 +1042,6 @@ static int search_and_fopen_internal( FILE **ret, char **ret_path) { - char **i; - assert(path); assert(mode); assert(ret); @@ -1195,7 +1193,7 @@ int write_timestamp_file_atomic(const char *fn, usec_t n) { /* Creates a "timestamp" file, that contains nothing but a * usec_t timestamp, formatted in ASCII. */ - if (n <= 0 || n >= USEC_INFINITY) + if (!timestamp_is_set(n)) return -ERANGE; xsprintf(ln, USEC_FMT "\n", n); @@ -1216,7 +1214,7 @@ int read_timestamp_file(const char *fn, usec_t *ret) { if (r < 0) return r; - if (t <= 0 || t >= (uint64_t) USEC_INFINITY) + if (!timestamp_is_set(t)) return -ERANGE; *ret = (usec_t) t; diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 552986f54..4c81057a8 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -570,7 +570,6 @@ int get_files_in_directory(const char *path, char ***list) { } static int getenv_tmp_dir(const char **ret_path) { - const char *n; int r, ret = 0; assert(ret_path); @@ -856,8 +855,7 @@ int conservative_renameat( if (fstat(new_fd, &new_stat) < 0) goto do_rename; - if (new_stat.st_ino == old_stat.st_ino && - new_stat.st_dev == old_stat.st_dev) + if (stat_inode_same(&new_stat, &old_stat)) goto is_same; if (old_stat.st_mode != new_stat.st_mode || @@ -1084,3 +1082,50 @@ int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode) { return TAKE_FD(fd); } + +int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created) { + unsigned attempts = 7; + int fd; + + /* Just like openat(), but adds one thing: optionally returns whether we created the file anew or if + * it already existed before. This is only relevant if O_CREAT is set without O_EXCL, and thus will + * shortcut to openat() otherwise */ + + if (!ret_newly_created) + return RET_NERRNO(openat(dirfd, pathname, flags, mode)); + + if (!FLAGS_SET(flags, O_CREAT) || FLAGS_SET(flags, O_EXCL)) { + fd = openat(dirfd, pathname, flags, mode); + if (fd < 0) + return -errno; + + *ret_newly_created = FLAGS_SET(flags, O_CREAT); + return fd; + } + + for (;;) { + /* First, attempt to open without O_CREAT/O_EXCL, i.e. open existing file */ + fd = openat(dirfd, pathname, flags & ~(O_CREAT | O_EXCL), mode); + if (fd >= 0) { + *ret_newly_created = false; + return fd; + } + if (errno != ENOENT) + return -errno; + + /* So the file didn't exist yet, hence create it with O_CREAT/O_EXCL. */ + fd = openat(dirfd, pathname, flags | O_CREAT | O_EXCL, mode); + if (fd >= 0) { + *ret_newly_created = true; + return fd; + } + if (errno != EEXIST) + return -errno; + + /* Hmm, so now we got EEXIST? So it apparently exists now? If so, let's try to open again + * without the two flags. But let's not spin forever, hence put a limit on things */ + + if (--attempts == 0) /* Give up eventually, somebody is playing with us */ + return -EEXIST; + } +} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 0bbb3f629..e48cf6800 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -110,3 +110,5 @@ int posix_fallocate_loop(int fd, uint64_t offset, uint64_t size); int parse_cifs_service(const char *s, char **ret_host, char **ret_service, char **ret_path); int open_mkdir_at(int dirfd, const char *path, int flags, mode_t mode); + +int openat_report_new(int dirfd, const char *pathname, int flags, mode_t mode, bool *ret_newly_created); diff --git a/src/basic/glyph-util.c b/src/basic/glyph-util.c index 8810738fc..cfa7d041e 100644 --- a/src/basic/glyph-util.c +++ b/src/basic/glyph-util.c @@ -39,6 +39,7 @@ const char *special_glyph(SpecialGlyph code) { [SPECIAL_GLYPH_TREE_BRANCH] = "|-", [SPECIAL_GLYPH_TREE_RIGHT] = "`-", [SPECIAL_GLYPH_TREE_SPACE] = " ", + [SPECIAL_GLYPH_TREE_TOP] = ",-", [SPECIAL_GLYPH_TRIANGULAR_BULLET] = ">", [SPECIAL_GLYPH_BLACK_CIRCLE] = "*", [SPECIAL_GLYPH_WHITE_CIRCLE] = "*", @@ -51,7 +52,9 @@ const char *special_glyph(SpecialGlyph code) { [SPECIAL_GLYPH_LIGHT_SHADE] = "-", [SPECIAL_GLYPH_DARK_SHADE] = "X", [SPECIAL_GLYPH_SIGMA] = "S", - [SPECIAL_GLYPH_ARROW] = "->", + [SPECIAL_GLYPH_ARROW_RIGHT] = "->", + [SPECIAL_GLYPH_ARROW_UP] = "^", + [SPECIAL_GLYPH_ARROW_DOWN] = "v", [SPECIAL_GLYPH_ELLIPSIS] = "...", [SPECIAL_GLYPH_EXTERNAL_LINK] = "[LNK]", [SPECIAL_GLYPH_ECSTATIC_SMILEY] = ":-]", @@ -71,53 +74,56 @@ const char *special_glyph(SpecialGlyph code) { /* UTF-8 */ [true] = { /* The following are multiple glyphs in both ASCII and in UNICODE */ - [SPECIAL_GLYPH_TREE_VERTICAL] = "\342\224\202 ", /* │ */ - [SPECIAL_GLYPH_TREE_BRANCH] = "\342\224\234\342\224\200", /* ├─ */ - [SPECIAL_GLYPH_TREE_RIGHT] = "\342\224\224\342\224\200", /* └─ */ - [SPECIAL_GLYPH_TREE_SPACE] = " ", /* */ + [SPECIAL_GLYPH_TREE_VERTICAL] = u8"│ ", + [SPECIAL_GLYPH_TREE_BRANCH] = u8"├─", + [SPECIAL_GLYPH_TREE_RIGHT] = u8"└─", + [SPECIAL_GLYPH_TREE_SPACE] = u8" ", + [SPECIAL_GLYPH_TREE_TOP] = u8"┌─", /* Single glyphs in both cases */ - [SPECIAL_GLYPH_TRIANGULAR_BULLET] = "\342\200\243", /* ‣ */ - [SPECIAL_GLYPH_BLACK_CIRCLE] = "\342\227\217", /* ● */ - [SPECIAL_GLYPH_WHITE_CIRCLE] = "\u25CB", /* ○ */ - [SPECIAL_GLYPH_MULTIPLICATION_SIGN] = "\u00D7", /* × */ - [SPECIAL_GLYPH_CIRCLE_ARROW] = "\u21BB", /* ↻ */ - [SPECIAL_GLYPH_BULLET] = "\342\200\242", /* • */ - [SPECIAL_GLYPH_MU] = "\316\274", /* μ (actually called: GREEK SMALL LETTER MU) */ - [SPECIAL_GLYPH_CHECK_MARK] = "\342\234\223", /* ✓ */ - [SPECIAL_GLYPH_CROSS_MARK] = "\342\234\227", /* ✗ (actually called: BALLOT X) */ - [SPECIAL_GLYPH_LIGHT_SHADE] = "\342\226\221", /* ░ */ - [SPECIAL_GLYPH_DARK_SHADE] = "\342\226\223", /* ▒ */ - [SPECIAL_GLYPH_SIGMA] = "\316\243", /* Σ */ + [SPECIAL_GLYPH_TRIANGULAR_BULLET] = u8"‣", + [SPECIAL_GLYPH_BLACK_CIRCLE] = u8"●", + [SPECIAL_GLYPH_WHITE_CIRCLE] = u8"○", + [SPECIAL_GLYPH_MULTIPLICATION_SIGN] = u8"×", + [SPECIAL_GLYPH_CIRCLE_ARROW] = u8"↻", + [SPECIAL_GLYPH_BULLET] = u8"•", + [SPECIAL_GLYPH_MU] = u8"μ", /* actually called: GREEK SMALL LETTER MU */ + [SPECIAL_GLYPH_CHECK_MARK] = u8"✓", + [SPECIAL_GLYPH_CROSS_MARK] = u8"✗", /* actually called: BALLOT X */ + [SPECIAL_GLYPH_LIGHT_SHADE] = u8"░", + [SPECIAL_GLYPH_DARK_SHADE] = u8"▒", + [SPECIAL_GLYPH_SIGMA] = u8"Σ", + [SPECIAL_GLYPH_ARROW_UP] = u8"↑", /* actually called: UPWARDS ARROW */ + [SPECIAL_GLYPH_ARROW_DOWN] = u8"↓", /* actually called: DOWNWARDS ARROW */ /* Single glyph in Unicode, two in ASCII */ - [SPECIAL_GLYPH_ARROW] = "\342\206\222", /* → (actually called: RIGHTWARDS ARROW) */ + [SPECIAL_GLYPH_ARROW_RIGHT] = u8"→", /* actually called: RIGHTWARDS ARROW */ /* Single glyph in Unicode, three in ASCII */ - [SPECIAL_GLYPH_ELLIPSIS] = "\342\200\246", /* … (actually called: HORIZONTAL ELLIPSIS) */ + [SPECIAL_GLYPH_ELLIPSIS] = u8"…", /* actually called: HORIZONTAL ELLIPSIS */ /* Three glyphs in Unicode, five in ASCII */ - [SPECIAL_GLYPH_EXTERNAL_LINK] = "[\360\237\241\225]", /* 🡕 (actually called: NORTH EAST SANS-SERIF ARROW, enclosed in []) */ + [SPECIAL_GLYPH_EXTERNAL_LINK] = u8"[🡕]", /* actually called: NORTH EAST SANS-SERIF ARROW, enclosed in [] */ /* These smileys are a single glyph in Unicode, and three in ASCII */ - [SPECIAL_GLYPH_ECSTATIC_SMILEY] = "\360\237\230\207", /* 😇 (actually called: SMILING FACE WITH HALO) */ - [SPECIAL_GLYPH_HAPPY_SMILEY] = "\360\237\230\200", /* 😀 (actually called: GRINNING FACE) */ - [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = "\360\237\231\202", /* 🙂 (actually called: SLIGHTLY SMILING FACE) */ - [SPECIAL_GLYPH_NEUTRAL_SMILEY] = "\360\237\230\220", /* 😐 (actually called: NEUTRAL FACE) */ - [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = "\360\237\231\201", /* 🙁 (actually called: SLIGHTLY FROWNING FACE) */ - [SPECIAL_GLYPH_UNHAPPY_SMILEY] = "\360\237\230\250", /* 😨 (actually called: FEARFUL FACE) */ - [SPECIAL_GLYPH_DEPRESSED_SMILEY] = "\360\237\244\242", /* 🤢 (actually called: NAUSEATED FACE) */ + [SPECIAL_GLYPH_ECSTATIC_SMILEY] = u8"😇", /* actually called: SMILING FACE WITH HALO */ + [SPECIAL_GLYPH_HAPPY_SMILEY] = u8"😀", /* actually called: GRINNING FACE */ + [SPECIAL_GLYPH_SLIGHTLY_HAPPY_SMILEY] = u8"🙂", /* actually called: SLIGHTLY SMILING FACE */ + [SPECIAL_GLYPH_NEUTRAL_SMILEY] = u8"😐", /* actually called: NEUTRAL FACE */ + [SPECIAL_GLYPH_SLIGHTLY_UNHAPPY_SMILEY] = u8"🙁", /* actually called: SLIGHTLY FROWNING FACE */ + [SPECIAL_GLYPH_UNHAPPY_SMILEY] = u8"😨", /* actually called: FEARFUL FACE */ + [SPECIAL_GLYPH_DEPRESSED_SMILEY] = u8"🤢", /* actually called: NAUSEATED FACE */ /* This emoji is a single character cell glyph in Unicode, and three in ASCII */ - [SPECIAL_GLYPH_LOCK_AND_KEY] = "\360\237\224\220", /* 🔐 (actually called: CLOSED LOCK WITH KEY) */ + [SPECIAL_GLYPH_LOCK_AND_KEY] = u8"🔐", /* actually called: CLOSED LOCK WITH KEY */ /* This emoji is a single character cell glyph in Unicode, and two in ASCII */ - [SPECIAL_GLYPH_TOUCH] = "\360\237\221\206", /* 👆 (actually called: BACKHAND INDEX POINTING UP) */ + [SPECIAL_GLYPH_TOUCH] = u8"👆", /* actually called: BACKHAND INDEX POINTING UP */ /* These three emojis are single character cell glyphs in Unicode and also in ASCII. */ - [SPECIAL_GLYPH_RECYCLING] = "\u267B\uFE0F ", /* ♻️ (actually called: UNIVERSAL RECYCLNG SYMBOL) */ - [SPECIAL_GLYPH_DOWNLOAD] = "\u2935\uFE0F ", /* ⤵️ (actually called: RIGHT ARROW CURVING DOWN) */ - [SPECIAL_GLYPH_SPARKLES] = "\u2728", /* ✨ */ + [SPECIAL_GLYPH_RECYCLING] = u8"♻️", /* actually called: UNIVERSAL RECYCLNG SYMBOL */ + [SPECIAL_GLYPH_DOWNLOAD] = u8"⤵️", /* actually called: RIGHT ARROW CURVING DOWN */ + [SPECIAL_GLYPH_SPARKLES] = u8"✨", }, }; diff --git a/src/basic/glyph-util.h b/src/basic/glyph-util.h index ddee21004..7e0a73842 100644 --- a/src/basic/glyph-util.h +++ b/src/basic/glyph-util.h @@ -11,6 +11,7 @@ typedef enum SpecialGlyph { SPECIAL_GLYPH_TREE_BRANCH, SPECIAL_GLYPH_TREE_RIGHT, SPECIAL_GLYPH_TREE_SPACE, + SPECIAL_GLYPH_TREE_TOP, SPECIAL_GLYPH_TRIANGULAR_BULLET, SPECIAL_GLYPH_BLACK_CIRCLE, SPECIAL_GLYPH_WHITE_CIRCLE, @@ -20,7 +21,9 @@ typedef enum SpecialGlyph { SPECIAL_GLYPH_MU, SPECIAL_GLYPH_CHECK_MARK, SPECIAL_GLYPH_CROSS_MARK, - SPECIAL_GLYPH_ARROW, + SPECIAL_GLYPH_ARROW_RIGHT, + SPECIAL_GLYPH_ARROW_UP, + SPECIAL_GLYPH_ARROW_DOWN, SPECIAL_GLYPH_ELLIPSIS, SPECIAL_GLYPH_LIGHT_SHADE, SPECIAL_GLYPH_DARK_SHADE, diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index b51d70bc8..bd7a6b0d6 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -1864,7 +1864,6 @@ int _set_put_strdup_full(Set **s, const struct hash_ops *hash_ops, const char *p int _set_put_strdupv_full(Set **s, const struct hash_ops *hash_ops, char **l HASHMAP_DEBUG_PARAMS) { int n = 0, r; - char **i; assert(s); diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index 8c83a0e71..190fca8ee 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -683,8 +683,7 @@ static int base64_append_width( s += indent; } - memcpy(s, x + width * line, act); - s += act; + s = mempcpy(s, x + width * line, act); *(s++) = line < lines - 1 ? '\n' : '\0'; avail -= act; } diff --git a/src/basic/hmac.h b/src/basic/hmac.h index a5682c439..e58c1838a 100644 --- a/src/basic/hmac.h +++ b/src/basic/hmac.h @@ -4,7 +4,7 @@ #include #include -#define SHA256_DIGEST_SIZE 32 +#include "sha256.h" /* Unoptimized implementation based on FIPS 198. 'res' has to be allocated by * the caller. Prefer external OpenSSL functions, and use this only when diff --git a/src/basic/hostname-util.c b/src/basic/hostname-util.c index 136fb3e59..d5d138810 100644 --- a/src/basic/hostname-util.c +++ b/src/basic/hostname-util.c @@ -8,6 +8,7 @@ #include #include "alloc-util.h" +#include "env-file.h" #include "hostname-util.h" #include "os-util.h" #include "string-util.h" @@ -191,3 +192,20 @@ bool is_localhost(const char *hostname) { endswith_no_case(hostname, ".localhost.localdomain") || endswith_no_case(hostname, ".localhost.localdomain."); } + +int get_pretty_hostname(char **ret) { + _cleanup_free_ char *n = NULL; + int r; + + assert(ret); + + r = parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &n); + if (r < 0) + return r; + + if (isempty(n)) + return -ENXIO; + + *ret = TAKE_PTR(n); + return 0; +} diff --git a/src/basic/hostname-util.h b/src/basic/hostname-util.h index d435bed50..a00b85239 100644 --- a/src/basic/hostname-util.h +++ b/src/basic/hostname-util.h @@ -4,7 +4,6 @@ #include #include -#include "env-file.h" #include "macro.h" #include "strv.h" @@ -61,6 +60,4 @@ static inline bool is_outbound_hostname(const char *hostname) { return STRCASE_IN_SET(hostname, "_outbound", "_outbound."); } -static inline int get_pretty_hostname(char **ret) { - return parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", ret); -} +int get_pretty_hostname(char **ret); diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index a43a83199..660c1f182 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -121,6 +121,19 @@ int in_addr_is_localhost(int family, const union in_addr_union *u) { return -EAFNOSUPPORT; } +int in_addr_is_localhost_one(int family, const union in_addr_union *u) { + assert(u); + + if (family == AF_INET) + /* 127.0.0.1 */ + return be32toh(u->in.s_addr) == UINT32_C(0x7F000001); + + if (family == AF_INET6) + return IN6_IS_ADDR_LOOPBACK(&u->in6); /* lgtm [cpp/potentially-dangerous-function] */ + + return -EAFNOSUPPORT; +} + bool in6_addr_is_ipv4_mapped_address(const struct in6_addr *a) { return a->s6_addr32[0] == 0 && a->s6_addr32[1] == 0 && diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 0178391e5..5de87a953 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -49,6 +49,7 @@ bool in6_addr_is_link_local_all_nodes(const struct in6_addr *a); bool in4_addr_is_localhost(const struct in_addr *a); int in_addr_is_localhost(int family, const union in_addr_union *u); +int in_addr_is_localhost_one(int family, const union in_addr_union *u); bool in4_addr_is_local_multicast(const struct in_addr *a); bool in4_addr_is_non_local(const struct in_addr *a); diff --git a/src/basic/inotify-util.h b/src/basic/inotify-util.h index 88af08688..61951ff3e 100644 --- a/src/basic/inotify-util.h +++ b/src/basic/inotify-util.h @@ -6,12 +6,28 @@ #include #include +#include "log.h" + #define INOTIFY_EVENT_MAX (offsetof(struct inotify_event, name) + NAME_MAX + 1) -#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ - for ((e) = &buffer.ev; \ - (uint8_t*) (e) < (uint8_t*) (buffer.raw) + (sz); \ - (e) = (struct inotify_event*) ((uint8_t*) (e) + sizeof(struct inotify_event) + (e)->len)) +#define _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, start, end) \ + for (struct inotify_event \ + *start = &((buffer).ev), \ + *end = (struct inotify_event*) ((uint8_t*) start + (sz)), \ + *e = start; \ + (size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) && \ + ((size_t) ((uint8_t*) end - (uint8_t*) e) >= sizeof(struct inotify_event) + e->len || \ + (log_full(log_level, "Received invalid inotify event, ignoring."), false)); \ + e = (struct inotify_event*) ((uint8_t*) e + sizeof(struct inotify_event) + e->len)) + +#define _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, log_level) \ + _FOREACH_INOTIFY_EVENT(e, buffer, sz, log_level, UNIQ_T(start, UNIQ), UNIQ_T(end, UNIQ)) + +#define FOREACH_INOTIFY_EVENT(e, buffer, sz) \ + _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_DEBUG) + +#define FOREACH_INOTIFY_EVENT_WARN(e, buffer, sz) \ + _FOREACH_INOTIFY_EVENT_FULL(e, buffer, sz, LOG_WARNING) union inotify_event_buffer { struct inotify_event ev; diff --git a/src/basic/io-util.c b/src/basic/io-util.c index 783ca1309..a591a75c3 100644 --- a/src/basic/io-util.c +++ b/src/basic/io-util.c @@ -159,7 +159,6 @@ int pipe_eof(int fd) { } int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) { - struct timespec ts; int r; assert(fds || nfds == 0); @@ -167,7 +166,7 @@ int ppoll_usec(struct pollfd *fds, size_t nfds, usec_t timeout) { if (nfds == 0) return 0; - r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : timespec_store(&ts, timeout), NULL); + r = ppoll(fds, nfds, timeout == USEC_INFINITY ? NULL : TIMESPEC_STORE(timeout), NULL); if (r < 0) return -errno; if (r == 0) diff --git a/src/basic/linux/btrfs.h b/src/basic/linux/btrfs.h index 0f8306fde..66cfd7b94 100644 --- a/src/basic/linux/btrfs.h +++ b/src/basic/linux/btrfs.h @@ -288,6 +288,7 @@ struct btrfs_ioctl_fs_info_args { * first mount when booting older kernel versions. */ #define BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE_VALID (1ULL << 1) +#define BTRFS_FEATURE_COMPAT_RO_VERITY (1ULL << 2) #define BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF (1ULL << 0) #define BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL (1ULL << 1) @@ -770,10 +771,16 @@ struct btrfs_ioctl_received_subvol_args { */ #define BTRFS_SEND_FLAG_OMIT_END_CMD 0x4 +/* + * Read the protocol version in the structure + */ +#define BTRFS_SEND_FLAG_VERSION 0x8 + #define BTRFS_SEND_FLAG_MASK \ (BTRFS_SEND_FLAG_NO_FILE_DATA | \ BTRFS_SEND_FLAG_OMIT_STREAM_HEADER | \ - BTRFS_SEND_FLAG_OMIT_END_CMD) + BTRFS_SEND_FLAG_OMIT_END_CMD | \ + BTRFS_SEND_FLAG_VERSION) struct btrfs_ioctl_send_args { __s64 send_fd; /* in */ @@ -781,7 +788,8 @@ struct btrfs_ioctl_send_args { __u64 *clone_sources; /* in */ __u64 parent_root; /* in */ __u64 flags; /* in */ - __u64 reserved[4]; /* in */ + __u32 version; /* in */ + __u8 reserved[28]; /* in */ }; /* diff --git a/src/basic/linux/btrfs_tree.h b/src/basic/linux/btrfs_tree.h index ccdb40fe4..e1c4c732a 100644 --- a/src/basic/linux/btrfs_tree.h +++ b/src/basic/linux/btrfs_tree.h @@ -118,6 +118,29 @@ #define BTRFS_INODE_REF_KEY 12 #define BTRFS_INODE_EXTREF_KEY 13 #define BTRFS_XATTR_ITEM_KEY 24 + +/* + * fs verity items are stored under two different key types on disk. + * The descriptor items: + * [ inode objectid, BTRFS_VERITY_DESC_ITEM_KEY, offset ] + * + * At offset 0, we store a btrfs_verity_descriptor_item which tracks the size + * of the descriptor item and some extra data for encryption. + * Starting at offset 1, these hold the generic fs verity descriptor. The + * latter are opaque to btrfs, we just read and write them as a blob for the + * higher level verity code. The most common descriptor size is 256 bytes. + * + * The merkle tree items: + * [ inode objectid, BTRFS_VERITY_MERKLE_ITEM_KEY, offset ] + * + * These also start at offset 0, and correspond to the merkle tree bytes. When + * fsverity asks for page 0 of the merkle tree, we pull up one page starting at + * offset 0 for this key type. These are also opaque to btrfs, we're blindly + * storing whatever fsverity sends down. + */ +#define BTRFS_VERITY_DESC_ITEM_KEY 36 +#define BTRFS_VERITY_MERKLE_ITEM_KEY 37 + #define BTRFS_ORPHAN_ITEM_KEY 48 /* reserve 2-15 close to the inode for later flexibility */ @@ -991,4 +1014,16 @@ struct btrfs_qgroup_limit_item { __le64 rsv_excl; } __attribute__ ((__packed__)); +struct btrfs_verity_descriptor_item { + /* Size of the verity descriptor in bytes */ + __le64 size; + /* + * When we implement support for fscrypt, we will need to encrypt the + * Merkle tree for encrypted verity files. These 128 bits are for the + * eventual storage of an fscrypt initialization vector. + */ + __le64 reserved[2]; + __u8 encryption; +} __attribute__ ((__packed__)); + #endif /* _BTRFS_CTREE_H_ */ diff --git a/src/basic/linux/can/netlink.h b/src/basic/linux/can/netlink.h index f730d443b..75b85c60e 100644 --- a/src/basic/linux/can/netlink.h +++ b/src/basic/linux/can/netlink.h @@ -101,6 +101,8 @@ struct can_ctrlmode { #define CAN_CTRLMODE_PRESUME_ACK 0x40 /* Ignore missing CAN ACKs */ #define CAN_CTRLMODE_FD_NON_ISO 0x80 /* CAN FD in non-ISO mode */ #define CAN_CTRLMODE_CC_LEN8_DLC 0x100 /* Classic CAN DLC option */ +#define CAN_CTRLMODE_TDC_AUTO 0x200 /* CAN transiver automatically calculates TDCV */ +#define CAN_CTRLMODE_TDC_MANUAL 0x400 /* TDCV is manually set up by user */ /* * CAN device statistics @@ -134,10 +136,35 @@ enum { IFLA_CAN_BITRATE_CONST, IFLA_CAN_DATA_BITRATE_CONST, IFLA_CAN_BITRATE_MAX, - __IFLA_CAN_MAX + IFLA_CAN_TDC, + + /* add new constants above here */ + __IFLA_CAN_MAX, + IFLA_CAN_MAX = __IFLA_CAN_MAX - 1 }; -#define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1) +/* + * CAN FD Transmitter Delay Compensation (TDC) + * + * Please refer to struct can_tdc_const and can_tdc in + * include/linux/can/bittiming.h for further details. + */ +enum { + IFLA_CAN_TDC_UNSPEC, + IFLA_CAN_TDC_TDCV_MIN, /* u32 */ + IFLA_CAN_TDC_TDCV_MAX, /* u32 */ + IFLA_CAN_TDC_TDCO_MIN, /* u32 */ + IFLA_CAN_TDC_TDCO_MAX, /* u32 */ + IFLA_CAN_TDC_TDCF_MIN, /* u32 */ + IFLA_CAN_TDC_TDCF_MAX, /* u32 */ + IFLA_CAN_TDC_TDCV, /* u32 */ + IFLA_CAN_TDC_TDCO, /* u32 */ + IFLA_CAN_TDC_TDCF, /* u32 */ + + /* add new constants above here */ + __IFLA_CAN_TDC, + IFLA_CAN_TDC_MAX = __IFLA_CAN_TDC - 1 +}; /* u16 termination range: 1..65535 Ohms */ #define CAN_TERMINATION_DISABLED 0 diff --git a/src/basic/linux/if_bridge.h b/src/basic/linux/if_bridge.h index 6b56a7549..2711c3522 100644 --- a/src/basic/linux/if_bridge.h +++ b/src/basic/linux/if_bridge.h @@ -479,16 +479,22 @@ enum { /* flags used in BRIDGE_VLANDB_DUMP_FLAGS attribute to affect dumps */ #define BRIDGE_VLANDB_DUMPF_STATS (1 << 0) /* Include stats in the dump */ +#define BRIDGE_VLANDB_DUMPF_GLOBAL (1 << 1) /* Dump global vlan options only */ /* Bridge vlan RTM attributes * [BRIDGE_VLANDB_ENTRY] = { * [BRIDGE_VLANDB_ENTRY_INFO] * ... * } + * [BRIDGE_VLANDB_GLOBAL_OPTIONS] = { + * [BRIDGE_VLANDB_GOPTS_ID] + * ... + * } */ enum { BRIDGE_VLANDB_UNSPEC, BRIDGE_VLANDB_ENTRY, + BRIDGE_VLANDB_GLOBAL_OPTIONS, __BRIDGE_VLANDB_MAX, }; #define BRIDGE_VLANDB_MAX (__BRIDGE_VLANDB_MAX - 1) @@ -500,6 +506,7 @@ enum { BRIDGE_VLANDB_ENTRY_STATE, BRIDGE_VLANDB_ENTRY_TUNNEL_INFO, BRIDGE_VLANDB_ENTRY_STATS, + BRIDGE_VLANDB_ENTRY_MCAST_ROUTER, __BRIDGE_VLANDB_ENTRY_MAX, }; #define BRIDGE_VLANDB_ENTRY_MAX (__BRIDGE_VLANDB_ENTRY_MAX - 1) @@ -538,6 +545,29 @@ enum { }; #define BRIDGE_VLANDB_STATS_MAX (__BRIDGE_VLANDB_STATS_MAX - 1) +enum { + BRIDGE_VLANDB_GOPTS_UNSPEC, + BRIDGE_VLANDB_GOPTS_ID, + BRIDGE_VLANDB_GOPTS_RANGE, + BRIDGE_VLANDB_GOPTS_MCAST_SNOOPING, + BRIDGE_VLANDB_GOPTS_MCAST_IGMP_VERSION, + BRIDGE_VLANDB_GOPTS_MCAST_MLD_VERSION, + BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_CNT, + BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_CNT, + BRIDGE_VLANDB_GOPTS_MCAST_LAST_MEMBER_INTVL, + BRIDGE_VLANDB_GOPTS_PAD, + BRIDGE_VLANDB_GOPTS_MCAST_MEMBERSHIP_INTVL, + BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_INTVL, + BRIDGE_VLANDB_GOPTS_MCAST_QUERY_INTVL, + BRIDGE_VLANDB_GOPTS_MCAST_QUERY_RESPONSE_INTVL, + BRIDGE_VLANDB_GOPTS_MCAST_STARTUP_QUERY_INTVL, + BRIDGE_VLANDB_GOPTS_MCAST_QUERIER, + BRIDGE_VLANDB_GOPTS_MCAST_ROUTER_PORTS, + BRIDGE_VLANDB_GOPTS_MCAST_QUERIER_STATE, + __BRIDGE_VLANDB_GOPTS_MAX +}; +#define BRIDGE_VLANDB_GOPTS_MAX (__BRIDGE_VLANDB_GOPTS_MAX - 1) + /* Bridge multicast database attributes * [MDBA_MDB] = { * [MDBA_MDB_ENTRY] = { @@ -629,6 +659,7 @@ enum { MDBA_ROUTER_PATTR_TYPE, MDBA_ROUTER_PATTR_INET_TIMER, MDBA_ROUTER_PATTR_INET6_TIMER, + MDBA_ROUTER_PATTR_VID, __MDBA_ROUTER_PATTR_MAX }; #define MDBA_ROUTER_PATTR_MAX (__MDBA_ROUTER_PATTR_MAX - 1) @@ -720,12 +751,14 @@ struct br_mcast_stats { /* bridge boolean options * BR_BOOLOPT_NO_LL_LEARN - disable learning from link-local packets + * BR_BOOLOPT_MCAST_VLAN_SNOOPING - control vlan multicast snooping * * IMPORTANT: if adding a new option do not forget to handle * it in br_boolopt_toggle/get and bridge sysfs */ enum br_boolopt_id { BR_BOOLOPT_NO_LL_LEARN, + BR_BOOLOPT_MCAST_VLAN_SNOOPING, BR_BOOLOPT_MAX }; @@ -738,4 +771,17 @@ struct br_boolopt_multi { __u32 optval; __u32 optmask; }; + +enum { + BRIDGE_QUERIER_UNSPEC, + BRIDGE_QUERIER_IP_ADDRESS, + BRIDGE_QUERIER_IP_PORT, + BRIDGE_QUERIER_IP_OTHER_TIMER, + BRIDGE_QUERIER_PAD, + BRIDGE_QUERIER_IPV6_ADDRESS, + BRIDGE_QUERIER_IPV6_PORT, + BRIDGE_QUERIER_IPV6_OTHER_TIMER, + __BRIDGE_QUERIER_MAX +}; +#define BRIDGE_QUERIER_MAX (__BRIDGE_QUERIER_MAX - 1) #endif /* _UAPI_LINUX_IF_BRIDGE_H */ diff --git a/src/basic/linux/if_ether.h b/src/basic/linux/if_ether.h index a0b637911..c0c2f3ed5 100644 --- a/src/basic/linux/if_ether.h +++ b/src/basic/linux/if_ether.h @@ -86,6 +86,7 @@ * over Ethernet */ #define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#define ETH_P_REALTEK 0x8899 /* Multiple proprietary protocols */ #define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ #define ETH_P_8021AD 0x88A8 /* 802.1ad Service VLAN */ #define ETH_P_802_EX1 0x88B5 /* 802.1 Local Experimental 1. */ @@ -116,7 +117,7 @@ #define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */ #define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */ -#define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is less than this value +#define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is more than this value * then the frame is Ethernet II. Else it is 802.3 */ /* @@ -151,6 +152,9 @@ #define ETH_P_MAP 0x00F9 /* Qualcomm multiplexing and * aggregation protocol */ +#define ETH_P_MCTP 0x00FA /* Management component transport + * protocol packets + */ /* * This is an Ethernet frame header. diff --git a/src/basic/linux/if_link.h b/src/basic/linux/if_link.h index 4882e8151..eebd3894f 100644 --- a/src/basic/linux/if_link.h +++ b/src/basic/linux/if_link.h @@ -417,6 +417,7 @@ enum { IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */ IFLA_INET6_TOKEN, /* device token */ IFLA_INET6_ADDR_GEN_MODE, /* implicit address generator mode */ + IFLA_INET6_RA_MTU, /* mtu carried in the RA message */ __IFLA_INET6_MAX }; @@ -479,6 +480,7 @@ enum { IFLA_BR_MCAST_MLD_VERSION, IFLA_BR_VLAN_STATS_PER_PORT, IFLA_BR_MULTI_BOOLOPT, + IFLA_BR_MCAST_QUERIER_STATE, __IFLA_BR_MAX, }; @@ -855,6 +857,7 @@ enum { IFLA_BOND_AD_ACTOR_SYSTEM, IFLA_BOND_TLB_DYNAMIC_LB, IFLA_BOND_PEER_NOTIF_DELAY, + IFLA_BOND_AD_LACP_ACTIVE, __IFLA_BOND_MAX, }; @@ -1260,4 +1263,14 @@ struct ifla_rmnet_flags { __u32 mask; }; +/* MCTP section */ + +enum { + IFLA_MCTP_UNSPEC, + IFLA_MCTP_NET, + __IFLA_MCTP_MAX, +}; + +#define IFLA_MCTP_MAX (__IFLA_MCTP_MAX - 1) + #endif /* _UAPI_LINUX_IF_LINK_H */ diff --git a/src/basic/linux/in.h b/src/basic/linux/in.h index d1b327036..14168225c 100644 --- a/src/basic/linux/in.h +++ b/src/basic/linux/in.h @@ -188,11 +188,22 @@ struct ip_mreq_source { }; struct ip_msfilter { - __be32 imsf_multiaddr; - __be32 imsf_interface; - __u32 imsf_fmode; - __u32 imsf_numsrc; - __be32 imsf_slist[1]; + union { + struct { + __be32 imsf_multiaddr_aux; + __be32 imsf_interface_aux; + __u32 imsf_fmode_aux; + __u32 imsf_numsrc_aux; + __be32 imsf_slist[1]; + }; + struct { + __be32 imsf_multiaddr; + __be32 imsf_interface; + __u32 imsf_fmode; + __u32 imsf_numsrc; + __be32 imsf_slist_flex[]; + }; + }; }; #define IP_MSFILTER_SIZE(numsrc) \ @@ -211,11 +222,22 @@ struct group_source_req { }; struct group_filter { - __u32 gf_interface; /* interface index */ - struct __kernel_sockaddr_storage gf_group; /* multicast address */ - __u32 gf_fmode; /* filter mode */ - __u32 gf_numsrc; /* number of sources */ - struct __kernel_sockaddr_storage gf_slist[1]; /* interface index */ + union { + struct { + __u32 gf_interface_aux; /* interface index */ + struct __kernel_sockaddr_storage gf_group_aux; /* multicast address */ + __u32 gf_fmode_aux; /* filter mode */ + __u32 gf_numsrc_aux; /* number of sources */ + struct __kernel_sockaddr_storage gf_slist[1]; /* interface index */ + }; + struct { + __u32 gf_interface; /* interface index */ + struct __kernel_sockaddr_storage gf_group; /* multicast address */ + __u32 gf_fmode; /* filter mode */ + __u32 gf_numsrc; /* number of sources */ + struct __kernel_sockaddr_storage gf_slist_flex[]; /* interface index */ + }; + }; }; #define GROUP_FILTER_SIZE(numsrc) \ diff --git a/src/basic/linux/in6.h b/src/basic/linux/in6.h index 5ad396a57..c4c53a9ab 100644 --- a/src/basic/linux/in6.h +++ b/src/basic/linux/in6.h @@ -145,6 +145,7 @@ struct in6_flowlabel_req { #define IPV6_TLV_PADN 1 #define IPV6_TLV_ROUTERALERT 5 #define IPV6_TLV_CALIPSO 7 /* RFC 5570 */ +#define IPV6_TLV_IOAM 49 /* TEMPORARY IANA allocation for IOAM */ #define IPV6_TLV_JUMBO 194 #define IPV6_TLV_HAO 201 /* home address option */ diff --git a/src/basic/linux/netfilter/nf_tables.h b/src/basic/linux/netfilter/nf_tables.h index e94d1fa55..466fd3f44 100644 --- a/src/basic/linux/netfilter/nf_tables.h +++ b/src/basic/linux/netfilter/nf_tables.h @@ -753,11 +753,13 @@ enum nft_dynset_attributes { * @NFT_PAYLOAD_LL_HEADER: link layer header * @NFT_PAYLOAD_NETWORK_HEADER: network header * @NFT_PAYLOAD_TRANSPORT_HEADER: transport header + * @NFT_PAYLOAD_INNER_HEADER: inner header / payload */ enum nft_payload_bases { NFT_PAYLOAD_LL_HEADER, NFT_PAYLOAD_NETWORK_HEADER, NFT_PAYLOAD_TRANSPORT_HEADER, + NFT_PAYLOAD_INNER_HEADER, }; /** @@ -896,7 +898,8 @@ enum nft_meta_keys { NFT_META_OIF, NFT_META_IIFNAME, NFT_META_OIFNAME, - NFT_META_IIFTYPE, + NFT_META_IFTYPE, +#define NFT_META_IIFTYPE NFT_META_IFTYPE NFT_META_OIFTYPE, NFT_META_SKUID, NFT_META_SKGID, @@ -923,6 +926,7 @@ enum nft_meta_keys { NFT_META_TIME_HOUR, NFT_META_SDIF, NFT_META_SDIFNAME, + __NFT_META_IIFTYPE, }; /** diff --git a/src/basic/linux/nl80211.h b/src/basic/linux/nl80211.h index c2efea98e..61cab81e9 100644 --- a/src/basic/linux/nl80211.h +++ b/src/basic/linux/nl80211.h @@ -300,6 +300,29 @@ * the interface goes down. */ +/** + * DOC: FILS shared key crypto offload + * + * This feature is applicable to drivers running in AP mode. + * + * FILS shared key crypto offload can be advertised by drivers by setting + * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD flag. The drivers that support + * FILS shared key crypto offload should be able to encrypt and decrypt + * association frames for FILS shared key authentication as per IEEE 802.11ai. + * With this capability, for FILS key derivation, drivers depend on userspace. + * + * After FILS key derivation, userspace shares the FILS AAD details with the + * driver and the driver stores the same to use in decryption of association + * request and in encryption of association response. The below parameters + * should be given to the driver in %NL80211_CMD_SET_FILS_AAD. + * %NL80211_ATTR_MAC - STA MAC address, used for storing FILS AAD per STA + * %NL80211_ATTR_FILS_KEK - Used for encryption or decryption + * %NL80211_ATTR_FILS_NONCES - Used for encryption or decryption + * (STA Nonce 16 bytes followed by AP Nonce 16 bytes) + * + * Once the association is done, the driver cleans the FILS AAD data. + */ + /** * enum nl80211_commands - supported nl80211 commands * @@ -337,7 +360,10 @@ * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from * userspace to request deletion of a virtual interface, then requires - * attribute %NL80211_ATTR_IFINDEX. + * attribute %NL80211_ATTR_IFINDEX. If multiple BSSID advertisements are + * enabled using %NL80211_ATTR_MBSSID_CONFIG, %NL80211_ATTR_MBSSID_ELEMS, + * and if this command is used for the transmitting interface, then all + * the non-transmitting interfaces are deleted as well. * * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. @@ -1200,6 +1226,12 @@ * @NL80211_CMD_COLOR_CHANGE_COMPLETED: Notify userland that the color change * has completed * + * @NL80211_CMD_SET_FILS_AAD: Set FILS AAD data to the driver using - + * &NL80211_ATTR_MAC - for STA MAC address + * &NL80211_ATTR_FILS_KEK - for KEK + * &NL80211_ATTR_FILS_NONCES - for FILS Nonces + * (STA Nonce 16 bytes followed by AP Nonce 16 bytes) + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -1440,6 +1472,8 @@ enum nl80211_commands { NL80211_CMD_COLOR_CHANGE_ABORTED, NL80211_CMD_COLOR_CHANGE_COMPLETED, + NL80211_CMD_SET_FILS_AAD, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -2593,6 +2627,18 @@ enum nl80211_commands { * @NL80211_ATTR_COLOR_CHANGE_ELEMS: Nested set of attributes containing the IE * information for the time while performing a color switch. * + * @NL80211_ATTR_MBSSID_CONFIG: Nested attribute for multiple BSSID + * advertisements (MBSSID) parameters in AP mode. + * Kernel uses this attribute to indicate the driver's support for MBSSID + * and enhanced multi-BSSID advertisements (EMA AP) to the userspace. + * Userspace should use this attribute to configure per interface MBSSID + * parameters. + * See &enum nl80211_mbssid_config_attributes for details. + * + * @NL80211_ATTR_MBSSID_ELEMS: Nested parameter to pass multiple BSSID elements. + * Mandatory parameter for the transmitting interface to enable MBSSID. + * Optional for the non-transmitting interfaces. + * * @NUM_NL80211_ATTR: total number of nl80211_attrs available * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use @@ -3096,6 +3142,9 @@ enum nl80211_attrs { NL80211_ATTR_COLOR_CHANGE_COLOR, NL80211_ATTR_COLOR_CHANGE_ELEMS, + NL80211_ATTR_MBSSID_CONFIG, + NL80211_ATTR_MBSSID_ELEMS, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -4929,6 +4978,7 @@ enum nl80211_txrate_gi { * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 69.12 GHz) * @NL80211_BAND_6GHZ: around 6 GHz band (5.9 - 7.2 GHz) * @NL80211_BAND_S1GHZ: around 900MHz, supported by S1G PHYs + * @NL80211_BAND_LC: light communication band (placeholder) * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace * since newer kernel versions may support more bands */ @@ -4938,6 +4988,7 @@ enum nl80211_band { NL80211_BAND_60GHZ, NL80211_BAND_6GHZ, NL80211_BAND_S1GHZ, + NL80211_BAND_LC, NUM_NL80211_BANDS, }; @@ -5995,6 +6046,11 @@ enum nl80211_feature_flags { * @NL80211_EXT_FEATURE_BSS_COLOR: The driver supports BSS color collision * detection and change announcemnts. * + * @NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD: Driver running in AP mode supports + * FILS encryption and decryption for (Re)Association Request and Response + * frames. Userspace has to share FILS AAD details to the driver by using + * @NL80211_CMD_SET_FILS_AAD. + * * @NUM_NL80211_EXT_FEATURES: number of extended features. * @MAX_NL80211_EXT_FEATURES: highest extended feature index. */ @@ -6060,6 +6116,7 @@ enum nl80211_ext_feature_index { NL80211_EXT_FEATURE_SECURE_RTT, NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE, NL80211_EXT_FEATURE_BSS_COLOR, + NL80211_EXT_FEATURE_FILS_CRYPTO_OFFLOAD, /* add new features before the definition below */ NUM_NL80211_EXT_FEATURES, @@ -7349,4 +7406,60 @@ enum nl80211_sar_specs_attrs { NL80211_SAR_ATTR_SPECS_MAX = __NL80211_SAR_ATTR_SPECS_LAST - 1, }; +/** + * enum nl80211_mbssid_config_attributes - multiple BSSID (MBSSID) and enhanced + * multi-BSSID advertisements (EMA) in AP mode. + * Kernel uses some of these attributes to advertise driver's support for + * MBSSID and EMA. + * Remaining attributes should be used by the userspace to configure the + * features. + * + * @__NL80211_MBSSID_CONFIG_ATTR_INVALID: Invalid + * + * @NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES: Used by the kernel to advertise + * the maximum number of MBSSID interfaces supported by the driver. + * Driver should indicate MBSSID support by setting + * wiphy->mbssid_max_interfaces to a value more than or equal to 2. + * + * @NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY: Used by the kernel + * to advertise the maximum profile periodicity supported by the driver + * if EMA is enabled. Driver should indicate EMA support to the userspace + * by setting wiphy->ema_max_profile_periodicity to + * a non-zero value. + * + * @NL80211_MBSSID_CONFIG_ATTR_INDEX: Mandatory parameter to pass the index of + * this BSS (u8) in the multiple BSSID set. + * Value must be set to 0 for the transmitting interface and non-zero for + * all non-transmitting interfaces. The userspace will be responsible + * for using unique indices for the interfaces. + * Range: 0 to wiphy->mbssid_max_interfaces-1. + * + * @NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX: Mandatory parameter for + * a non-transmitted profile which provides the interface index (u32) of + * the transmitted profile. The value must match one of the interface + * indices advertised by the kernel. Optional if the interface being set up + * is the transmitting one, however, if provided then the value must match + * the interface index of the same. + * + * @NL80211_MBSSID_CONFIG_ATTR_EMA: Flag used to enable EMA AP feature. + * Setting this flag is permitted only if the driver advertises EMA support + * by setting wiphy->ema_max_profile_periodicity to non-zero. + * + * @__NL80211_MBSSID_CONFIG_ATTR_LAST: Internal + * @NL80211_MBSSID_CONFIG_ATTR_MAX: highest attribute + */ +enum nl80211_mbssid_config_attributes { + __NL80211_MBSSID_CONFIG_ATTR_INVALID, + + NL80211_MBSSID_CONFIG_ATTR_MAX_INTERFACES, + NL80211_MBSSID_CONFIG_ATTR_MAX_EMA_PROFILE_PERIODICITY, + NL80211_MBSSID_CONFIG_ATTR_INDEX, + NL80211_MBSSID_CONFIG_ATTR_TX_IFINDEX, + NL80211_MBSSID_CONFIG_ATTR_EMA, + + /* keep last */ + __NL80211_MBSSID_CONFIG_ATTR_LAST, + NL80211_MBSSID_CONFIG_ATTR_MAX = __NL80211_MBSSID_CONFIG_ATTR_LAST - 1, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/src/basic/linux/pkt_sched.h b/src/basic/linux/pkt_sched.h index 79a699f10..f292b467b 100644 --- a/src/basic/linux/pkt_sched.h +++ b/src/basic/linux/pkt_sched.h @@ -827,6 +827,8 @@ struct tc_codel_xstats { /* FQ_CODEL */ +#define FQ_CODEL_QUANTUM_MAX (1 << 20) + enum { TCA_FQ_CODEL_UNSPEC, TCA_FQ_CODEL_TARGET, @@ -838,6 +840,8 @@ enum { TCA_FQ_CODEL_CE_THRESHOLD, TCA_FQ_CODEL_DROP_BATCH_SIZE, TCA_FQ_CODEL_MEMORY_LIMIT, + TCA_FQ_CODEL_CE_THRESHOLD_SELECTOR, + TCA_FQ_CODEL_CE_THRESHOLD_MASK, __TCA_FQ_CODEL_MAX }; diff --git a/src/basic/list.h b/src/basic/list.h index effee0e43..58e83a6cb 100644 --- a/src/basic/list.h +++ b/src/basic/list.h @@ -134,35 +134,41 @@ } while (false) #define LIST_JUST_US(name,item) \ - (!(item)->name##_prev && !(item)->name##_next) \ + (!(item)->name##_prev && !(item)->name##_next) + +/* The type of the iterator 'i' is automatically determined by the type of 'head', and declared in the + * loop. Hence, do not declare the same variable in the outer scope. Sometimes, we set 'head' through + * hashmap_get(). In that case, you need to explicitly cast the result. */ +#define LIST_FOREACH_WITH_NEXT(name,i,n,head) \ + for (typeof(*(head)) *n, *i = (head); i && (n = i->name##_next, true); i = n) #define LIST_FOREACH(name,i,head) \ - for ((i) = (head); (i); (i) = (i)->name##_next) + LIST_FOREACH_WITH_NEXT(name, i, UNIQ_T(n, UNIQ), head) -#define LIST_FOREACH_SAFE(name,i,n,head) \ - for ((i) = (head); (i) && (((n) = (i)->name##_next), 1); (i) = (n)) +#define _LIST_FOREACH_WITH_PREV(name,i,p,start) \ + for (typeof(*(start)) *p, *i = (start); i && (p = i->name##_prev, true); i = p) -#define LIST_FOREACH_BACKWARDS(name,i,p) \ - for ((i) = (p); (i); (i) = (i)->name##_prev) +#define LIST_FOREACH_BACKWARDS(name,i,start) \ + _LIST_FOREACH_WITH_PREV(name, i, UNIQ_T(p, UNIQ), start) /* Iterate through all the members of the list p is included in, but skip over p */ #define LIST_FOREACH_OTHERS(name,i,p) \ - for (({ \ - (i) = (p); \ - while ((i) && (i)->name##_prev) \ - (i) = (i)->name##_prev; \ - if ((i) == (p)) \ - (i) = (p)->name##_next; \ - }); \ - (i); \ - (i) = (i)->name##_next == (p) ? (p)->name##_next : (i)->name##_next) + for (typeof(*(p)) *_p = (p), *i = ({ \ + typeof(*_p) *_j = _p; \ + while (_j && _j->name##_prev) \ + _j = _j->name##_prev; \ + if (_j == _p) \ + _j = _p->name##_next; \ + _j; \ + }); \ + i; \ + i = i->name##_next == _p ? _p->name##_next : i->name##_next) -/* Loop starting from p->next until p->prev. - p can be adjusted meanwhile. */ +/* Loop starting from p->next until p->prev. p can be adjusted meanwhile. */ #define LIST_LOOP_BUT_ONE(name,i,head,p) \ - for ((i) = (p)->name##_next ? (p)->name##_next : (head); \ - (i) != (p); \ - (i) = (i)->name##_next ? (i)->name##_next : (head)) + for (typeof(*(p)) *i = (p)->name##_next ? (p)->name##_next : (head); \ + i != (p); \ + i = i->name##_next ? i->name##_next : (head)) #define LIST_IS_EMPTY(head) \ (!(head)) diff --git a/src/basic/log.h b/src/basic/log.h index 1e2bec164..0d927bfce 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -273,9 +273,11 @@ int log_emergency_level(void); }) #if LOG_TRACE -# define log_trace(...) log_debug(__VA_ARGS__) +# define log_trace(...) log_debug(__VA_ARGS__) +# define log_trace_errno(...) log_debug_errno(__VA_ARGS__) #else -# define log_trace(...) do {} while (0) +# define log_trace(...) do {} while (0) +# define log_trace_errno(e, ...) (-ERRNO_VALUE(e)) #endif /* Structured logging */ diff --git a/src/basic/macro.h b/src/basic/macro.h index aa04039e8..68d8b062e 100644 --- a/src/basic/macro.h +++ b/src/basic/macro.h @@ -309,23 +309,32 @@ static inline int __coverity_check_and_return__(int condition) { #define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member) -/* Returns the number of chars needed to format variables of the - * specified type as a decimal string. Adds in extra space for a - * negative '-' prefix (hence works correctly on signed - * types). Includes space for the trailing NUL. */ +/* Returns the number of chars needed to format variables of the specified type as a decimal string. Adds in + * extra space for a negative '-' prefix for signed types. Includes space for the trailing NUL. */ #define DECIMAL_STR_MAX(type) \ - (2U+(sizeof(type) <= 1 ? 3U : \ + ((size_t) IS_SIGNED_INTEGER_TYPE(type) + 1U + \ + (sizeof(type) <= 1 ? 3U : \ sizeof(type) <= 2 ? 5U : \ sizeof(type) <= 4 ? 10U : \ - sizeof(type) <= 8 ? 20U : sizeof(int[-2*(sizeof(type) > 8)]))) + sizeof(type) <= 8 ? (IS_SIGNED_INTEGER_TYPE(type) ? 19U : 20U) : sizeof(int[-2*(sizeof(type) > 8)]))) -#define DECIMAL_STR_WIDTH(x) \ - ({ \ - typeof(x) _x_ = (x); \ - size_t ans = 1; \ - while ((_x_ /= 10) != 0) \ - ans++; \ - ans; \ +/* Returns the number of chars needed to format the specified integer value. It's hence more specific than + * DECIMAL_STR_MAX() which answers the same question for all possible values of the specified type. Does + * *not* include space for a trailing NUL. (If you wonder why we special case _x_ == 0 here: it's to trick + * out gcc's -Wtype-limits, which would complain on comparing an unsigned type with < 0, otherwise. By + * special-casing == 0 here first, we can use <= 0 instead of < 0 to trick out gcc.) */ +#define DECIMAL_STR_WIDTH(x) \ + ({ \ + typeof(x) _x_ = (x); \ + size_t ans; \ + if (_x_ == 0) \ + ans = 1; \ + else { \ + ans = _x_ <= 0 ? 2 : 1; \ + while ((_x_ /= 10) != 0) \ + ans++; \ + } \ + ans; \ }) #define SWAP_TWO(x, y) do { \ @@ -454,4 +463,20 @@ typedef struct { assert_cc(sizeof(dummy_t) == 0); +/* A little helper for subtracting 1 off a pointer in a safe UB-free way. This is intended to be used for for + * loops that count down from a high pointer until some base. A naive loop would implement this like this: + * + * for (p = end-1; p >= base; p--) … + * + * But this is not safe because p before the base is UB in C. With this macro the loop becomes this instead: + * + * for (p = PTR_SUB1(end, base); p; p = PTR_SUB1(p, base)) … + * + * And is free from UB! */ +#define PTR_SUB1(p, base) \ + ({ \ + typeof(p) _q = (p); \ + _q && _q > (base) ? &_q[-1] : NULL; \ + }) + #include "log.h" diff --git a/src/basic/memory-util.h b/src/basic/memory-util.h index 9f37431fc..6e3280b9d 100644 --- a/src/basic/memory-util.h +++ b/src/basic/memory-util.h @@ -15,7 +15,7 @@ size_t page_size(void) _pure_; #define PAGE_ALIGN_DOWN(l) ((l) & ~(page_size() - 1)) #define PAGE_OFFSET(l) ((l) & (page_size() - 1)) -/* Normal memcpy requires src to be nonnull. We do nothing if n is 0. */ +/* Normal memcpy() requires src to be nonnull. We do nothing if n is 0. */ static inline void *memcpy_safe(void *dst, const void *src, size_t n) { if (n == 0) return dst; @@ -23,7 +23,15 @@ static inline void *memcpy_safe(void *dst, const void *src, size_t n) { return memcpy(dst, src, n); } -/* Normal memcmp requires s1 and s2 to be nonnull. We do nothing if n is 0. */ +/* Normal mempcpy() requires src to be nonnull. We do nothing if n is 0. */ +static inline void *mempcpy_safe(void *dst, const void *src, size_t n) { + if (n == 0) + return dst; + assert(src); + return mempcpy(dst, src, n); +} + +/* Normal memcmp() requires s1 and s2 to be nonnull. We do nothing if n is 0. */ static inline int memcmp_safe(const void *s1, const void *s2, size_t n) { if (n == 0) return 0; diff --git a/src/basic/meson.build b/src/basic/meson.build index 49e1e7f43..92a4df201 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -383,7 +383,7 @@ filesystem_includes = ['linux/magic.h', 'linux/gfs2_ondisk.h'] check_filesystems = find_program('check-filesystems.sh') -r = run_command([check_filesystems, cpp, 'filesystems-gperf.gperf'] + filesystem_includes) +r = run_command([check_filesystems, cpp, 'filesystems-gperf.gperf'] + filesystem_includes, check: false) if r.returncode() != 0 error('found unknown filesystem(s) defined in kernel headers:\n\n' + r.stdout()) r.stdout() diff --git a/src/basic/missing_syscall.h b/src/basic/missing_syscall.h index 8267b1a90..793d111c5 100644 --- a/src/basic/missing_syscall.h +++ b/src/basic/missing_syscall.h @@ -569,6 +569,10 @@ static inline int missing_open_tree( #define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */ #endif +#ifndef MOVE_MOUNT_T_EMPTY_PATH +#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */ +#endif + static inline int missing_move_mount( int from_dfd, const char *from_pathname, diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index 51a0d74e8..d2c6b96a3 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -51,6 +51,9 @@ int mkdir_safe_internal( _mkdirat); } + if (flags & MKDIR_IGNORE_EXISTING) + return 0; + if (!S_ISDIR(st.st_mode)) return log_full_errno(flags & MKDIR_WARN_MODE ? LOG_WARNING : LOG_DEBUG, SYNTHETIC_ERRNO(ENOTDIR), "Path \"%s\" already exists and is not a directory, refusing.", path); @@ -138,7 +141,7 @@ int mkdir_parents_internal(const char *prefix, const char *path, mode_t mode, ui s[n] = '\0'; if (!prefix || !path_startswith_full(prefix, path, /* accept_dot_dot= */ false)) { - r = mkdir_safe_internal(path, mode, uid, gid, flags, _mkdirat); + r = mkdir_safe_internal(path, mode, uid, gid, flags | MKDIR_IGNORE_EXISTING, _mkdirat); if (r < 0 && r != -EEXIST) return r; } diff --git a/src/basic/mkdir.h b/src/basic/mkdir.h index 34a522757..c0c0ea6c4 100644 --- a/src/basic/mkdir.h +++ b/src/basic/mkdir.h @@ -4,8 +4,9 @@ #include typedef enum MkdirFlags { - MKDIR_FOLLOW_SYMLINK = 1 << 0, - MKDIR_WARN_MODE = 1 << 1, + MKDIR_FOLLOW_SYMLINK = 1 << 0, + MKDIR_IGNORE_EXISTING = 1 << 1, /* Quietly accept a preexisting directory (or file) */ + MKDIR_WARN_MODE = 1 << 2, /* Log at LOG_WARNING when mode doesn't match */ } MkdirFlags; int mkdirat_errno_wrapper(int dirfd, const char *pathname, mode_t mode); diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 82a33a682..2e451085b 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -298,10 +298,8 @@ fallback_fstat: if (fstatat(fd, "", &b, AT_EMPTY_PATH) < 0) return -errno; - /* A directory with same device and inode as its parent? Must - * be the root directory */ - if (a.st_dev == b.st_dev && - a.st_ino == b.st_ino) + /* A directory with same device and inode as its parent? Must be the root directory */ + if (stat_inode_same(&a, &b)) return 1; return check_st_dev && (a.st_dev != b.st_dev); diff --git a/src/basic/ordered-set.c b/src/basic/ordered-set.c index 0a76f0456..b4c258839 100644 --- a/src/basic/ordered-set.c +++ b/src/basic/ordered-set.c @@ -58,7 +58,6 @@ int _ordered_set_put_strdup(OrderedSet **s, const char *p HASHMAP_DEBUG_PARAMS) int _ordered_set_put_strdupv(OrderedSet **s, char **l HASHMAP_DEBUG_PARAMS) { int n = 0, r; - char **i; STRV_FOREACH(i, l) { r = _ordered_set_put_strdup(s, *i HASHMAP_DEBUG_PASS_ARGS); diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h index 3ee47350b..c0650e015 100644 --- a/src/basic/ordered-set.h +++ b/src/basic/ordered-set.h @@ -18,6 +18,14 @@ int _ordered_set_ensure_allocated(OrderedSet **s, const struct hash_ops *ops HA int _ordered_set_ensure_put(OrderedSet **s, const struct hash_ops *ops, void *p HASHMAP_DEBUG_PARAMS); #define ordered_set_ensure_put(s, hash_ops, key) _ordered_set_ensure_put(s, hash_ops, key HASHMAP_DEBUG_SRC_ARGS) +static inline void ordered_set_clear(OrderedSet *s) { + return ordered_hashmap_clear((OrderedHashmap*) s); +} + +static inline void ordered_set_clear_free(OrderedSet *s) { + return ordered_hashmap_clear_free((OrderedHashmap*) s); +} + static inline OrderedSet* ordered_set_free(OrderedSet *s) { return (OrderedSet*) ordered_hashmap_free((OrderedHashmap*) s); } diff --git a/src/basic/os-util.c b/src/basic/os-util.c index 75c8500e5..acfff2431 100644 --- a/src/basic/os-util.c +++ b/src/basic/os-util.c @@ -170,15 +170,19 @@ int open_extension_release(const char *root, const char *extension, char **ret_p } } } else { - const char *p; - - FOREACH_STRING(p, "/etc/os-release", "/usr/lib/os-release") { - r = chase_symlinks(p, root, CHASE_PREFIX_ROOT, + const char *var = secure_getenv("SYSTEMD_OS_RELEASE"); + if (var) + r = chase_symlinks(var, root, 0, ret_path ? &q : NULL, ret_fd ? &fd : NULL); - if (r != -ENOENT) - break; - } + else + FOREACH_STRING(path, "/etc/os-release", "/usr/lib/os-release") { + r = chase_symlinks(path, root, CHASE_PREFIX_ROOT, + ret_path ? &q : NULL, + ret_fd ? &fd : NULL); + if (r != -ENOENT) + break; + } } if (r < 0) return r; @@ -273,7 +277,6 @@ int load_os_release_pairs(const char *root, char ***ret) { int load_os_release_pairs_with_prefix(const char *root, const char *prefix, char ***ret) { _cleanup_strv_free_ char **os_release_pairs = NULL, **os_release_pairs_prefixed = NULL; - char **p, **q; int r; r = load_os_release_pairs(root, &os_release_pairs); diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 2888ab652..222b2304c 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -415,7 +415,7 @@ int safe_atoi(const char *s, int *ret_i) { return 0; } -int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu) { +int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu) { char *x = NULL; unsigned long long l; diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index 3dc5e140c..827312462 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -65,9 +65,9 @@ static inline int safe_atoi32(const char *s, int32_t *ret_i) { return safe_atoi(s, (int*) ret_i); } -int safe_atollu_full(const char *s, unsigned base, long long unsigned *ret_llu); +int safe_atollu_full(const char *s, unsigned base, unsigned long long *ret_llu); -static inline int safe_atollu(const char *s, long long unsigned *ret_llu) { +static inline int safe_atollu(const char *s, unsigned long long *ret_llu) { return safe_atollu_full(s, 0, ret_llu); } @@ -82,12 +82,12 @@ static inline int safe_atoi64(const char *s, int64_t *ret_i) { } static inline int safe_atoux64(const char *s, uint64_t *ret) { - assert_cc(sizeof(int64_t) == sizeof(long long unsigned)); - return safe_atollu_full(s, 16, (long long unsigned*) ret); + assert_cc(sizeof(int64_t) == sizeof(unsigned long long)); + return safe_atollu_full(s, 16, (unsigned long long*) ret); } #if LONG_MAX == INT_MAX -static inline int safe_atolu_full(const char *s, unsigned base, long unsigned *ret_u) { +static inline int safe_atolu_full(const char *s, unsigned base, unsigned long *ret_u) { assert_cc(sizeof(unsigned long) == sizeof(unsigned)); return safe_atou_full(s, base, (unsigned*) ret_u); } @@ -117,7 +117,7 @@ static inline int safe_atozu(const char *s, size_t *ret_u) { } #else static inline int safe_atozu(const char *s, size_t *ret_u) { - assert_cc(sizeof(size_t) == sizeof(long unsigned)); + assert_cc(sizeof(size_t) == sizeof(unsigned long)); return safe_atolu(s, ret_u); } #endif diff --git a/src/basic/path-lookup.c b/src/basic/path-lookup.c index 6fb8c40e7..1f4331a8b 100644 --- a/src/basic/path-lookup.c +++ b/src/basic/path-lookup.c @@ -232,36 +232,40 @@ bool path_is_user_config_dir(const char *path) { } static int acquire_generator_dirs( - UnitFileScope scope, + LookupScope scope, const char *tempdir, char **generator, char **generator_early, char **generator_late) { - _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL; + _cleanup_free_ char *x = NULL, *y = NULL, *z = NULL, *p = NULL; const char *prefix; assert(generator); assert(generator_early); assert(generator_late); - assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL)); + assert(IN_SET(scope, LOOKUP_SCOPE_SYSTEM, LOOKUP_SCOPE_USER, LOOKUP_SCOPE_GLOBAL)); - if (scope == UNIT_FILE_GLOBAL) + if (scope == LOOKUP_SCOPE_GLOBAL) return -EOPNOTSUPP; if (tempdir) prefix = tempdir; - else if (scope == UNIT_FILE_SYSTEM) + else if (scope == LOOKUP_SCOPE_SYSTEM) prefix = "/run/systemd"; else { - /* UNIT_FILE_USER */ + /* LOOKUP_SCOPE_USER */ const char *e; e = getenv("XDG_RUNTIME_DIR"); if (!e) return -ENXIO; - prefix = strjoina(e, "/systemd"); + p = path_join(e, "/systemd"); + if (!p) + return -ENOMEM; + + prefix = p; } x = path_join(prefix, "generator"); @@ -284,21 +288,21 @@ static int acquire_generator_dirs( } static int acquire_transient_dir( - UnitFileScope scope, + LookupScope scope, const char *tempdir, char **ret) { char *transient; assert(ret); - assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER, UNIT_FILE_GLOBAL)); + assert(IN_SET(scope, LOOKUP_SCOPE_SYSTEM, LOOKUP_SCOPE_USER, LOOKUP_SCOPE_GLOBAL)); - if (scope == UNIT_FILE_GLOBAL) + if (scope == LOOKUP_SCOPE_GLOBAL) return -EOPNOTSUPP; if (tempdir) transient = path_join(tempdir, "transient"); - else if (scope == UNIT_FILE_SYSTEM) + else if (scope == LOOKUP_SCOPE_SYSTEM) transient = strdup("/run/systemd/transient"); else return xdg_user_runtime_dir(ret, "/systemd/transient"); @@ -309,7 +313,7 @@ static int acquire_transient_dir( return 0; } -static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **runtime) { +static int acquire_config_dirs(LookupScope scope, char **persistent, char **runtime) { _cleanup_free_ char *a = NULL, *b = NULL; int r; @@ -318,17 +322,17 @@ static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **ru switch (scope) { - case UNIT_FILE_SYSTEM: + case LOOKUP_SCOPE_SYSTEM: a = strdup(SYSTEM_CONFIG_UNIT_DIR); b = strdup("/run/systemd/system"); break; - case UNIT_FILE_GLOBAL: + case LOOKUP_SCOPE_GLOBAL: a = strdup(USER_CONFIG_UNIT_DIR); b = strdup("/run/systemd/user"); break; - case UNIT_FILE_USER: + case LOOKUP_SCOPE_USER: r = xdg_user_config_dir(&a, "/systemd/user"); if (r < 0 && r != -ENXIO) return r; @@ -360,7 +364,7 @@ static int acquire_config_dirs(UnitFileScope scope, char **persistent, char **ru return 0; } -static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **runtime) { +static int acquire_control_dirs(LookupScope scope, char **persistent, char **runtime) { _cleanup_free_ char *a = NULL; int r; @@ -369,7 +373,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r switch (scope) { - case UNIT_FILE_SYSTEM: { + case LOOKUP_SCOPE_SYSTEM: { _cleanup_free_ char *b = NULL; a = strdup("/etc/systemd/system.control"); @@ -385,7 +389,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r break; } - case UNIT_FILE_USER: + case LOOKUP_SCOPE_USER: r = xdg_user_config_dir(&a, "/systemd/user.control"); if (r < 0 && r != -ENXIO) return r; @@ -402,7 +406,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r break; - case UNIT_FILE_GLOBAL: + case LOOKUP_SCOPE_GLOBAL: return -EOPNOTSUPP; default: @@ -415,7 +419,7 @@ static int acquire_control_dirs(UnitFileScope scope, char **persistent, char **r } static int acquire_attached_dirs( - UnitFileScope scope, + LookupScope scope, char **ret_persistent, char **ret_runtime) { @@ -425,7 +429,7 @@ static int acquire_attached_dirs( assert(ret_runtime); /* Portable services are not available to regular users for now. */ - if (scope != UNIT_FILE_SYSTEM) + if (scope != LOOKUP_SCOPE_SYSTEM) return -EOPNOTSUPP; a = strdup("/etc/systemd/system.attached"); @@ -459,7 +463,6 @@ static int patch_root_prefix(char **p, const char *root_dir) { } static int patch_root_prefix_strv(char **l, const char *root_dir) { - char **i; int r; if (!root_dir) @@ -505,8 +508,8 @@ static int get_paths_from_environ(const char *var, char ***paths, bool *append) } int lookup_paths_init( - LookupPaths *p, - UnitFileScope scope, + LookupPaths *lp, + LookupScope scope, LookupPathsFlags flags, const char *root_dir) { @@ -523,16 +526,16 @@ int lookup_paths_init( _cleanup_strv_free_ char **paths = NULL; int r; - assert(p); + assert(lp); assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); #if HAVE_SPLIT_USR flags |= LOOKUP_PATHS_SPLIT_USR; #endif if (!empty_or_root(root_dir)) { - if (scope == UNIT_FILE_USER) + if (scope == LOOKUP_SCOPE_USER) return -EINVAL; r = is_dir(root_dir, true); @@ -557,8 +560,8 @@ int lookup_paths_init( if (r < 0) return r; - if (scope == UNIT_FILE_USER) { - r = acquire_config_dirs(UNIT_FILE_GLOBAL, &global_persistent_config, &global_runtime_config); + if (scope == LOOKUP_SCOPE_USER) { + r = acquire_config_dirs(LOOKUP_SCOPE_GLOBAL, &global_persistent_config, &global_runtime_config); if (r < 0) return r; } @@ -603,7 +606,7 @@ int lookup_paths_init( switch (scope) { - case UNIT_FILE_SYSTEM: + case LOOKUP_SCOPE_SYSTEM: add = strv_new( /* If you modify this you also want to modify * systemdsystemunitpath= in systemd.pc.in! */ @@ -626,7 +629,7 @@ int lookup_paths_init( STRV_IFNOTNULL(generator_late)); break; - case UNIT_FILE_GLOBAL: + case LOOKUP_SCOPE_GLOBAL: add = strv_new( /* If you modify this you also want to modify * systemduserunitpath= in systemd.pc.in, and @@ -649,7 +652,7 @@ int lookup_paths_init( STRV_IFNOTNULL(generator_late)); break; - case UNIT_FILE_USER: + case LOOKUP_SCOPE_USER: add = user_dirs(persistent_config, runtime_config, global_persistent_config, global_runtime_config, generator, generator_early, generator_late, @@ -713,7 +716,7 @@ int lookup_paths_init( if (r < 0) return -ENOMEM; - *p = (LookupPaths) { + *lp = (LookupPaths) { .search_path = strv_uniq(TAKE_PTR(paths)), .persistent_config = TAKE_PTR(persistent_config), @@ -738,46 +741,56 @@ int lookup_paths_init( return 0; } -void lookup_paths_free(LookupPaths *p) { - if (!p) - return; +int lookup_paths_init_or_warn(LookupPaths *lp, LookupScope scope, LookupPathsFlags flags, const char *root_dir) { + int r; - p->search_path = strv_free(p->search_path); - - p->persistent_config = mfree(p->persistent_config); - p->runtime_config = mfree(p->runtime_config); - - p->persistent_attached = mfree(p->persistent_attached); - p->runtime_attached = mfree(p->runtime_attached); - - p->generator = mfree(p->generator); - p->generator_early = mfree(p->generator_early); - p->generator_late = mfree(p->generator_late); - - p->transient = mfree(p->transient); - - p->persistent_control = mfree(p->persistent_control); - p->runtime_control = mfree(p->runtime_control); - - p->root_dir = mfree(p->root_dir); - p->temporary_dir = mfree(p->temporary_dir); + r = lookup_paths_init(lp, scope, flags, root_dir); + if (r < 0) + return log_error_errno(r, "Failed to initialize unit search paths%s%s: %m", + isempty(root_dir) ? "" : " for root directory ", strempty(root_dir)); + return r; } -void lookup_paths_log(LookupPaths *p) { - assert(p); +void lookup_paths_free(LookupPaths *lp) { + if (!lp) + return; - if (strv_isempty(p->search_path)) { + lp->search_path = strv_free(lp->search_path); + + lp->persistent_config = mfree(lp->persistent_config); + lp->runtime_config = mfree(lp->runtime_config); + + lp->persistent_attached = mfree(lp->persistent_attached); + lp->runtime_attached = mfree(lp->runtime_attached); + + lp->generator = mfree(lp->generator); + lp->generator_early = mfree(lp->generator_early); + lp->generator_late = mfree(lp->generator_late); + + lp->transient = mfree(lp->transient); + + lp->persistent_control = mfree(lp->persistent_control); + lp->runtime_control = mfree(lp->runtime_control); + + lp->root_dir = mfree(lp->root_dir); + lp->temporary_dir = mfree(lp->temporary_dir); +} + +void lookup_paths_log(LookupPaths *lp) { + assert(lp); + + if (strv_isempty(lp->search_path)) { log_debug("Ignoring unit files."); - p->search_path = strv_free(p->search_path); + lp->search_path = strv_free(lp->search_path); } else { _cleanup_free_ char *t = NULL; - t = strv_join(p->search_path, "\n\t"); + t = strv_join(lp->search_path, "\n\t"); log_debug("Looking for unit files in (higher priority first):\n\t%s", strna(t)); } } -char **generator_binary_paths(UnitFileScope scope) { +char **generator_binary_paths(LookupScope scope) { bool append = false; /* Add items from SYSTEMD_GENERATOR_PATH before normal directories */ _cleanup_strv_free_ char **paths = NULL; int r; @@ -792,15 +805,15 @@ char **generator_binary_paths(UnitFileScope scope) { switch (scope) { - case UNIT_FILE_SYSTEM: + case LOOKUP_SCOPE_SYSTEM: add = strv_new("/run/systemd/system-generators", "/etc/systemd/system-generators", "/usr/local/lib/systemd/system-generators", SYSTEM_GENERATOR_DIR); break; - case UNIT_FILE_GLOBAL: - case UNIT_FILE_USER: + case LOOKUP_SCOPE_GLOBAL: + case LOOKUP_SCOPE_USER: add = strv_new("/run/systemd/user-generators", "/etc/systemd/user-generators", "/usr/local/lib/systemd/user-generators", diff --git a/src/basic/path-lookup.h b/src/basic/path-lookup.h index af85dc7b4..aed72defe 100644 --- a/src/basic/path-lookup.h +++ b/src/basic/path-lookup.h @@ -3,10 +3,7 @@ #include -typedef struct LookupPaths LookupPaths; - #include "def.h" -#include "unit-file.h" #include "macro.h" typedef enum LookupPathsFlags { @@ -15,7 +12,15 @@ typedef enum LookupPathsFlags { LOOKUP_PATHS_SPLIT_USR = 1 << 2, } LookupPathsFlags; -struct LookupPaths { +typedef enum LookupScope { + LOOKUP_SCOPE_SYSTEM, + LOOKUP_SCOPE_GLOBAL, + LOOKUP_SCOPE_USER, + _LOOKUP_SCOPE_MAX, + _LOOKUP_SCOPE_INVALID = -EINVAL, +} LookupScope; + +typedef struct LookupPaths { /* Where we look for unit files. This includes the individual special paths below, but also any vendor * supplied, static unit file paths. */ char **search_path; @@ -52,9 +57,10 @@ struct LookupPaths { /* A temporary directory when running in test mode, to be nuked */ char *temporary_dir; -}; +} LookupPaths; -int lookup_paths_init(LookupPaths *p, UnitFileScope scope, LookupPathsFlags flags, const char *root_dir); +int lookup_paths_init(LookupPaths *lp, LookupScope scope, LookupPathsFlags flags, const char *root_dir); +int lookup_paths_init_or_warn(LookupPaths *lp, LookupScope scope, LookupPathsFlags flags, const char *root_dir); int xdg_user_dirs(char ***ret_config_dirs, char ***ret_data_dirs); int xdg_user_runtime_dir(char **ret, const char *suffix); @@ -67,7 +73,7 @@ bool path_is_user_config_dir(const char *path); void lookup_paths_log(LookupPaths *p); void lookup_paths_free(LookupPaths *p); -char **generator_binary_paths(UnitFileScope scope); +char **generator_binary_paths(LookupScope scope); char **env_generator_binary_paths(bool is_system); #define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network")) diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 4c952d863..0be2b9eec 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -202,9 +202,9 @@ int path_make_relative(const char *from, const char *to, char **ret) { } char* path_startswith_strv(const char *p, char **set) { - char **s, *t; - STRV_FOREACH(s, set) { + char *t; + t = path_startswith(p, *s); if (t) return t; @@ -214,7 +214,6 @@ char* path_startswith_strv(const char *p, char **set) { } int path_strv_make_absolute_cwd(char **l) { - char **s; int r; /* Goes through every item in the string list and makes it @@ -236,7 +235,6 @@ int path_strv_make_absolute_cwd(char **l) { } char **path_strv_resolve(char **l, const char *root) { - char **s; unsigned k = 0; bool enomem = false; int r; @@ -700,12 +698,12 @@ int find_executable_full(const char *name, const char *root, char **exec_search_ p = DEFAULT_PATH; if (exec_search_path) { - char **element; - STRV_FOREACH(element, exec_search_path) { _cleanup_free_ char *full_path = NULL; + if (!path_is_absolute(*element)) continue; + full_path = path_join(*element, name); if (!full_path) return -ENOMEM; @@ -754,7 +752,6 @@ int find_executable_full(const char *name, const char *root, char **exec_search_ bool paths_check_timestamp(const char* const* paths, usec_t *timestamp, bool update) { bool changed = false, originally_unset; - const char* const* i; assert(timestamp); @@ -932,8 +929,9 @@ int path_find_first_component(const char **p, bool accept_dot_dot, const char ** static const char *skip_slash_or_dot_backward(const char *path, const char *q) { assert(path); + assert(!q || q >= path); - for (; q >= path; q--) { + for (; q; q = PTR_SUB1(q, path)) { if (*q == '/') continue; if (q > path && strneq(q - 1, "/.", 2)) @@ -998,7 +996,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char * q = path + strlen(path) - 1; q = skip_slash_or_dot_backward(path, q); - if ((q < path) || /* the root directory */ + if (!q || /* the root directory */ (q == path && *q == '.')) { /* path is "." or "./" */ if (next) *next = path; @@ -1009,10 +1007,10 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char * last_end = q + 1; - while (q >= path && *q != '/') - q--; + while (q && *q != '/') + q = PTR_SUB1(q, path); - last_begin = q + 1; + last_begin = q ? q + 1 : path; len = last_end - last_begin; if (len > NAME_MAX) @@ -1022,10 +1020,7 @@ int path_find_last_component(const char *path, bool accept_dot_dot, const char * if (next) { q = skip_slash_or_dot_backward(path, q); - if (q < path) - *next = path; - else - *next = q + 1; + *next = q ? q + 1 : path; } if (ret) @@ -1237,8 +1232,6 @@ char *file_in_same_dir(const char *path, const char *filename) { } bool hidden_or_backup_file(const char *filename) { - const char *p; - assert(filename); if (filename[0] == '.' || @@ -1248,24 +1241,25 @@ bool hidden_or_backup_file(const char *filename) { endswith(filename, "~")) return true; - p = strrchr(filename, '.'); - if (!p) + const char *dot = strrchr(filename, '.'); + if (!dot) return false; - /* Please, let's not add more entries to the list below. If external projects think it's a good idea to come up - * with always new suffixes and that everybody else should just adjust to that, then it really should be on - * them. Hence, in future, let's not add any more entries. Instead, let's ask those packages to instead adopt - * one of the generic suffixes/prefixes for hidden files or backups, possibly augmented with an additional - * string. Specifically: there's now: + /* Please, let's not add more entries to the list below. If external projects think it's a good idea + * to come up with always new suffixes and that everybody else should just adjust to that, then it + * really should be on them. Hence, in future, let's not add any more entries. Instead, let's ask + * those packages to instead adopt one of the generic suffixes/prefixes for hidden files or backups, + * possibly augmented with an additional string. Specifically: there's now: * * The generic suffixes "~" and ".bak" for backup files * The generic prefix "." for hidden files * - * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", ".foopkg-dist" - * or so registered, let's refuse that and ask them to use ".foopkg.new", ".foopkg.old" or ".foopkg~" instead. + * Thus, if a new package manager "foopkg" wants its own set of ".foopkg-new", ".foopkg-old", + * ".foopkg-dist" or so registered, let's refuse that and ask them to use ".foopkg.new", + * ".foopkg.old" or ".foopkg~" instead. */ - return STR_IN_SET(p + 1, + return STR_IN_SET(dot + 1, "rpmnew", "rpmsave", "rpmorig", @@ -1287,15 +1281,16 @@ bool hidden_or_backup_file(const char *filename) { bool is_device_path(const char *path) { - /* Returns true on paths that likely refer to a device, either by path in sysfs or to something in /dev */ + /* Returns true for paths that likely refer to a device, either by path in sysfs or to something in + * /dev. */ return PATH_STARTSWITH_SET(path, "/dev/", "/sys/"); } bool valid_device_node_path(const char *path) { - /* Some superficial checks whether the specified path is a valid device node path, all without looking at the - * actual device node. */ + /* Some superficial checks whether the specified path is a valid device node path, all without + * looking at the actual device node. */ if (!PATH_STARTSWITH_SET(path, "/dev/", "/run/systemd/inaccessible/")) return false; @@ -1309,8 +1304,8 @@ bool valid_device_node_path(const char *path) { bool valid_device_allow_pattern(const char *path) { assert(path); - /* Like valid_device_node_path(), but also allows full-subsystem expressions, like DeviceAllow= and DeviceDeny= - * accept it */ + /* Like valid_device_node_path(), but also allows full-subsystem expressions like those accepted by + * DeviceAllow= and DeviceDeny=. */ if (STARTSWITH_SET(path, "block-", "char-")) return true; @@ -1339,7 +1334,7 @@ int systemd_installation_has_version(const char *root, unsigned minimal_version) _cleanup_strv_free_ char **names = NULL; _cleanup_free_ char *path = NULL; - char *c, **name; + char *c; path = path_join(root, pattern); if (!path) @@ -1401,8 +1396,8 @@ bool dot_or_dot_dot(const char *path) { bool empty_or_root(const char *path) { - /* For operations relative to some root directory, returns true if the specified root directory is redundant, - * i.e. either / or NULL or the empty string or any equivalent. */ + /* For operations relative to some root directory, returns true if the specified root directory is + * redundant, i.e. either / or NULL or the empty string or any equivalent. */ if (isempty(path)) return true; @@ -1411,8 +1406,6 @@ bool empty_or_root(const char *path) { } bool path_strv_contains(char **l, const char *path) { - char **i; - STRV_FOREACH(i, l) if (path_equal(*i, path)) return true; @@ -1421,10 +1414,9 @@ bool path_strv_contains(char **l, const char *path) { } bool prefixed_path_strv_contains(char **l, const char *path) { - char **i, *j; - STRV_FOREACH(i, l) { - j = *i; + const char *j = *i; + if (*j == '-') j++; if (*j == '+') diff --git a/src/basic/pcapng.h b/src/basic/pcapng.h new file mode 100644 index 000000000..57c3af501 --- /dev/null +++ b/src/basic/pcapng.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +/* + * For details about the file format see RFC: + * https://www.ietf.org/id/draft-tuexen-opsawg-pcapng-03.html + * and + * https://github.com/pcapng/pcapng/ + */ +enum pcapng_block_types { + PCAPNG_INTERFACE_BLOCK = 1, + PCAPNG_PACKET_BLOCK, /* Obsolete */ + PCAPNG_SIMPLE_PACKET_BLOCK, + PCAPNG_NAME_RESOLUTION_BLOCK, + PCAPNG_INTERFACE_STATS_BLOCK, + PCAPNG_ENHANCED_PACKET_BLOCK, + + PCAPNG_SECTION_BLOCK = 0x0A0D0D0A, +}; + +struct pcapng_option { + uint16_t code; + uint16_t length; + uint8_t data[]; +}; + +#define PCAPNG_BYTE_ORDER_MAGIC 0x1A2B3C4D +#define PCAPNG_MAJOR_VERS 1 +#define PCAPNG_MINOR_VERS 0 + +enum pcapng_opt { + PCAPNG_OPT_END = 0, + PCAPNG_OPT_COMMENT = 1, +}; + +struct pcapng_section { + uint32_t block_type; + uint32_t block_length; + uint32_t byte_order_magic; + uint16_t major_version; + uint16_t minor_version; + uint64_t section_length; +}; + +enum pcapng_section_opt { + PCAPNG_SHB_HARDWARE = 2, + PCAPNG_SHB_OS = 3, + PCAPNG_SHB_USERAPPL = 4, +}; + +struct pcapng_interface_block { + uint32_t block_type; /* 1 */ + uint32_t block_length; + uint16_t link_type; + uint16_t reserved; + uint32_t snap_len; +}; + +enum pcapng_interface_options { + PCAPNG_IFB_NAME = 2, + PCAPNG_IFB_DESCRIPTION, + PCAPNG_IFB_IPV4ADDR, + PCAPNG_IFB_IPV6ADDR, + PCAPNG_IFB_MACADDR, + PCAPNG_IFB_EUIADDR, + PCAPNG_IFB_SPEED, + PCAPNG_IFB_TSRESOL, + PCAPNG_IFB_TZONE, + PCAPNG_IFB_FILTER, + PCAPNG_IFB_OS, + PCAPNG_IFB_FCSLEN, + PCAPNG_IFB_TSOFFSET, + PCAPNG_IFB_HARDWARE, +}; + +struct pcapng_enhance_packet_block { + uint32_t block_type; /* 6 */ + uint32_t block_length; + uint32_t interface_id; + uint32_t timestamp_hi; + uint32_t timestamp_lo; + uint32_t capture_length; + uint32_t original_length; +}; + +/* Flags values */ +#define PCAPNG_IFB_INBOUND 0b01 +#define PCAPNG_IFB_OUTBOUND 0b10 + +enum pcapng_epb_options { + PCAPNG_EPB_FLAGS = 2, + PCAPNG_EPB_HASH, + PCAPNG_EPB_DROPCOUNT, + PCAPNG_EPB_PACKETID, + PCAPNG_EPB_QUEUE, + PCAPNG_EPB_VERDICT, +}; + +struct pcapng_statistics_block { + uint32_t block_type; /* 5 */ + uint32_t block_length; + uint32_t interface_id; + uint32_t timestamp_hi; + uint32_t timestamp_lo; +}; + +enum pcapng_isb_options { + PCAPNG_ISB_STARTTIME = 2, + PCAPNG_ISB_ENDTIME, + PCAPNG_ISB_IFRECV, + PCAPNG_ISB_IFDROP, + PCAPNG_ISB_FILTERACCEPT, + PCAPNG_ISB_OSDROP, + PCAPNG_ISB_USRDELIV, +}; diff --git a/src/basic/process-util.c b/src/basic/process-util.c index c97185215..72807d039 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -214,7 +215,6 @@ int get_process_cmdline(pid_t pid, size_t max_columns, ProcessCmdlineFlags flags assert(!(flags & PROCESS_CMDLINE_USE_LOCALE)); _cleanup_strv_free_ char **args = NULL; - char **p; args = strv_parse_nulstr(t, k); if (!args) @@ -645,7 +645,7 @@ int get_process_environ(pid_t pid, char **ret) { int get_process_ppid(pid_t pid, pid_t *ret) { _cleanup_free_ char *line = NULL; - long unsigned ppid; + unsigned long ppid; const char *p; int r; @@ -688,7 +688,7 @@ int get_process_ppid(pid_t pid, pid_t *ret) { if (ppid == 0) return -EADDRNOTAVAIL; - if ((pid_t) ppid < 0 || (long unsigned) (pid_t) ppid != ppid) + if ((pid_t) ppid < 0 || (unsigned long) (pid_t) ppid != ppid) return -ERANGE; if (ret) @@ -819,13 +819,12 @@ int wait_for_terminate_with_timeout(pid_t pid, usec_t timeout) { for (;;) { usec_t n; siginfo_t status = {}; - struct timespec ts; n = now(CLOCK_MONOTONIC); if (n >= until) break; - r = RET_NERRNO(sigtimedwait(&mask, NULL, timespec_store(&ts, until - n))); + r = RET_NERRNO(sigtimedwait(&mask, NULL, TIMESPEC_STORE(until - n))); /* Assuming we woke due to the child exiting. */ if (waitid(P_PID, pid, &status, WEXITED|WNOHANG) == 0) { if (status.si_pid == pid) { @@ -1161,12 +1160,6 @@ void reset_cached_pid(void) { cached_pid = CACHED_PID_UNSET; } -/* We use glibc __register_atfork() + __dso_handle directly here, as they are not included in the glibc - * headers. __register_atfork() is mostly equivalent to pthread_atfork(), but doesn't require us to link against - * libpthread, as it is part of glibc anyway. */ -extern int __register_atfork(void (*prepare) (void), void (*parent) (void), void (*child) (void), void *dso_handle); -extern void* __dso_handle _weak_; - pid_t getpid_cached(void) { static bool installed = false; pid_t current_value; @@ -1194,7 +1187,7 @@ pid_t getpid_cached(void) { * only half-documented (glibc doesn't document it but LSB does — though only superficially) * we'll check for errors only in the most generic fashion possible. */ - if (__register_atfork(NULL, NULL, reset_cached_pid, __dso_handle) != 0) { + if (pthread_atfork(NULL, NULL, reset_cached_pid) != 0) { /* OOM? Let's try again later */ cached_pid = CACHED_PID_UNSET; return new_pid; diff --git a/src/basic/random-util.c b/src/basic/random-util.c index e11733085..f2e68fcdd 100644 --- a/src/basic/random-util.c +++ b/src/basic/random-util.c @@ -35,218 +35,34 @@ static bool srand_called = false; -int rdrand(unsigned long *ret) { - - /* So, you are a "security researcher", and you wonder why we bother with using raw RDRAND here, - * instead of sticking to /dev/urandom or getrandom()? - * - * Here's why: early boot. On Linux, during early boot the random pool that backs /dev/urandom and - * getrandom() is generally not initialized yet. It is very common that initialization of the random - * pool takes a longer time (up to many minutes), in particular on embedded devices that have no - * explicit hardware random generator, as well as in virtualized environments such as major cloud - * installations that do not provide virtio-rng or a similar mechanism. - * - * In such an environment using getrandom() synchronously means we'd block the entire system boot-up - * until the pool is initialized, i.e. *very* long. Using getrandom() asynchronously (GRND_NONBLOCK) - * would mean acquiring randomness during early boot would simply fail. Using /dev/urandom would mean - * generating many kmsg log messages about our use of it before the random pool is properly - * initialized. Neither of these outcomes is desirable. - * - * Thus, for very specific purposes we use RDRAND instead of either of these three options. RDRAND - * provides us quickly and relatively reliably with random values, without having to delay boot, - * without triggering warning messages in kmsg. - * - * Note that we use RDRAND only under very specific circumstances, when the requirements on the - * quality of the returned entropy permit it. Specifically, here are some cases where we *do* use - * RDRAND: - * - * • UUID generation: UUIDs are supposed to be universally unique but are not cryptographic - * key material. The quality and trust level of RDRAND should hence be OK: UUIDs should be - * generated in a way that is reliably unique, but they do not require ultimate trust into - * the entropy generator. systemd generates a number of UUIDs during early boot, including - * 'invocation IDs' for every unit spawned that identify the specific invocation of the - * service globally, and a number of others. Other alternatives for generating these UUIDs - * have been considered, but don't really work: for example, hashing uuids from a local - * system identifier combined with a counter falls flat because during early boot disk - * storage is not yet available (think: initrd) and thus a system-specific ID cannot be - * stored or retrieved yet. - * - * • Hash table seed generation: systemd uses many hash tables internally. Hash tables are - * generally assumed to have O(1) access complexity, but can deteriorate to prohibitive - * O(n) access complexity if an attacker manages to trigger a large number of hash - * collisions. Thus, systemd (as any software employing hash tables should) uses seeded - * hash functions for its hash tables, with a seed generated randomly. The hash tables - * systemd employs watch the fill level closely and reseed if necessary. This allows use of - * a low quality RNG initially, as long as it improves should a hash table be under attack: - * the attacker after all needs to trigger many collisions to exploit it for the purpose - * of DoS, but if doing so improves the seed the attack surface is reduced as the attack - * takes place. - * - * Some cases where we do NOT use RDRAND are: - * - * • Generation of cryptographic key material 🔑 - * - * • Generation of cryptographic salt values 🧂 - * - * This function returns: - * - * -EOPNOTSUPP → RDRAND is not available on this system 😔 - * -EAGAIN → The operation failed this time, but is likely to work if you try again a few - * times ♻ - * -EUCLEAN → We got some random value, but it looked strange, so we refused using it. - * This failure might or might not be temporary. 😕 - */ - -#if defined(__i386__) || defined(__x86_64__) - static int have_rdrand = -1; - unsigned long v; - uint8_t success; - - if (have_rdrand < 0) { - uint32_t eax, ebx, ecx, edx; - - /* Check if RDRAND is supported by the CPU */ - if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) { - have_rdrand = false; - return -EOPNOTSUPP; - } - -/* Compat with old gcc where bit_RDRND didn't exist yet */ -#ifndef bit_RDRND -#define bit_RDRND (1U << 30) -#endif - - have_rdrand = !!(ecx & bit_RDRND); - - if (have_rdrand > 0) { - /* Allow disabling use of RDRAND with SYSTEMD_RDRAND=0 - If it is unset getenv_bool_secure will return a negative value. */ - if (getenv_bool_secure("SYSTEMD_RDRAND") == 0) { - have_rdrand = false; - return -EOPNOTSUPP; - } - } - } - - if (have_rdrand == 0) - return -EOPNOTSUPP; - - asm volatile("rdrand %0;" - "setc %1" - : "=r" (v), - "=qm" (success)); - msan_unpoison(&success, sizeof(success)); - if (!success) - return -EAGAIN; - - /* Apparently on some AMD CPUs RDRAND will sometimes (after a suspend/resume cycle?) report success - * via the carry flag but nonetheless return the same fixed value -1 in all cases. This appears to be - * a bad bug in the CPU or firmware. Let's deal with that and work-around this by explicitly checking - * for this special value (and also 0, just to be sure) and filtering it out. This is a work-around - * only however and something AMD really should fix properly. The Linux kernel should probably work - * around this issue by turning off RDRAND altogether on those CPUs. See: - * https://github.com/systemd/systemd/issues/11810 */ - if (v == 0 || v == ULONG_MAX) - return log_debug_errno(SYNTHETIC_ERRNO(EUCLEAN), - "RDRAND returned suspicious value %lx, assuming bad hardware RNG, not using value.", v); - - *ret = v; - return 0; -#else - return -EOPNOTSUPP; -#endif -} - int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { static int have_syscall = -1; _cleanup_close_ int fd = -1; - bool got_some = false; - /* Gathers some high-quality randomness from the kernel (or potentially mid-quality randomness from - * the CPU if the RANDOM_ALLOW_RDRAND flag is set). This call won't block, unless the RANDOM_BLOCK - * flag is set. If RANDOM_MAY_FAIL is set, an error is returned if the random pool is not - * initialized. Otherwise it will always return some data from the kernel, regardless of whether the - * random pool is fully initialized or not. If RANDOM_EXTEND_WITH_PSEUDO is set, and some but not - * enough better quality randomness could be acquired, the rest is filled up with low quality - * randomness. - * - * Of course, when creating cryptographic key material you really shouldn't use RANDOM_ALLOW_DRDRAND - * or even RANDOM_EXTEND_WITH_PSEUDO. - * - * When generating UUIDs it's fine to use RANDOM_ALLOW_RDRAND but not OK to use - * RANDOM_EXTEND_WITH_PSEUDO. In fact RANDOM_EXTEND_WITH_PSEUDO is only really fine when invoked via - * an "all bets are off" wrapper, such as random_bytes(), see below. */ + /* Gathers some high-quality randomness from the kernel. This call won't block, unless the RANDOM_BLOCK + * flag is set. If it doesn't block, it will still always return some data from the kernel, regardless + * of whether the random pool is fully initialized or not. When creating cryptographic key material you + * should always use RANDOM_BLOCK. */ if (n == 0) return 0; - if (FLAGS_SET(flags, RANDOM_ALLOW_RDRAND)) - /* Try x86-64' RDRAND intrinsic if we have it. We only use it if high quality randomness is - * not required, as we don't trust it (who does?). Note that we only do a single iteration of - * RDRAND here, even though the Intel docs suggest calling this in a tight loop of 10 - * invocations or so. That's because we don't really care about the quality here. We - * generally prefer using RDRAND if the caller allows us to, since this way we won't upset - * the kernel's random subsystem by accessing it before the pool is initialized (after all it - * will kmsg log about every attempt to do so). */ - for (;;) { - unsigned long u; - size_t m; - - if (rdrand(&u) < 0) { - if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudo-random values */ - pseudo_random_bytes(p, n); - return 0; - } - - /* OK, this didn't work, let's go to getrandom() + /dev/urandom instead */ - break; - } - - m = MIN(sizeof(u), n); - memcpy(p, &u, m); - - p = (uint8_t*) p + m; - n -= m; - - if (n == 0) - return 0; /* Yay, success! */ - - got_some = true; - } - /* Use the getrandom() syscall unless we know we don't have it. */ if (have_syscall != 0 && !HAS_FEATURE_MEMORY_SANITIZER) { - for (;;) { - ssize_t l; - l = getrandom(p, n, - (FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_NONBLOCK) | - (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE) ? GRND_INSECURE : 0)); + ssize_t l = getrandom(p, n, FLAGS_SET(flags, RANDOM_BLOCK) ? 0 : GRND_INSECURE); + if (l > 0) { have_syscall = true; if ((size_t) l == n) return 0; /* Yay, success! */ + /* We didn't get enough data, so try again */ assert((size_t) l < n); p = (uint8_t*) p + l; n -= l; - - if (FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudo-random values */ - pseudo_random_bytes(p, n); - return 0; - } - - got_some = true; - - /* Hmm, we didn't get enough good data but the caller insists on good data? Then try again */ - if (FLAGS_SET(flags, RANDOM_BLOCK)) - continue; - - /* Fill in the rest with /dev/urandom */ - break; + continue; } else if (l == 0) { have_syscall = true; @@ -257,37 +73,12 @@ int genuine_random_bytes(void *p, size_t n, RandomFlags flags) { have_syscall = false; break; - } else if (errno == EAGAIN) { - /* The kernel has no entropy whatsoever. Let's remember to use the syscall - * the next time again though. - * - * If RANDOM_MAY_FAIL is set, return an error so that random_bytes() can - * produce some pseudo-random bytes instead. Otherwise, fall back to - * /dev/urandom, which we know is empty, but the kernel will produce some - * bytes for us on a best-effort basis. */ - have_syscall = true; - - if (got_some && FLAGS_SET(flags, RANDOM_EXTEND_WITH_PSEUDO)) { - /* Fill in the remaining bytes using pseudorandom values */ - pseudo_random_bytes(p, n); - return 0; - } - - if (FLAGS_SET(flags, RANDOM_MAY_FAIL)) - return -ENODATA; - - /* Use /dev/urandom instead */ - break; - } else if (errno == EINVAL) { - - /* Most likely: unknown flag. We know that GRND_INSECURE might cause this, - * hence try without. */ - - if (FLAGS_SET(flags, RANDOM_ALLOW_INSECURE)) { - flags = flags &~ RANDOM_ALLOW_INSECURE; - continue; - } + /* If we previously passed GRND_INSECURE, and this flag isn't known, then + * we're likely running an old kernel which has getrandom() but not + * GRND_INSECURE. In this case, fall back to /dev/urandom. */ + if (!FLAGS_SET(flags, RANDOM_BLOCK)) + break; return -errno; } else @@ -312,8 +103,6 @@ void initialize_srand(void) { #if HAVE_SYS_AUXV_H const void *auxv; #endif - unsigned long k; - if (srand_called) return; @@ -338,9 +127,6 @@ void initialize_srand(void) { x ^= (unsigned) now(CLOCK_REALTIME); x ^= (unsigned) gettid(); - if (rdrand(&k) >= 0) - x ^= (unsigned) k; - srand(x); srand_called = true; @@ -394,11 +180,8 @@ void random_bytes(void *p, size_t n) { * * What this function will do: * - * • This function will preferably use the CPU's RDRAND operation, if it is available, in - * order to return "mid-quality" random values cheaply. - * - * • Use getrandom() with GRND_NONBLOCK, to return high-quality random values if they are - * cheaply available. + * • Use getrandom(GRND_INSECURE) or /dev/urandom, to return high-quality random values if + * they are cheaply available, or less high-quality random values if they are not. * * • This function will return pseudo-random data, generated via libc rand() if nothing * better is available. @@ -421,7 +204,7 @@ void random_bytes(void *p, size_t n) { * This function is hence not useful for generating UUIDs or cryptographic key material. */ - if (genuine_random_bytes(p, n, RANDOM_EXTEND_WITH_PSEUDO|RANDOM_MAY_FAIL|RANDOM_ALLOW_RDRAND|RANDOM_ALLOW_INSECURE) >= 0) + if (genuine_random_bytes(p, n, 0) >= 0) return; /* If for some reason some user made /dev/urandom unavailable to us, or the kernel has no entropy, use a PRNG instead. */ diff --git a/src/basic/random-util.h b/src/basic/random-util.h index e6528ddc7..ccee32792 100644 --- a/src/basic/random-util.h +++ b/src/basic/random-util.h @@ -6,11 +6,7 @@ #include typedef enum RandomFlags { - RANDOM_EXTEND_WITH_PSEUDO = 1 << 0, /* If we can't get enough genuine randomness, but some, fill up the rest with pseudo-randomness */ - RANDOM_BLOCK = 1 << 1, /* Rather block than return crap randomness (only if the kernel supports that) */ - RANDOM_MAY_FAIL = 1 << 2, /* If we can't get any randomness at all, return early with -ENODATA */ - RANDOM_ALLOW_RDRAND = 1 << 3, /* Allow usage of the CPU RNG */ - RANDOM_ALLOW_INSECURE = 1 << 4, /* Allow usage of GRND_INSECURE flag to kernel's getrandom() API */ + RANDOM_BLOCK = 1 << 0, /* Rather block than return crap randomness (only if the kernel supports that) */ } RandomFlags; int genuine_random_bytes(void *p, size_t n, RandomFlags flags); /* returns "genuine" randomness, optionally filled up with pseudo random, if not enough is available */ @@ -31,10 +27,8 @@ static inline uint32_t random_u32(void) { return u; } -int rdrand(unsigned long *ret); - /* Some limits on the pool sizes when we deal with the kernel random pool */ -#define RANDOM_POOL_SIZE_MIN 512U +#define RANDOM_POOL_SIZE_MIN 32U #define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U) size_t random_pool_size(void); diff --git a/src/basic/sort-util.h b/src/basic/sort-util.h index 02a6784d9..f0bf246aa 100644 --- a/src/basic/sort-util.h +++ b/src/basic/sort-util.h @@ -16,8 +16,8 @@ void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, #define typesafe_bsearch_r(k, b, n, func, userdata) \ ({ \ - const typeof(b[0]) *_k = k; \ - int (*_func_)(const typeof(b[0])*, const typeof(b[0])*, typeof(userdata)) = func; \ + const typeof((b)[0]) *_k = k; \ + int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*, typeof(userdata)) = func; \ xbsearch_r((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_userdata_fn_t) _func_, userdata); \ }) @@ -36,8 +36,8 @@ static inline void* bsearch_safe(const void *key, const void *base, #define typesafe_bsearch(k, b, n, func) \ ({ \ - const typeof(b[0]) *_k = k; \ - int (*_func_)(const typeof(b[0])*, const typeof(b[0])*) = func; \ + const typeof((b)[0]) *_k = k; \ + int (*_func_)(const typeof((b)[0])*, const typeof((b)[0])*) = func; \ bsearch_safe((const void*) _k, (b), (n), sizeof((b)[0]), (comparison_fn_t) _func_); \ }) @@ -57,7 +57,7 @@ static inline void _qsort_safe(void *base, size_t nmemb, size_t size, comparison * is the prototype for the comparison function */ #define typesafe_qsort(p, n, func) \ ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ + int (*_func_)(const typeof((p)[0])*, const typeof((p)[0])*) = func; \ _qsort_safe((p), (n), sizeof((p)[0]), (comparison_fn_t) _func_); \ }) @@ -71,7 +71,7 @@ static inline void qsort_r_safe(void *base, size_t nmemb, size_t size, compariso #define typesafe_qsort_r(p, n, func, userdata) \ ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*, typeof(userdata)) = func; \ + int (*_func_)(const typeof((p)[0])*, const typeof((p)[0])*, typeof(userdata)) = func; \ qsort_r_safe((p), (n), sizeof((p)[0]), (comparison_userdata_fn_t) _func_, userdata); \ }) diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index efac7b002..b25cabc6b 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -127,17 +127,22 @@ bool null_or_empty(struct stat *st) { return false; } -int null_or_empty_path(const char *fn) { +int null_or_empty_path_with_root(const char *fn, const char *root) { struct stat st; + int r; assert(fn); - /* If we have the path, let's do an easy text comparison first. */ - if (path_equal(fn, "/dev/null")) + /* A symlink to /dev/null or an empty file? + * When looking under root_dir, we can't expect /dev/ to be mounted, + * so let's see if the path is a (possibly dangling) symlink to /dev/null. */ + + if (path_equal_ptr(path_startswith(fn, root ?: "/"), "dev/null")) return true; - if (stat(fn, &st) < 0) - return -errno; + r = chase_symlinks_and_stat(fn, root, CHASE_PREFIX_ROOT, NULL, &st, NULL); + if (r < 0) + return r; return null_or_empty(&st); } @@ -185,8 +190,7 @@ int files_same(const char *filea, const char *fileb, int flags) { if (fstatat(AT_FDCWD, fileb, &b, flags) < 0) return -errno; - return a.st_dev == b.st_dev && - a.st_ino == b.st_ino; + return stat_inode_same(&a, &b); } bool is_fs_type(const struct statfs *s, statfs_f_type_t magic_value) { @@ -249,6 +253,15 @@ int path_is_temporary_fs(const char *path) { return is_temporary_fs(&s); } +int path_is_network_fs(const char *path) { + struct statfs s; + + if (statfs(path, &s) < 0) + return -errno; + + return is_network_fs(&s); +} + int stat_verify_regular(const struct stat *st) { assert(st); @@ -408,6 +421,18 @@ int proc_mounted(void) { return r; } +bool stat_inode_same(const struct stat *a, const struct stat *b) { + + /* Returns if the specified stat structure references the same (though possibly modified) inode. Does + * a thorough check, comparing inode nr, backing device and if the inode is still of the same type. */ + + return a && b && + (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ + ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ + a->st_dev == b->st_dev && + a->st_ino == b->st_ino; +} + bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { /* Returns if the specified stat structures reference the same, unmodified inode. This check tries to @@ -419,14 +444,10 @@ bool stat_inode_unmodified(const struct stat *a, const struct stat *b) { * about contents of the file. The purpose here is to detect file contents changes, and nothing * else. */ - return a && b && - (a->st_mode & S_IFMT) != 0 && /* We use the check for .st_mode if the structure was ever initialized */ - ((a->st_mode ^ b->st_mode) & S_IFMT) == 0 && /* same inode type */ + return stat_inode_same(a, b) && a->st_mtim.tv_sec == b->st_mtim.tv_sec && a->st_mtim.tv_nsec == b->st_mtim.tv_nsec && (!S_ISREG(a->st_mode) || a->st_size == b->st_size) && /* if regular file, compare file size */ - a->st_dev == b->st_dev && - a->st_ino == b->st_ino && (!(S_ISCHR(a->st_mode) || S_ISBLK(a->st_mode)) || a->st_rdev == b->st_rdev); /* if device node, also compare major/minor, because we can */ } diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index a566114f7..37513a43e 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -31,9 +31,13 @@ static inline int dir_is_populated(const char *path) { } bool null_or_empty(struct stat *st) _pure_; -int null_or_empty_path(const char *fn); +int null_or_empty_path_with_root(const char *fn, const char *root); int null_or_empty_fd(int fd); +static inline int null_or_empty_path(const char *fn) { + return null_or_empty_path_with_root(fn, NULL); +} + int path_is_read_only_fs(const char *path); int files_same(const char *filea, const char *fileb, int flags); @@ -53,6 +57,7 @@ int fd_is_temporary_fs(int fd); int fd_is_network_fs(int fd); int path_is_temporary_fs(const char *path); +int path_is_network_fs(const char *path); /* Because statfs.t_type can be int on some architectures, we have to cast * the const magic to the type, otherwise the compiler warns about @@ -91,6 +96,7 @@ int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret int proc_mounted(void); +bool stat_inode_same(const struct stat *a, const struct stat *b); bool stat_inode_unmodified(const struct stat *a, const struct stat *b); int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct statx *sx); @@ -113,3 +119,9 @@ int statx_fallback(int dfd, const char *path, int flags, unsigned mask, struct s struct new_statx nsx; \ } var #endif + +static inline bool devid_set_and_equal(dev_t a, dev_t b) { + /* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't + * know" and we'll return false */ + return a == b && a != 0; +} diff --git a/src/basic/strv.c b/src/basic/strv.c index 2ba5a9483..c4e3dad44 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -17,8 +17,6 @@ #include "strv.h" char* strv_find(char * const *l, const char *name) { - char * const *i; - assert(name); STRV_FOREACH(i, l) @@ -29,8 +27,6 @@ char* strv_find(char * const *l, const char *name) { } char* strv_find_case(char * const *l, const char *name) { - char * const *i; - assert(name); STRV_FOREACH(i, l) @@ -41,8 +37,6 @@ char* strv_find_case(char * const *l, const char *name) { } char* strv_find_prefix(char * const *l, const char *name) { - char * const *i; - assert(name); STRV_FOREACH(i, l) @@ -53,14 +47,14 @@ char* strv_find_prefix(char * const *l, const char *name) { } char* strv_find_startswith(char * const *l, const char *name) { - char * const *i, *e; - assert(name); /* Like strv_find_prefix, but actually returns only the * suffix, not the whole item */ STRV_FOREACH(i, l) { + char *e; + e = startswith(*i, name); if (e) return e; @@ -70,18 +64,13 @@ char* strv_find_startswith(char * const *l, const char *name) { } char** strv_free(char **l) { - if (!l) - return NULL; - - for (char **k = l; *k; k++) + STRV_FOREACH(k, l) free(*k); return mfree(l); } char** strv_free_erase(char **l) { - char **i; - STRV_FOREACH(i, l) erase_and_freep(i); @@ -89,32 +78,29 @@ char** strv_free_erase(char **l) { } char** strv_copy(char * const *l) { - char **r, **k; + _cleanup_strv_free_ char **result = NULL; + char **k; - k = r = new(char*, strv_length(l) + 1); - if (!r) + result = new(char*, strv_length(l) + 1); + if (!result) return NULL; - if (l) - for (; *l; k++, l++) { - *k = strdup(*l); - if (!*k) { - strv_free(r); - return NULL; - } - } + k = result; + STRV_FOREACH(i, l) { + *k = strdup(*i); + if (!*k) + return NULL; + k++; + } *k = NULL; - return r; + return TAKE_PTR(result); } size_t strv_length(char * const *l) { size_t n = 0; - if (!l) - return 0; - - for (; *l; l++) + STRV_FOREACH(i, l) n++; return n; @@ -171,8 +157,8 @@ char** strv_new_internal(const char *x, ...) { } int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates) { - char * const *s, **t; size_t p, q, i = 0; + char **t; assert(a); @@ -217,7 +203,6 @@ rollback: } int strv_extend_strv_concat(char ***a, char * const *b, const char *suffix) { - char * const *s; int r; STRV_FOREACH(s, b) { @@ -367,7 +352,6 @@ int strv_split_colon_pairs(char ***t, const char *s) { } char* strv_join_full(char * const *l, const char *separator, const char *prefix, bool unescape_separators) { - char * const *s; char *r, *e; size_t n, k, m; @@ -595,8 +579,6 @@ int strv_extend_front(char ***l, const char *value) { } char** strv_uniq(char **l) { - char **i; - /* Drops duplicate entries. The first identical string will be * kept, the others dropped */ @@ -607,10 +589,8 @@ char** strv_uniq(char **l) { } bool strv_is_uniq(char * const *l) { - char * const *i; - STRV_FOREACH(i, l) - if (strv_find(i+1, *i)) + if (strv_contains(i+1, *i)) return false; return true; @@ -717,7 +697,6 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { */ _cleanup_free_ char *m = NULL; - char * const *i; size_t n = 0; assert(ret); @@ -754,8 +733,6 @@ int strv_make_nulstr(char * const *l, char **ret, size_t *ret_size) { } bool strv_overlap(char * const *a, char * const *b) { - char * const *i; - STRV_FOREACH(i, a) if (strv_contains(b, *i)) return true; @@ -795,8 +772,6 @@ int strv_compare(char * const *a, char * const *b) { } void strv_print(char * const *l) { - char * const *s; - STRV_FOREACH(s, l) puts(*s); } @@ -830,8 +805,6 @@ char** strv_reverse(char **l) { } char** strv_shell_escape(char **l, const char *bad) { - char **s; - /* Escapes every character in every string in l that is in bad, * edits in-place, does not roll-back on error. */ @@ -914,7 +887,6 @@ rollback: int fputstrv(FILE *f, char * const *l, const char *separator, bool *space) { bool b = false; - char * const *s; int r; /* Like fputs(), but for strv, and with a less stupid argument order */ diff --git a/src/basic/strv.h b/src/basic/strv.h index 092d40c84..2a858326c 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -122,19 +122,30 @@ static inline int strv_from_nulstr(char ***a, const char *nulstr) { bool strv_overlap(char * const *a, char * const *b) _pure_; +#define _STRV_FOREACH(s, l, i) \ + for (typeof(*(l)) *s, *i = (l); (s = i) && *i; i++) + #define STRV_FOREACH(s, l) \ - for ((s) = (l); (s) && *(s); (s)++) + _STRV_FOREACH(s, l, UNIQ_T(i, UNIQ)) -#define STRV_FOREACH_BACKWARDS(s, l) \ - for (s = ({ \ - typeof(l) _l = l; \ - _l ? _l + strv_length(_l) - 1U : NULL; \ - }); \ - (l) && ((s) >= (l)); \ - (s)--) +#define _STRV_FOREACH_BACKWARDS(s, l, h, i) \ + for (typeof(*(l)) *s, *h = (l), *i = ({ \ + size_t _len = strv_length(h); \ + _len > 0 ? h + _len - 1 : NULL; \ + }); \ + (s = i); \ + i = PTR_SUB1(i, h)) -#define STRV_FOREACH_PAIR(x, y, l) \ - for ((x) = (l), (y) = (x) ? (x+1) : NULL; (x) && *(x) && *(y); (x) += 2, (y) = (x + 1)) +#define STRV_FOREACH_BACKWARDS(s, l) \ + _STRV_FOREACH_BACKWARDS(s, l, UNIQ_T(h, UNIQ), UNIQ_T(i, UNIQ)) + +#define _STRV_FOREACH_PAIR(x, y, l, i) \ + for (typeof(*l) *x, *y, *i = (l); \ + i && *(x = i) && *(y = i + 1); \ + i += 2) + +#define STRV_FOREACH_PAIR(x, y, l) \ + _STRV_FOREACH_PAIR(x, y, l, UNIQ_T(i, UNIQ)) char** strv_sort(char **l); void strv_print(char * const *l); @@ -185,7 +196,7 @@ void strv_print(char * const *l); #define STARTSWITH_SET(p, ...) \ ({ \ const char *_p = (p); \ - char *_found = NULL, **_i; \ + char *_found = NULL; \ STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ _found = startswith(_p, *_i); \ if (_found) \ @@ -197,7 +208,7 @@ void strv_print(char * const *l); #define ENDSWITH_SET(p, ...) \ ({ \ const char *_p = (p); \ - char *_found = NULL, **_i; \ + char *_found = NULL; \ STRV_FOREACH(_i, STRV_MAKE(__VA_ARGS__)) { \ _found = endswith(_p, *_i); \ if (_found) \ @@ -207,7 +218,7 @@ void strv_print(char * const *l); }) #define _FOREACH_STRING(uniq, x, y, ...) \ - for (char **UNIQ_T(l, uniq) = STRV_MAKE(({ x = y; }), ##__VA_ARGS__); \ + for (const char *x, * const*UNIQ_T(l, uniq) = STRV_MAKE_CONST(({ x = y; }), ##__VA_ARGS__); \ x; \ x = *(++UNIQ_T(l, uniq))) diff --git a/src/basic/strxcpyx.c b/src/basic/strxcpyx.c index dbbf7d08d..52b95650c 100644 --- a/src/basic/strxcpyx.c +++ b/src/basic/strxcpyx.c @@ -15,57 +15,73 @@ #include #include +#include "string-util.h" #include "strxcpyx.h" -size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) { +size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated) { + bool truncated = false; + assert(dest); assert(src); - if (size == 0) + if (size == 0) { + if (ret_truncated) + *ret_truncated = len > 0; return 0; + } if (len >= size) { if (size > 1) *dest = mempcpy(*dest, src, size-1); size = 0; + truncated = true; } else if (len > 0) { *dest = mempcpy(*dest, src, len); size -= len; } + if (ret_truncated) + *ret_truncated = truncated; + *dest[0] = '\0'; return size; } -size_t strpcpy(char **dest, size_t size, const char *src) { +size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated) { assert(dest); assert(src); - return strnpcpy(dest, size, src, strlen(src)); + return strnpcpy_full(dest, size, src, strlen(src), ret_truncated); } -size_t strpcpyf(char **dest, size_t size, const char *src, ...) { +size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) { + bool truncated = false; va_list va; int i; assert(dest); assert(src); - if (size == 0) - return 0; - va_start(va, src); i = vsnprintf(*dest, size, src, va); - if (i < (int)size) { + va_end(va); + + if (i < (int) size) { *dest += i; size -= i; - } else + } else { size = 0; - va_end(va); + truncated = i > 0; + } + + if (ret_truncated) + *ret_truncated = truncated; + return size; } -size_t strpcpyl(char **dest, size_t size, const char *src, ...) { +size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) { + bool truncated = false; va_list va; assert(dest); @@ -73,31 +89,38 @@ size_t strpcpyl(char **dest, size_t size, const char *src, ...) { va_start(va, src); do { - size = strpcpy(dest, size, src); + bool t; + + size = strpcpy_full(dest, size, src, &t); + truncated = truncated || t; src = va_arg(va, char *); } while (src); va_end(va); + + if (ret_truncated) + *ret_truncated = truncated; return size; } -size_t strnscpy(char *dest, size_t size, const char *src, size_t len) { +size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated) { char *s; assert(dest); assert(src); s = dest; - return strnpcpy(&s, size, src, len); + return strnpcpy_full(&s, size, src, len, ret_truncated); } -size_t strscpy(char *dest, size_t size, const char *src) { +size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated) { assert(dest); assert(src); - return strnscpy(dest, size, src, strlen(src)); + return strnscpy_full(dest, size, src, strlen(src), ret_truncated); } -size_t strscpyl(char *dest, size_t size, const char *src, ...) { +size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) { + bool truncated = false; va_list va; char *s; @@ -107,10 +130,16 @@ size_t strscpyl(char *dest, size_t size, const char *src, ...) { va_start(va, src); s = dest; do { - size = strpcpy(&s, size, src); + bool t; + + size = strpcpy_full(&s, size, src, &t); + truncated = truncated || t; src = va_arg(va, char *); } while (src); va_end(va); + if (ret_truncated) + *ret_truncated = truncated; + return size; } diff --git a/src/basic/strxcpyx.h b/src/basic/strxcpyx.h index cdef492db..4a648ed03 100644 --- a/src/basic/strxcpyx.h +++ b/src/basic/strxcpyx.h @@ -1,14 +1,33 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include #include #include "macro.h" -size_t strnpcpy(char **dest, size_t size, const char *src, size_t len); -size_t strpcpy(char **dest, size_t size, const char *src); -size_t strpcpyf(char **dest, size_t size, const char *src, ...) _printf_(3, 4); -size_t strpcpyl(char **dest, size_t size, const char *src, ...) _sentinel_; -size_t strnscpy(char *dest, size_t size, const char *src, size_t len); -size_t strscpy(char *dest, size_t size, const char *src); -size_t strscpyl(char *dest, size_t size, const char *src, ...) _sentinel_; +size_t strnpcpy_full(char **dest, size_t size, const char *src, size_t len, bool *ret_truncated); +static inline size_t strnpcpy(char **dest, size_t size, const char *src, size_t len) { + return strnpcpy_full(dest, size, src, len, NULL); +} +size_t strpcpy_full(char **dest, size_t size, const char *src, bool *ret_truncated); +static inline size_t strpcpy(char **dest, size_t size, const char *src) { + return strpcpy_full(dest, size, src, NULL); +} +size_t strpcpyf_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _printf_(4, 5); +#define strpcpyf(dest, size, src, ...) \ + strpcpyf_full((dest), (size), NULL, (src), ##__VA_ARGS__) +size_t strpcpyl_full(char **dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_; +#define strpcpyl(dest, size, src, ...) \ + strpcpyl_full((dest), (size), NULL, (src), ##__VA_ARGS__) +size_t strnscpy_full(char *dest, size_t size, const char *src, size_t len, bool *ret_truncated); +static inline size_t strnscpy(char *dest, size_t size, const char *src, size_t len) { + return strnscpy_full(dest, size, src, len, NULL); +} +size_t strscpy_full(char *dest, size_t size, const char *src, bool *ret_truncated); +static inline size_t strscpy(char *dest, size_t size, const char *src) { + return strscpy_full(dest, size, src, NULL); +} +size_t strscpyl_full(char *dest, size_t size, bool *ret_truncated, const char *src, ...) _sentinel_; +#define strscpyl(dest, size, src, ...) \ + strscpyl_full(dest, size, NULL, src, ##__VA_ARGS__) diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 79bb33df8..6119f21c1 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -432,7 +432,6 @@ int acquire_terminal( for (;;) { union inotify_event_buffer buffer; - struct inotify_event *e; ssize_t l; if (timeout != USEC_INFINITY) { diff --git a/src/basic/time-util.c b/src/basic/time-util.c index b659d6905..ad4a189ff 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -77,7 +77,7 @@ triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { ts->realtime = now(CLOCK_REALTIME); ts->monotonic = now(CLOCK_MONOTONIC); - ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY; + ts->boottime = now(CLOCK_BOOTTIME); return ts; } @@ -126,7 +126,7 @@ usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) { dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { assert(ts); - if (u == USEC_INFINITY || u == 0) { + if (!timestamp_is_set(u)) { ts->realtime = ts->monotonic = u; return ts; } @@ -141,7 +141,7 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) assert(ts); - if (u == USEC_INFINITY || u == 0) { + if (!timestamp_is_set(u)) { ts->realtime = ts->monotonic = ts->boottime = u; return ts; } @@ -150,9 +150,7 @@ triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) ts->realtime = u; ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC)); - ts->boottime = clock_boottime_supported() ? - map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)) : - USEC_INFINITY; + ts->boottime = map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME)); return ts; } @@ -170,8 +168,7 @@ dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { return ts; } -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { - clockid_t cid; +dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) { usec_t nowm; if (u == USEC_INFINITY) { @@ -179,14 +176,8 @@ dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, us return ts; } - cid = clock_boottime_or_monotonic(); - nowm = now(cid); - - if (cid == CLOCK_MONOTONIC) - ts->monotonic = u; - else - ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC)); - + nowm = now(CLOCK_BOOTTIME); + ts->monotonic = map_clock_usec_internal(u, nowm, now(CLOCK_MONOTONIC)); ts->realtime = map_clock_usec_internal(u, nowm, now(CLOCK_REALTIME)); return ts; } @@ -320,11 +311,13 @@ char *format_timestamp_style( time_t sec; size_t n; bool utc = false, us = false; + int r; assert(buf); switch (style) { case TIMESTAMP_PRETTY: + case TIMESTAMP_UNIX: break; case TIMESTAMP_US: us = true; @@ -347,9 +340,17 @@ char *format_timestamp_style( 1 + 1 + /* space and shortest possible zone */ 1)) return NULL; /* Not enough space even for the shortest form. */ - if (t <= 0 || t == USEC_INFINITY) + if (!timestamp_is_set(t)) return NULL; /* Timestamp is unset */ + if (style == TIMESTAMP_UNIX) { + r = snprintf(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down µs → s */ + if (r < 0 || (size_t) r >= l) + return NULL; /* Doesn't fit */ + + return buf; + } + /* Let's not format times with years > 9999 */ if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) { assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1); @@ -417,7 +418,7 @@ char *format_timestamp_relative(char *buf, size_t l, usec_t t) { const char *s; usec_t n, d; - if (t <= 0 || t == USEC_INFINITY) + if (!timestamp_is_set(t)) return NULL; n = now(CLOCK_REALTIME); @@ -788,6 +789,16 @@ static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) { goto from_tm; } + /* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */ + tm = copy; + k = strptime(t, "%b %d %H:%M:%S", &tm); + if (k) { + if (*k == '.') + goto parse_usec; + else if (*k == 0) + goto from_tm; + } + tm = copy; k = strptime(t, "%y-%m-%d %H:%M", &tm); if (k && *k == 0) { @@ -1441,33 +1452,6 @@ int verify_timezone(const char *name, int log_level) { return 0; } -bool clock_boottime_supported(void) { - static int supported = -1; - - /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ - - if (supported < 0) { - int fd; - - fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC); - if (fd < 0) - supported = false; - else { - safe_close(fd); - supported = true; - } - } - - return supported; -} - -clockid_t clock_boottime_or_monotonic(void) { - if (clock_boottime_supported()) - return CLOCK_BOOTTIME; - else - return CLOCK_MONOTONIC; -} - bool clock_supported(clockid_t clock) { struct timespec ts; @@ -1475,16 +1459,10 @@ bool clock_supported(clockid_t clock) { case CLOCK_MONOTONIC: case CLOCK_REALTIME: + case CLOCK_BOOTTIME: + /* These three are always available in our baseline, and work in timerfd, as of kernel 3.15 */ return true; - case CLOCK_BOOTTIME: - return clock_boottime_supported(); - - case CLOCK_BOOTTIME_ALARM: - if (!clock_boottime_supported()) - return false; - - _fallthrough_; default: /* For everything else, check properly */ return clock_gettime(clock, &ts) >= 0; @@ -1632,6 +1610,7 @@ static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = { [TIMESTAMP_US] = "us", [TIMESTAMP_UTC] = "utc", [TIMESTAMP_US_UTC] = "us+utc", + [TIMESTAMP_UNIX] = "unix", }; /* Use the macro for enum → string to allow for aliases */ diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 895af8829..bf312442b 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -34,6 +34,7 @@ typedef enum TimestampStyle { TIMESTAMP_US, TIMESTAMP_UTC, TIMESTAMP_US_UTC, + TIMESTAMP_UNIX, _TIMESTAMP_STYLE_MAX, _TIMESTAMP_STYLE_INVALID = -EINVAL, } TimestampStyle; @@ -81,7 +82,7 @@ usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock); dual_timestamp* dual_timestamp_get(dual_timestamp *ts); dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u); dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u); -dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u); +dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u); triple_timestamp* triple_timestamp_get(triple_timestamp *ts); triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u); @@ -114,9 +115,13 @@ nsec_t timespec_load_nsec(const struct timespec *ts) _pure_; struct timespec* timespec_store(struct timespec *ts, usec_t u); struct timespec* timespec_store_nsec(struct timespec *ts, nsec_t n); +#define TIMESPEC_STORE(u) timespec_store(&(struct timespec) {}, (u)) + usec_t timeval_load(const struct timeval *tv) _pure_; struct timeval* timeval_store(struct timeval *tv, usec_t u); +#define TIMEVAL_STORE(u) timeval_store(&(struct timeval) {}, (u)) + char* format_timestamp_style(char *buf, size_t l, usec_t t, TimestampStyle style) _warn_unused_result_; char* format_timestamp_relative(char *buf, size_t l, usec_t t) _warn_unused_result_; char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) _warn_unused_result_; @@ -150,9 +155,7 @@ static inline bool timezone_is_valid(const char *name, int log_level) { return verify_timezone(name, log_level) >= 0; } -bool clock_boottime_supported(void); bool clock_supported(clockid_t clock); -clockid_t clock_boottime_or_monotonic(void); usec_t usec_shift_clock(usec_t, clockid_t from, clockid_t to); diff --git a/src/basic/tmpfile-util.c b/src/basic/tmpfile-util.c index cf3bbad1c..e0a338c16 100644 --- a/src/basic/tmpfile-util.c +++ b/src/basic/tmpfile-util.c @@ -275,6 +275,28 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { return fd; } +int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file) { + _cleanup_free_ char *path = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_close_ int fd = -1; + + assert(target); + assert(ret_file); + assert(ret_path); + + fd = open_tmpfile_linkable(target, flags, &path); + if (fd < 0) + return fd; + + f = take_fdopen(&fd, "w"); + if (!f) + return -ENOMEM; + + *ret_path = TAKE_PTR(path); + *ret_file = TAKE_PTR(f); + return 0; +} + int link_tmpfile(int fd, const char *path, const char *target) { assert(fd >= 0); assert(target); @@ -292,6 +314,23 @@ int link_tmpfile(int fd, const char *path, const char *target) { return RET_NERRNO(linkat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), AT_FDCWD, target, AT_SYMLINK_FOLLOW)); } +int flink_tmpfile(FILE *f, const char *path, const char *target) { + int fd, r; + + assert(f); + assert(target); + + fd = fileno(f); + if (fd < 0) /* Not all FILE* objects encapsulate fds */ + return -EBADF; + + r = fflush_sync_and_check(f); + if (r < 0) + return r; + + return link_tmpfile(fd, path, target); +} + int mkdtemp_malloc(const char *template, char **ret) { _cleanup_free_ char *p = NULL; int r; diff --git a/src/basic/tmpfile-util.h b/src/basic/tmpfile-util.h index 45255fc06..610cbaf87 100644 --- a/src/basic/tmpfile-util.h +++ b/src/basic/tmpfile-util.h @@ -13,7 +13,9 @@ int tempfn_random_child(const char *p, const char *extra, char **ret); int open_tmpfile_unlinkable(const char *directory, int flags); int open_tmpfile_linkable(const char *target, int flags, char **ret_path); +int fopen_tmpfile_linkable(const char *target, int flags, char **ret_path, FILE **ret_file); int link_tmpfile(int fd, const char *path, const char *target); +int flink_tmpfile(FILE *f, const char *path, const char *target); int mkdtemp_malloc(const char *template, char **ret); diff --git a/src/basic/unit-file.c b/src/basic/unit-file.c index faea92f66..7c1ae515e 100644 --- a/src/basic/unit-file.c +++ b/src/basic/unit-file.c @@ -69,7 +69,7 @@ int unit_symlink_name_compatible(const char *symlink, const char *target, bool i return 0; } -int unit_validate_alias_symlink_and_warn(const char *filename, const char *target) { +int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target) { const char *src, *dst; _cleanup_free_ char *src_instance = NULL, *dst_instance = NULL; UnitType src_unit_type, dst_unit_type; @@ -82,7 +82,8 @@ int unit_validate_alias_symlink_and_warn(const char *filename, const char *targe * * -EINVAL is returned if the something is wrong with the source filename or the source unit type is * not allowed to symlink, - * -EXDEV if the target filename is not a valid unit name or doesn't match the source. + * -EXDEV if the target filename is not a valid unit name or doesn't match the source, + * -ELOOP for an alias to self. */ src = basename(filename); @@ -92,51 +93,56 @@ int unit_validate_alias_symlink_and_warn(const char *filename, const char *targe src_name_type = unit_name_to_instance(src, &src_instance); if (src_name_type < 0) - return log_notice_errno(src_name_type, - "%s: not a valid unit name \"%s\": %m", filename, src); + return log_full_errno(log_level, src_name_type, + "%s: not a valid unit name \"%s\": %m", filename, src); src_unit_type = unit_name_to_type(src); assert(src_unit_type >= 0); /* unit_name_to_instance() checked the suffix already */ if (!unit_type_may_alias(src_unit_type)) - return log_notice_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: symlinks are not allowed for units of this type, rejecting.", - filename); + return log_full_errno(log_level, SYNTHETIC_ERRNO(EINVAL), + "%s: symlinks are not allowed for units of this type, rejecting.", + filename); if (src_name_type != UNIT_NAME_PLAIN && !unit_type_may_template(src_unit_type)) - return log_notice_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: templates not allowed for %s units, rejecting.", - filename, unit_type_to_string(src_unit_type)); + return log_full_errno(log_level, SYNTHETIC_ERRNO(EINVAL), + "%s: templates not allowed for %s units, rejecting.", + filename, unit_type_to_string(src_unit_type)); /* dst checks */ + if (streq(src, dst)) + return log_debug_errno(SYNTHETIC_ERRNO(ELOOP), + "%s: unit self-alias: %s → %s, ignoring.", + filename, src, dst); + dst_name_type = unit_name_to_instance(dst, &dst_instance); if (dst_name_type < 0) - return log_notice_errno(dst_name_type == -EINVAL ? SYNTHETIC_ERRNO(EXDEV) : dst_name_type, - "%s points to \"%s\" which is not a valid unit name: %m", - filename, dst); + return log_full_errno(log_level, dst_name_type == -EINVAL ? SYNTHETIC_ERRNO(EXDEV) : dst_name_type, + "%s points to \"%s\" which is not a valid unit name: %m", + filename, dst); if (!(dst_name_type == src_name_type || (src_name_type == UNIT_NAME_INSTANCE && dst_name_type == UNIT_NAME_TEMPLATE))) - return log_notice_errno(SYNTHETIC_ERRNO(EXDEV), - "%s: symlink target name type \"%s\" does not match source, rejecting.", - filename, dst); + return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV), + "%s: symlink target name type \"%s\" does not match source, rejecting.", + filename, dst); if (dst_name_type == UNIT_NAME_INSTANCE) { assert(src_instance); assert(dst_instance); if (!streq(src_instance, dst_instance)) - return log_notice_errno(SYNTHETIC_ERRNO(EXDEV), - "%s: unit symlink target \"%s\" instance name doesn't match, rejecting.", - filename, dst); + return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV), + "%s: unit symlink target \"%s\" instance name doesn't match, rejecting.", + filename, dst); } dst_unit_type = unit_name_to_type(dst); if (dst_unit_type != src_unit_type) - return log_notice_errno(SYNTHETIC_ERRNO(EXDEV), - "%s: symlink target \"%s\" has incompatible suffix, rejecting.", - filename, dst); + return log_full_errno(log_level, SYNTHETIC_ERRNO(EXDEV), + "%s: symlink target \"%s\" has incompatible suffix, rejecting.", + filename, dst); return 0; } @@ -209,8 +215,7 @@ bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_ siphash24_init(&state, HASH_KEY.bytes); - char **dir; - STRV_FOREACH(dir, (char**) lp->search_path) { + STRV_FOREACH(dir, lp->search_path) { struct stat st; if (lookup_paths_mtime_exclude(lp, *dir)) @@ -237,7 +242,6 @@ bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_ } static int directory_name_is_valid(const char *name) { - const char *suffix; /* Accept a directory whose name is a valid unit file name ending in .wants/, .requires/ or .d/ */ @@ -261,6 +265,110 @@ static int directory_name_is_valid(const char *name) { return false; } +int unit_file_resolve_symlink( + const char *root_dir, + char **search_path, + const char *dir, + int dirfd, + const char *filename, + bool resolve_destination_target, + char **ret_destination) { + + _cleanup_free_ char *target = NULL, *simplified = NULL, *dst = NULL, *_dir = NULL, *_filename = NULL; + int r; + + /* This can be called with either dir+dirfd valid and filename just a name, + * or !dir && dirfd==AT_FDCWD, and filename being a full path. + * + * If resolve_destination_target is true, an absolute path will be returned. + * If not, an absolute path is returned for linked unit files, and a relative + * path otherwise. + * + * Returns an error, false if this is an alias, true if it's a linked unit file. */ + + assert(filename); + assert(ret_destination); + assert(dir || path_is_absolute(filename)); + assert(dirfd >= 0 || dirfd == AT_FDCWD); + + r = readlinkat_malloc(dirfd, filename, &target); + if (r < 0) + return log_warning_errno(r, "Failed to read symlink %s%s%s: %m", + dir, dir ? "/" : "", filename); + + if (!dir) { + r = path_extract_directory(filename, &_dir); + if (r < 0) + return r; + dir = _dir; + + r = path_extract_filename(filename, &_filename); + if (r < 0) + return r; + if (r == O_DIRECTORY) + return log_warning_errno(SYNTHETIC_ERRNO(EISDIR), + "Unexpected path to a directory \"%s\", refusing.", filename); + filename = _filename; + } + + bool is_abs = path_is_absolute(target); + if (root_dir || !is_abs) { + char *target_abs = path_join(is_abs ? root_dir : dir, target); + if (!target_abs) + return log_oom(); + + free_and_replace(target, target_abs); + } + + /* Get rid of "." and ".." components in target path */ + r = chase_symlinks(target, root_dir, CHASE_NOFOLLOW | CHASE_NONEXISTENT, &simplified, NULL); + if (r < 0) + return log_warning_errno(r, "Failed to resolve symlink %s/%s pointing to %s: %m", + dir, filename, target); + + assert(path_is_absolute(simplified)); + + /* Check if the symlink remain inside of of our search path. + * If yes, it is an alias. Verify that it is valid. + * + * If no, then this is a linked unit file or mask, and we don't care about the target name + * when loading units, and we return the link *source* (resolve_destination_target == false); + * When this is called for installation purposes, we want the final destination, + * so we return the *target*. + */ + const char *tail = path_startswith_strv(simplified, search_path); + if (tail) { /* An alias */ + _cleanup_free_ char *target_name = NULL; + + r = path_extract_filename(simplified, &target_name); + if (r < 0) + return r; + + r = unit_validate_alias_symlink_or_warn(LOG_NOTICE, filename, simplified); + if (r < 0) + return r; + if (is_path(tail)) + log_warning("Suspicious symlink %s/%s→%s, treating as alias.", + dir, filename, simplified); + + dst = resolve_destination_target ? TAKE_PTR(simplified) : TAKE_PTR(target_name); + + } else { + log_debug("Linked unit file: %s/%s → %s", dir, filename, simplified); + + if (resolve_destination_target) + dst = TAKE_PTR(simplified); + else { + dst = path_join(dir, filename); + if (!dst) + return log_oom(); + } + } + + *ret_destination = TAKE_PTR(dst); + return !tail; /* true if linked unit file */ +} + int unit_file_build_name_map( const LookupPaths *lp, uint64_t *cache_timestamp_hash, @@ -281,7 +389,6 @@ int unit_file_build_name_map( _cleanup_hashmap_free_ Hashmap *ids = NULL, *names = NULL; _cleanup_set_free_free_ Set *paths = NULL; uint64_t timestamp_hash; - char **dir; int r; /* Before doing anything, check if the timestamp hash that was passed is still valid. @@ -299,7 +406,7 @@ int unit_file_build_name_map( return log_oom(); } - STRV_FOREACH(dir, (char**) lp->search_path) { + STRV_FOREACH(dir, lp->search_path) { _cleanup_closedir_ DIR *d = NULL; d = opendir(*dir); @@ -311,10 +418,9 @@ int unit_file_build_name_map( FOREACH_DIRENT_ALL(de, d, log_warning_errno(errno, "Failed to read \"%s\", ignoring: %m", *dir)) { _unused_ _cleanup_free_ char *_filename_free = NULL; - _cleanup_free_ char *simplified = NULL; - bool symlink_to_dir = false; - const char *dst = NULL; char *filename; + _cleanup_free_ char *dst = NULL; + bool symlink_to_dir = false; /* We only care about valid units and dirs with certain suffixes, let's ignore the * rest. */ @@ -398,77 +504,35 @@ int unit_file_build_name_map( /* We don't explicitly check for alias loops here. unit_ids_map_get() which * limits the number of hops should be used to access the map. */ - _cleanup_free_ char *target = NULL; - - r = readlinkat_malloc(dirfd(d), de->d_name, &target); - if (r < 0) { - log_warning_errno(r, "Failed to read symlink %s/%s, ignoring: %m", - *dir, de->d_name); + r = unit_file_resolve_symlink(lp->root_dir, lp->search_path, + *dir, dirfd(d), de->d_name, + /* resolve_destination_target= */ false, + &dst); + if (r == -ENOMEM) + return r; + if (r < 0) /* we ignore other errors here */ continue; - } - - const bool is_abs = path_is_absolute(target); - if (lp->root_dir || !is_abs) { - char *target_abs = path_join(is_abs ? lp->root_dir : *dir, target); - if (!target_abs) - return log_oom(); - - free_and_replace(target, target_abs); - } - - /* Get rid of "." and ".." components in target path */ - r = chase_symlinks(target, lp->root_dir, CHASE_NOFOLLOW | CHASE_NONEXISTENT, &simplified, NULL); - if (r < 0) { - log_warning_errno(r, "Failed to resolve symlink %s pointing to %s, ignoring: %m", - filename, target); - continue; - } - - /* Check if the symlink goes outside of our search path. - * If yes, it's a linked unit file or mask, and we don't care about the target name. - * Let's just store the link source directly. - * If not, let's verify that it's a good symlink. */ - char *tail = path_startswith_strv(simplified, lp->search_path); - if (!tail) { - log_debug("%s: linked unit file: %s → %s", - __func__, filename, simplified); - - dst = filename; - } else { - - bool self_alias; - - dst = basename(simplified); - self_alias = streq(dst, de->d_name); - - if (is_path(tail)) - log_full(self_alias ? LOG_DEBUG : LOG_WARNING, - "Suspicious symlink %s→%s, treating as alias.", - filename, simplified); - - r = unit_validate_alias_symlink_and_warn(filename, simplified); - if (r < 0) - continue; - - if (self_alias) { - /* A self-alias that has no effect */ - log_debug("%s: self-alias: %s/%s → %s, ignoring.", - __func__, *dir, de->d_name, dst); - continue; - } - - log_debug("%s: alias: %s/%s → %s", __func__, *dir, de->d_name, dst); - } } else { - dst = filename; + dst = TAKE_PTR(_filename_free); /* Grab the copy we made previously, if available. */ + if (!dst) { + dst = strdup(filename); + if (!dst) + return log_oom(); + } + log_debug("%s: normal unit file: %s", __func__, dst); } - r = hashmap_put_strdup(&ids, de->d_name, dst); + _cleanup_free_ char *key = strdup(de->d_name); + if (!key) + return log_oom(); + + r = hashmap_ensure_put(&ids, &string_hash_ops_free_free, key, dst); if (r < 0) return log_warning_errno(r, "Failed to add entry to hashmap (%s→%s): %m", de->d_name, dst); + key = dst = NULL; } } @@ -550,7 +614,7 @@ static int add_names( Set **names, const char *name) { - char **aliases, **alias; + char **aliases; int r; assert(name_type == UNIT_NAME_PLAIN || instance); diff --git a/src/basic/unit-file.h b/src/basic/unit-file.h index cc731a9e0..1c43861f0 100644 --- a/src/basic/unit-file.h +++ b/src/basic/unit-file.h @@ -4,12 +4,11 @@ #include #include "hashmap.h" +#include "path-lookup.h" #include "time-util.h" #include "unit-name.h" typedef enum UnitFileState UnitFileState; -typedef enum UnitFileScope UnitFileScope; -typedef struct LookupPaths LookupPaths; enum UnitFileState { UNIT_FILE_ENABLED, @@ -29,21 +28,23 @@ enum UnitFileState { _UNIT_FILE_STATE_INVALID = -EINVAL, }; -enum UnitFileScope { - UNIT_FILE_SYSTEM, - UNIT_FILE_GLOBAL, - UNIT_FILE_USER, - _UNIT_FILE_SCOPE_MAX, - _UNIT_FILE_SCOPE_INVALID = -EINVAL, -}; - bool unit_type_may_alias(UnitType type) _const_; bool unit_type_may_template(UnitType type) _const_; int unit_symlink_name_compatible(const char *symlink, const char *target, bool instance_propagation); -int unit_validate_alias_symlink_and_warn(const char *filename, const char *target); +int unit_validate_alias_symlink_or_warn(int log_level, const char *filename, const char *target); bool lookup_paths_timestamp_hash_same(const LookupPaths *lp, uint64_t timestamp_hash, uint64_t *ret_new); + +int unit_file_resolve_symlink( + const char *root_dir, + char **search_path, + const char *dir, + int dirfd, + const char *filename, + bool resolve_destination_target, + char **ret_destination); + int unit_file_build_name_map( const LookupPaths *lp, uint64_t *cache_timestamp_hash, diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c index 671e30a53..0a4843f45 100644 --- a/src/basic/unit-name.c +++ b/src/basic/unit-name.c @@ -57,7 +57,7 @@ bool unit_name_is_valid(const char *n, UnitNameFlags flags) { if (*i == '@' && !at) at = i; - if (!strchr("@" VALID_CHARS, *i)) + if (!strchr(VALID_CHARS_WITH_AT, *i)) return false; } diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 63bef51b0..da3b922bc 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -247,7 +247,7 @@ int get_user_creds( else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !gid && !home && !shell) { /* If the specified user is a numeric UID and it isn't in the user database, and the caller - * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then juts return that + * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then just return that * and don't complain. */ if (uid) diff --git a/src/basic/user-util.h b/src/basic/user-util.h index bc76de6b4..e1692c4f6 100644 --- a/src/basic/user-util.h +++ b/src/basic/user-util.h @@ -12,6 +12,14 @@ #include #include +/* Users managed by systemd-homed. See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */ +#define HOME_UID_MIN 60001 +#define HOME_UID_MAX 60513 + +/* Users mapped from host into a container */ +#define MAP_UID_MIN 60514 +#define MAP_UID_MAX 60577 + bool uid_is_valid(uid_t uid); static inline bool gid_is_valid(gid_t gid) { @@ -59,6 +67,19 @@ int take_etc_passwd_lock(const char *root); #define UID_NOBODY ((uid_t) 65534U) #define GID_NOBODY ((gid_t) 65534U) +/* If REMOUNT_IDMAP_HOST_ROOT is set for remount_idmap() we'll include a mapping here that maps the host root + * user accessing the idmapped mount to the this user ID on the backing fs. This is the last valid UID in the + * *signed* 32bit range. You might wonder why precisely use this specific UID for this purpose? Well, we + * definitely cannot use the first 0…65536 UIDs for that, since in most cases that's precisely the file range + * we intend to map to some high UID range, and since UID mappings have to be bijective we thus cannot use + * them at all. Furthermore the UID range beyond INT32_MAX (i.e. the range above the signed 32bit range) is + * icky, since many APIs cannot use it (example: setfsuid() returns the old UID as signed integer). Following + * our usual logic of assigning a 16bit UID range to each container, so that the upper 16bit of a 32bit UID + * value indicate kind of a "container ID" and the lower 16bit map directly to the intended user you can read + * this specific UID as the "nobody" user of the container with ID 0x7FFF, which is kinda nice. */ +#define UID_MAPPED_ROOT ((uid_t) (INT32_MAX-1)) +#define GID_MAPPED_ROOT ((gid_t) (INT32_MAX-1)) + #define ETC_PASSWD_LOCK_PATH "/etc/.pwd.lock" /* The following macros add 1 when converting things, since UID 0 is a valid UID, while the pointer diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c index ebd620604..ac4fd7b1c 100644 --- a/src/basic/xattr-util.c +++ b/src/basic/xattr-util.c @@ -199,7 +199,7 @@ int fd_setcrtime(int fd, usec_t usec) { assert(fd >= 0); - if (IN_SET(usec, 0, USEC_INFINITY)) + if (!timestamp_is_set(usec)) usec = now(CLOCK_REALTIME); le = htole64((uint64_t) usec); diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c index 18231c261..817ee387f 100644 --- a/src/binfmt/binfmt.c +++ b/src/binfmt/binfmt.c @@ -213,7 +213,6 @@ static int run(int argc, char *argv[]) { } else { _cleanup_strv_free_ char **files = NULL; - char **f; r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("binfmt.d")); if (r < 0) diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c index 9e4b0d1f7..c8bc205d1 100644 --- a/src/boot/bless-boot.c +++ b/src/boot/bless-boot.c @@ -5,15 +5,18 @@ #include "alloc-util.h" #include "bootspec.h" +#include "efi-api.h" #include "efi-loader.h" #include "efivars.h" #include "fd-util.h" +#include "find-esp.h" #include "fs-util.h" #include "log.h" #include "main-func.h" #include "parse-util.h" #include "path-util.h" #include "pretty-print.h" +#include "stat-util.h" #include "sync-util.h" #include "terminal-util.h" #include "util.h" @@ -98,17 +101,18 @@ static int parse_argv(int argc, char *argv[]) { static int acquire_path(void) { _cleanup_free_ char *esp_path = NULL, *xbootldr_path = NULL; + dev_t esp_devid = 0, xbootldr_devid = 0; char **a; int r; if (!strv_isempty(arg_path)) return 0; - r = find_esp_and_warn(NULL, false, &esp_path, NULL, NULL, NULL, NULL); + r = find_esp_and_warn(NULL, /* unprivileged_mode= */ false, &esp_path, NULL, NULL, NULL, NULL, &esp_devid); if (r < 0 && r != -ENOKEY) /* ENOKEY means not found, and is the only error the function won't log about on its own */ return r; - r = find_xbootldr_and_warn(NULL, false, &xbootldr_path, NULL); + r = find_xbootldr_and_warn(NULL, /* unprivileged_mode= */ false, &xbootldr_path, NULL, &xbootldr_devid); if (r < 0 && r != -ENOKEY) return r; @@ -117,8 +121,10 @@ static int acquire_path(void) { "Couldn't find $BOOT partition. It is recommended to mount it to /boot.\n" "Alternatively, use --path= to specify path to mount point."); - if (esp_path) + if (esp_path && xbootldr_path && !devid_set_and_equal(esp_devid, xbootldr_devid)) /* in case the two paths refer to the same inode, suppress one */ a = strv_new(esp_path, xbootldr_path); + else if (esp_path) + a = strv_new(esp_path); else a = strv_new(xbootldr_path); if (!a) @@ -130,7 +136,7 @@ static int acquire_path(void) { _cleanup_free_ char *j = NULL; j = strv_join(arg_path, ":"); - log_debug("Using %s as boot loader drop-in search path.", j); + log_debug("Using %s as boot loader drop-in search path.", strna(j)); } return 0; @@ -320,7 +326,6 @@ static const char *skip_slash(const char *path) { static int verb_status(int argc, char *argv[], void *userdata) { _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL; uint64_t left, done; - char **p; int r; r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix); @@ -397,7 +402,6 @@ static int verb_set(int argc, char *argv[], void *userdata) { _cleanup_free_ char *path = NULL, *prefix = NULL, *suffix = NULL, *good = NULL, *bad = NULL, *parent = NULL; const char *target, *source1, *source2; uint64_t done; - char **p; int r; r = acquire_boot_count_path(&path, &prefix, NULL, &done, &suffix); diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index ebe257512..759b3ca86 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -17,6 +17,7 @@ #include "bootspec.h" #include "copy.h" #include "dirent-util.h" +#include "efi-api.h" #include "efi-loader.h" #include "efivars.h" #include "env-file.h" @@ -24,10 +25,12 @@ #include "escape.h" #include "fd-util.h" #include "fileio.h" +#include "find-esp.h" #include "fs-util.h" #include "glyph-util.h" #include "main-func.h" #include "mkdir.h" +#include "os-util.h" #include "pager.h" #include "parse-argument.h" #include "parse-util.h" @@ -36,6 +39,7 @@ #include "rm-rf.h" #include "stat-util.h" #include "stdio-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" #include "sync-util.h" @@ -55,13 +59,23 @@ static bool arg_print_dollar_boot_path = false; static bool arg_touch_variables = true; static PagerFlags arg_pager_flags = 0; static bool arg_graceful = false; -static int arg_make_machine_id_directory = 0; +static int arg_make_entry_directory = false; /* tri-state: < 0 for automatic logic */ static sd_id128_t arg_machine_id = SD_ID128_NULL; static char *arg_install_layout = NULL; +static enum { + ARG_ENTRY_TOKEN_MACHINE_ID, + ARG_ENTRY_TOKEN_OS_IMAGE_ID, + ARG_ENTRY_TOKEN_OS_ID, + ARG_ENTRY_TOKEN_LITERAL, + ARG_ENTRY_TOKEN_AUTO, +} arg_entry_token_type = ARG_ENTRY_TOKEN_AUTO; +static char *arg_entry_token = NULL; +static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep); STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep); STATIC_DESTRUCTOR_REGISTER(arg_install_layout, freep); +STATIC_DESTRUCTOR_REGISTER(arg_entry_token, freep); static const char *arg_dollar_boot_path(void) { /* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */ @@ -74,7 +88,8 @@ static int acquire_esp( uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, - sd_id128_t *ret_uuid) { + sd_id128_t *ret_uuid, + dev_t *ret_devid) { char *np; int r; @@ -85,7 +100,7 @@ static int acquire_esp( * we simply eat up the error here, so that --list and --status work too, without noise about * this). */ - r = find_esp_and_warn(arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid); + r = find_esp_and_warn(arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid); if (r == -ENOKEY) { if (graceful) return log_info_errno(r, "Couldn't find EFI system partition, skipping."); @@ -103,16 +118,23 @@ static int acquire_esp( return 1; } -static int acquire_xbootldr(bool unprivileged_mode, sd_id128_t *ret_uuid) { +static int acquire_xbootldr( + bool unprivileged_mode, + sd_id128_t *ret_uuid, + dev_t *ret_devid) { + char *np; int r; - r = find_xbootldr_and_warn(arg_xbootldr_path, unprivileged_mode, &np, ret_uuid); + r = find_xbootldr_and_warn(arg_xbootldr_path, unprivileged_mode, &np, ret_uuid, ret_devid); if (r == -ENOKEY) { log_debug_errno(r, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT."); + arg_xbootldr_path = mfree(arg_xbootldr_path); + if (ret_uuid) *ret_uuid = SD_ID128_NULL; - arg_xbootldr_path = mfree(arg_xbootldr_path); + if (ret_devid) + *ret_devid = 0; return 0; } if (r < 0) @@ -124,69 +146,210 @@ static int acquire_xbootldr(bool unprivileged_mode, sd_id128_t *ret_uuid) { return 1; } -static int load_install_machine_id_and_layout(void) { - /* Figure out the right machine-id for operations. If KERNEL_INSTALL_MACHINE_ID is configured in - * /etc/machine-info, let's use that. Otherwise, just use the real machine-id. - * - * Also load KERNEL_INSTALL_LAYOUT. - */ +static int load_etc_machine_id(void) { + int r; + + r = sd_id128_get_machine(&arg_machine_id); + if (IN_SET(r, -ENOENT, -ENOMEDIUM)) /* Not set or empty */ + return 0; + if (r < 0) + return log_error_errno(r, "Failed to get machine-id: %m"); + + log_debug("Loaded machine ID %s from /etc/machine-id.", SD_ID128_TO_STRING(arg_machine_id)); + return 0; +} + +static int load_etc_machine_info(void) { + /* systemd v250 added support to store the kernel-install layout setting and the machine ID to use + * for setting up the ESP in /etc/machine-info. The newer /etc/kernel/entry-token file, as well as + * the $layout field in /etc/kernel/install.conf are better replacements for this though, hence this + * has been deprecated and is only returned for compatibility. */ _cleanup_free_ char *s = NULL, *layout = NULL; int r; r = parse_env_file(NULL, "/etc/machine-info", "KERNEL_INSTALL_LAYOUT", &layout, "KERNEL_INSTALL_MACHINE_ID", &s); - if (r < 0 && r != -ENOENT) + if (r == -ENOENT) + return 0; + if (r < 0) return log_error_errno(r, "Failed to parse /etc/machine-info: %m"); - if (isempty(s)) { - r = sd_id128_get_machine(&arg_machine_id); - if (r < 0 && !IN_SET(r, -ENOENT, -ENOMEDIUM)) - return log_error_errno(r, "Failed to get machine-id: %m"); - } else { + if (!isempty(s)) { + log_notice("Read $KERNEL_INSTALL_MACHINE_ID from /etc/machine-info. Please move it to /etc/kernel/entry-token."); + r = sd_id128_from_string(s, &arg_machine_id); if (r < 0) return log_error_errno(r, "Failed to parse KERNEL_INSTALL_MACHINE_ID=%s in /etc/machine-info: %m", s); + log_debug("Loaded KERNEL_INSTALL_MACHINE_ID=%s from KERNEL_INSTALL_MACHINE_ID in /etc/machine-info.", + SD_ID128_TO_STRING(arg_machine_id)); } - log_debug("Using KERNEL_INSTALL_MACHINE_ID=%s from %s.", - SD_ID128_TO_STRING(arg_machine_id), - isempty(s) ? "/etc/machine_id" : "KERNEL_INSTALL_MACHINE_ID in /etc/machine-info"); if (!isempty(layout)) { + log_notice("Read $KERNEL_INSTALL_LAYOUT from /etc/machine-info. Please move it to the layout= setting of /etc/kernel/install.conf."); + log_debug("KERNEL_INSTALL_LAYOUT=%s is specified in /etc/machine-info.", layout); - arg_install_layout = TAKE_PTR(layout); + free_and_replace(arg_install_layout, layout); } return 0; } -static int settle_install_machine_id(void) { +static int load_etc_kernel_install_conf(void) { + _cleanup_free_ char *layout = NULL; int r; - r = load_install_machine_id_and_layout(); + r = parse_env_file(NULL, "/etc/kernel/install.conf", + "layout", &layout); + if (r == -ENOENT) + return 0; + if (r < 0) + return log_error_errno(r, "Failed to parse /etc/kernel/install.conf: %m"); + + if (!isempty(layout)) { + log_debug("layout=%s is specified in /etc/machine-info.", layout); + free_and_replace(arg_install_layout, layout); + } + + return 0; +} + +static int settle_entry_token(void) { + int r; + + switch (arg_entry_token_type) { + + case ARG_ENTRY_TOKEN_AUTO: { + _cleanup_free_ char *buf = NULL; + r = read_one_line_file("/etc/kernel/entry-token", &buf); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to read /etc/kernel/entry-token: %m"); + + if (!isempty(buf)) { + free_and_replace(arg_entry_token, buf); + arg_entry_token_type = ARG_ENTRY_TOKEN_LITERAL; + } else if (sd_id128_is_null(arg_machine_id)) { + _cleanup_free_ char *id = NULL, *image_id = NULL; + + r = parse_os_release(NULL, + "IMAGE_ID", &image_id, + "ID", &id); + if (r < 0) + return log_error_errno(r, "Failed to load /etc/os-release: %m"); + + if (!isempty(image_id)) { + free_and_replace(arg_entry_token, image_id); + arg_entry_token_type = ARG_ENTRY_TOKEN_OS_IMAGE_ID; + } else if (!isempty(id)) { + free_and_replace(arg_entry_token, id); + arg_entry_token_type = ARG_ENTRY_TOKEN_OS_ID; + } else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set, and /etc/os-release carries no ID=/IMAGE_ID= fields."); + } else { + r = free_and_strdup_warn(&arg_entry_token, SD_ID128_TO_STRING(arg_machine_id)); + if (r < 0) + return r; + + arg_entry_token_type = ARG_ENTRY_TOKEN_MACHINE_ID; + } + + break; + } + + case ARG_ENTRY_TOKEN_MACHINE_ID: + if (sd_id128_is_null(arg_machine_id)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No machine ID set."); + + r = free_and_strdup_warn(&arg_entry_token, SD_ID128_TO_STRING(arg_machine_id)); + if (r < 0) + return r; + + break; + + case ARG_ENTRY_TOKEN_OS_IMAGE_ID: { + _cleanup_free_ char *buf = NULL; + + r = parse_os_release(NULL, "IMAGE_ID", &buf); + if (r < 0) + return log_error_errno(r, "Failed to load /etc/os-release: %m"); + + if (isempty(buf)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "IMAGE_ID= field not set in /etc/os-release."); + + free_and_replace(arg_entry_token, buf); + break; + } + + case ARG_ENTRY_TOKEN_OS_ID: { + _cleanup_free_ char *buf = NULL; + + r = parse_os_release(NULL, "ID", &buf); + if (r < 0) + return log_error_errno(r, "Failed to load /etc/os-release: %m"); + + if (isempty(buf)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "ID= field not set in /etc/os-release."); + + free_and_replace(arg_entry_token, buf); + break; + } + + case ARG_ENTRY_TOKEN_LITERAL: + assert(!isempty(arg_entry_token)); /* already filled in by command line parser */ + break; + } + + if (isempty(arg_entry_token) || !string_is_safe(arg_entry_token)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected entry token not valid: %s", arg_entry_token); + + log_debug("Using entry token: %s", arg_entry_token); + return 0; +} + +static bool use_boot_loader_spec_type1(void) { + /* If the layout is not specified, or if it is set explicitly to "bls" we assume Boot Loader + * Specification Type #1 is the chosen format for our boot loader entries */ + return !arg_install_layout || streq(arg_install_layout, "bls"); +} + +static int settle_make_entry_directory(void) { + int r; + + r = load_etc_machine_id(); if (r < 0) return r; - bool layout_non_bls = arg_install_layout && !streq(arg_install_layout, "bls"); - if (arg_make_machine_id_directory < 0) { - if (layout_non_bls || sd_id128_is_null(arg_machine_id)) - arg_make_machine_id_directory = 0; - else { - r = path_is_temporary_fs("/etc/machine-id"); - if (r < 0) - return log_debug_errno(r, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m"); - arg_make_machine_id_directory = r == 0; - } + r = load_etc_machine_info(); + if (r < 0) + return r; + + r = load_etc_kernel_install_conf(); + if (r < 0) + return r; + + r = settle_entry_token(); + if (r < 0) + return r; + + bool layout_type1 = use_boot_loader_spec_type1(); + if (arg_make_entry_directory < 0) { /* Automatic mode */ + if (layout_type1) { + if (arg_entry_token == ARG_ENTRY_TOKEN_MACHINE_ID) { + r = path_is_temporary_fs("/etc/machine-id"); + if (r < 0) + return log_debug_errno(r, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m"); + + arg_make_entry_directory = r == 0; + } else + arg_make_entry_directory = true; + } else + arg_make_entry_directory = false; } - if (arg_make_machine_id_directory > 0 && sd_id128_is_null(arg_machine_id)) + if (arg_make_entry_directory > 0 && !layout_type1) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Machine ID not found, but bls directory creation was requested."); - - if (arg_make_machine_id_directory > 0 && layout_non_bls) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "KERNEL_INSTALL_LAYOUT=%s is configured, but bls directory creation was requested.", + "KERNEL_INSTALL_LAYOUT=%s is configured, but Boot Loader Specification Type #1 entry directory creation was requested.", arg_install_layout); return 0; @@ -411,7 +574,21 @@ static void boot_entry_file_list(const char *field, const char *root, const char *ret_status = status; } -static int boot_entry_show(const BootEntry *e, bool show_as_default) { +static const char* const boot_entry_type_table[_BOOT_ENTRY_TYPE_MAX] = { + [BOOT_ENTRY_CONF] = "Boot Loader Specification Type #1 (.conf)", + [BOOT_ENTRY_UNIFIED] = "Boot Loader Specification Type #2 (.efi)", + [BOOT_ENTRY_LOADER] = "Reported by Boot Loader", + [BOOT_ENTRY_LOADER_AUTO] = "Automatic", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(boot_entry_type, BootEntryType); + +static int boot_entry_show( + const BootEntry *e, + bool show_as_default, + bool show_as_selected, + bool show_reported) { + int status = 0; /* Returns 0 on success, negative on processing error, and positive if something is wrong with the @@ -419,9 +596,30 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) { assert(e); - printf(" title: %s%s%s" "%s%s%s\n", - ansi_highlight(), boot_entry_title(e), ansi_normal(), - ansi_highlight_green(), show_as_default ? " (default)" : "", ansi_normal()); + printf(" type: %s\n", + boot_entry_type_to_string(e->type)); + + printf(" title: %s%s%s", + ansi_highlight(), boot_entry_title(e), ansi_normal()); + + if (show_as_default) + printf(" %s(default)%s", + ansi_highlight_green(), ansi_normal()); + + if (show_as_selected) + printf(" %s(selected)%s", + ansi_highlight_magenta(), ansi_normal()); + + if (show_reported) { + if (e->type == BOOT_ENTRY_LOADER) + printf(" %s(reported/absent)%s", + ansi_highlight_red(), ansi_normal()); + else if (!e->reported_by_loader && e->type != BOOT_ENTRY_LOADER_AUTO) + printf(" %s(not reported/new)%s", + ansi_highlight_green(), ansi_normal()); + } + + putchar('\n'); if (e->id) printf(" id: %s\n", e->id); @@ -435,6 +633,8 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) { printf(" source: %s\n", link ?: e->path); } + if (e->sort_key) + printf(" sort-key: %s\n", e->sort_key); if (e->version) printf(" version: %s\n", e->version); if (e->machine_id) @@ -444,12 +644,12 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) { if (e->kernel) boot_entry_file_list("linux", e->root, e->kernel, &status); - char **s; STRV_FOREACH(s, e->initrd) boot_entry_file_list(s == e->initrd ? "initrd" : NULL, e->root, *s, &status); + if (!strv_isempty(e->options)) { _cleanup_free_ char *t = NULL, *t2 = NULL; _cleanup_strv_free_ char **ts = NULL; @@ -468,9 +668,16 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) { printf(" options: %s\n", t2); } + if (e->device_tree) boot_entry_file_list("devicetree", e->root, e->device_tree, &status); + STRV_FOREACH(s, e->device_tree_overlay) + boot_entry_file_list(s == e->device_tree_overlay ? "devicetree-overlay" : NULL, + e->root, + *s, + &status); + return -status; } @@ -480,7 +687,7 @@ static int status_entries( const char *xbootldr_path, sd_id128_t xbootldr_partition_uuid) { - _cleanup_(boot_config_free) BootConfig config = {}; + _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL; sd_id128_t dollar_boot_partition_uuid; const char *dollar_boot_path; int r; @@ -502,7 +709,11 @@ static int status_entries( SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid)); printf("\n\n"); - r = boot_entries_load_config(esp_path, xbootldr_path, &config); + r = boot_config_load(&config, esp_path, xbootldr_path); + if (r < 0) + return r; + + r = boot_config_select_special_entries(&config); if (r < 0) return r; @@ -511,7 +722,11 @@ static int status_entries( else { printf("Default Boot Loader Entry:\n"); - r = boot_entry_show(config.entries + config.default_entry, false); + r = boot_entry_show( + boot_config_default_entry(&config), + /* show_as_default= */ false, + /* show_as_selected= */ false, + /* show_discovered= */ false); if (r > 0) /* < 0 is already logged by the function itself, let's just emit an extra warning if the default entry is broken */ @@ -673,7 +888,6 @@ static const char *const dollar_boot_subdirs[] = { }; static int create_subdirs(const char *root, const char * const *subdirs) { - const char *const *i; int r; STRV_FOREACH(i, subdirs) { @@ -732,10 +946,10 @@ static int install_binaries(const char *esp_path, bool force) { /* skip the .efi file, if there's a .signed version of it */ if (endswith_no_case(de->d_name, ".efi")) { - _cleanup_free_ const char *s = strjoin(BOOTLIBDIR, "/", de->d_name, ".signed"); + _cleanup_free_ const char *s = strjoin(de->d_name, ".signed"); if (!s) return log_oom(); - if (access(s, F_OK) >= 0) + if (faccessat(dirfd(d), s, F_OK, 0) >= 0) continue; } @@ -983,14 +1197,14 @@ static int remove_subdirs(const char *root, const char *const *subdirs) { return r < 0 ? r : q; } -static int remove_machine_id_directory(const char *root) { +static int remove_entry_directory(const char *root) { assert(root); - assert(arg_make_machine_id_directory >= 0); + assert(arg_make_entry_directory >= 0); - if (!arg_make_machine_id_directory || sd_id128_is_null(arg_machine_id)) + if (!arg_make_entry_directory || !arg_entry_token) return 0; - return rmdir_one(root, SD_ID128_TO_STRING(arg_machine_id)); + return rmdir_one(root, arg_entry_token); } static int remove_binaries(const char *esp_path) { @@ -1047,12 +1261,11 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) { } static int remove_loader_variables(void) { - const char *variable; int r = 0; /* Remove all persistent loader variables we define */ - FOREACH_STRING(variable, + FOREACH_STRING(var, EFI_LOADER_VARIABLE(LoaderConfigTimeout), EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot), EFI_LOADER_VARIABLE(LoaderEntryDefault), @@ -1061,15 +1274,15 @@ static int remove_loader_variables(void) { int q; - q = efi_set_variable(variable, NULL, 0); + q = efi_set_variable(var, NULL, 0); if (q == -ENOENT) continue; if (q < 0) { - log_warning_errno(q, "Failed to remove EFI variable %s: %m", variable); + log_warning_errno(q, "Failed to remove EFI variable %s: %m", var); if (r >= 0) r = q; } else - log_info("Removed EFI variable %s.", variable); + log_info("Removed EFI variable %s.", var); } return r; @@ -1078,37 +1291,28 @@ static int remove_loader_variables(void) { static int install_loader_config(const char *esp_path) { _cleanup_(unlink_and_freep) char *t = NULL; _cleanup_fclose_ FILE *f = NULL; - _cleanup_close_ int fd = -1; const char *p; int r; - assert(arg_make_machine_id_directory >= 0); + assert(arg_make_entry_directory >= 0); p = prefix_roota(esp_path, "/loader/loader.conf"); if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */ return 0; - fd = open_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t); - if (fd < 0) - return log_error_errno(fd, "Failed to open \"%s\" for writing: %m", p); - - f = take_fdopen(&fd, "w"); - if (!f) - return log_oom(); + r = fopen_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t, &f); + if (r < 0) + return log_error_errno(r, "Failed to open \"%s\" for writing: %m", p); fprintf(f, "#timeout 3\n" "#console-mode keep\n"); - if (arg_make_machine_id_directory) { - assert(!sd_id128_is_null(arg_machine_id)); - fprintf(f, "default %s-*\n", SD_ID128_TO_STRING(arg_machine_id)); + if (arg_make_entry_directory) { + assert(arg_entry_token); + fprintf(f, "default %s-*\n", arg_entry_token); } - r = fflush_sync_and_check(f); - if (r < 0) - return log_error_errno(r, "Failed to write \"%s\": %m", p); - - r = link_tmpfile(fileno(f), t, p); + r = flink_tmpfile(f, t, p); if (r == -EEXIST) return 0; /* Silently skip creation if the file exists now (recheck) */ if (r < 0) @@ -1118,100 +1322,62 @@ static int install_loader_config(const char *esp_path) { return 1; } -static int install_machine_id_directory(const char *root) { - assert(root); - assert(arg_make_machine_id_directory >= 0); - - if (!arg_make_machine_id_directory) - return 0; - - assert(!sd_id128_is_null(arg_machine_id)); - return mkdir_one(root, SD_ID128_TO_STRING(arg_machine_id)); -} - -static int install_machine_info_config(void) { - _cleanup_free_ char *contents = NULL; - size_t length; - bool need_install_layout = true, need_machine_id; +static int install_loader_specification(const char *root) { + _cleanup_(unlink_and_freep) char *t = NULL; + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *p = NULL; int r; - assert(arg_make_machine_id_directory >= 0); + p = path_join(root, "/loader/entries.srel"); + if (!p) + return log_oom(); - /* We only want to save the machine-id if we created any directories using it. */ - need_machine_id = arg_make_machine_id_directory; + if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */ + return 0; - _cleanup_fclose_ FILE *orig = fopen("/etc/machine-info", "re"); - if (!orig && errno != ENOENT) - return log_error_errno(errno, "Failed to open /etc/machine-info: %m"); - - if (orig) { - _cleanup_free_ char *install_layout = NULL, *machine_id = NULL; - - r = parse_env_file(orig, "/etc/machine-info", - "KERNEL_INSTALL_LAYOUT", &install_layout, - "KERNEL_INSTALL_MACHINE_ID", &machine_id); - if (r < 0) - return log_error_errno(r, "Failed to parse /etc/machine-info: %m"); - - rewind(orig); - - if (!isempty(install_layout)) - need_install_layout = false; - - if (!isempty(machine_id)) - need_machine_id = false; - - if (!need_install_layout && !need_machine_id) { - log_debug("/etc/machine-info already has KERNEL_INSTALL_MACHINE_ID=%s and KERNEL_INSTALL_LAYOUT=%s.", - machine_id, install_layout); - return 0; - } - - r = read_full_stream(orig, &contents, &length); - if (r < 0) - return log_error_errno(r, "Failed to read /etc/machine-info: %m"); - } - - _cleanup_(unlink_and_freep) char *dst_tmp = NULL; - _cleanup_fclose_ FILE *dst = NULL; - r = fopen_temporary_label("/etc/machine-info", /* The path for which to the look up the label */ - "/etc/machine-info", /* Where we want the file actually to end up */ - &dst, /* The temporary file we write to */ - &dst_tmp); + r = fopen_tmpfile_linkable(p, O_WRONLY|O_CLOEXEC, &t, &f); if (r < 0) - return log_debug_errno(r, "Failed to open temporary copy of /etc/machine-info: %m"); + return log_error_errno(r, "Failed to open \"%s\" for writing: %m", p); - if (contents) - fwrite_unlocked(contents, 1, length, dst); + fprintf(f, "type1\n"); - bool no_newline = !contents || contents[length - 1] == '\n'; - - if (need_install_layout) { - const char *line = "\nKERNEL_INSTALL_LAYOUT=bls\n" + no_newline; - fwrite_unlocked(line, 1, strlen(line), dst); - no_newline = false; - } - - const char *mid_string = SD_ID128_TO_STRING(arg_machine_id); - if (need_machine_id) - fprintf(dst, "%sKERNEL_INSTALL_MACHINE_ID=%s\n", - no_newline ? "" : "\n", - mid_string); - - r = fflush_and_check(dst); + r = flink_tmpfile(f, t, p); + if (r == -EEXIST) + return 0; /* Silently skip creation if the file exists now (recheck) */ if (r < 0) - return log_error_errno(r, "Failed to write temporary copy of /etc/machine-info: %m"); - if (fchmod(fileno(dst), 0644) < 0) - return log_debug_errno(errno, "Failed to fchmod %s: %m", dst_tmp); + return log_error_errno(r, "Failed to move \"%s\" into place: %m", p); - if (rename(dst_tmp, "/etc/machine-info") < 0) - return log_error_errno(errno, "Failed to replace /etc/machine-info: %m"); + t = mfree(t); + return 1; +} + +static int install_entry_directory(const char *root) { + assert(root); + assert(arg_make_entry_directory >= 0); + + if (!arg_make_entry_directory) + return 0; + + assert(arg_entry_token); + return mkdir_one(root, arg_entry_token); +} + +static int install_entry_token(void) { + int r; + + assert(arg_make_entry_directory >= 0); + assert(arg_entry_token); + + /* Let's save the used entry token in /etc/kernel/entry-token if we used it to create the entry + * directory, or if anything else but the machine ID */ + + if (!arg_make_entry_directory && arg_entry_token_type == ARG_ENTRY_TOKEN_MACHINE_ID) + return 0; + + r = write_string_file("/etc/kernel/entry-token", arg_entry_token, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC|WRITE_STRING_FILE_MKDIR_0755); + if (r < 0) + return log_error_errno(r, "Failed to write entry token '%s' to /etc/kernel/entry-token", arg_entry_token); - log_info("%s /etc/machine-info with%s%s%s", - orig ? "Updated" : "Created", - need_install_layout ? " KERNEL_INSTALL_LAYOUT=bls" : "", - need_machine_id ? " KERNEL_INSTALL_MACHINE_ID=" : "", - need_machine_id ? mid_string : ""); return 0; } @@ -1255,8 +1421,12 @@ static int help(int argc, char *argv[], void *userdata) { " --no-pager Do not pipe output into a pager\n" " --graceful Don't fail when the ESP cannot be found or EFI\n" " variables cannot be written\n" - " --make-machine-id-directory=yes|no|auto\n" - " Create $BOOT/$MACHINE_ID\n" + " --make-entry-directory=yes|no|auto\n" + " Create $BOOT/ENTRY-TOKEN/ directory\n" + " --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n" + " Entry token to use for this installation\n" + " --json=pretty|short|off\n" + " Generate JSON output\n" "\nSee the %2$s for details.\n", program_invocation_short_name, link, @@ -1276,7 +1446,9 @@ static int parse_argv(int argc, char *argv[]) { ARG_NO_VARIABLES, ARG_NO_PAGER, ARG_GRACEFUL, - ARG_MAKE_MACHINE_ID_DIRECTORY, + ARG_MAKE_ENTRY_DIRECTORY, + ARG_ENTRY_TOKEN, + ARG_JSON, }; static const struct option options[] = { @@ -1291,7 +1463,10 @@ static int parse_argv(int argc, char *argv[]) { { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, { "no-pager", no_argument, NULL, ARG_NO_PAGER }, { "graceful", no_argument, NULL, ARG_GRACEFUL }, - { "make-machine-id-directory", required_argument, NULL, ARG_MAKE_MACHINE_ID_DIRECTORY }, + { "make-entry-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, + { "make-machine-id-directory", required_argument, NULL, ARG_MAKE_ENTRY_DIRECTORY }, /* Compatibility alias */ + { "entry-token", required_argument, NULL, ARG_ENTRY_TOKEN }, + { "json", required_argument, NULL, ARG_JSON }, {} }; @@ -1349,17 +1524,50 @@ static int parse_argv(int argc, char *argv[]) { arg_graceful = true; break; - case ARG_MAKE_MACHINE_ID_DIRECTORY: - if (streq(optarg, "auto")) /* retained for backwards compatibility */ - arg_make_machine_id_directory = -1; /* yes if machine-id is permanent */ - else { - r = parse_boolean_argument("--make-machine-id-directory=", optarg, &b); + case ARG_ENTRY_TOKEN: { + const char *e; + + if (streq(optarg, "machine-id")) { + arg_entry_token_type = ARG_ENTRY_TOKEN_MACHINE_ID; + arg_entry_token = mfree(arg_entry_token); + } else if (streq(optarg, "os-image-id")) { + arg_entry_token_type = ARG_ENTRY_TOKEN_OS_IMAGE_ID; + arg_entry_token = mfree(arg_entry_token); + } else if (streq(optarg, "os-id")) { + arg_entry_token_type = ARG_ENTRY_TOKEN_OS_ID; + arg_entry_token = mfree(arg_entry_token); + } else if ((e = startswith(optarg, "literal:"))) { + arg_entry_token_type = ARG_ENTRY_TOKEN_LITERAL; + + r = free_and_strdup_warn(&arg_entry_token, e); if (r < 0) return r; - arg_make_machine_id_directory = b; + } else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Unexpected parameter for --entry-token=: %s", optarg); + + break; + } + + case ARG_MAKE_ENTRY_DIRECTORY: + if (streq(optarg, "auto")) /* retained for backwards compatibility */ + arg_make_entry_directory = -1; /* yes if machine-id is permanent */ + else { + r = parse_boolean_argument("--make-entry-directory=", optarg, &b); + if (r < 0) + return r; + + arg_make_entry_directory = b; } break; + case ARG_JSON: + r = parse_json_argument(optarg, &arg_json_format_flags); + if (r <= 0) + return r; + + break; + case '?': return -EINVAL; @@ -1388,7 +1596,7 @@ static void print_yes_no_line(bool first, bool good, const char *name) { static int are_we_installed(void) { int r; - r = acquire_esp(/* privileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, NULL); + r = acquire_esp(/* privileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, NULL, NULL); if (r < 0) return r; @@ -1420,9 +1628,10 @@ static int are_we_installed(void) { static int verb_status(int argc, char *argv[], void *userdata) { sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL; + dev_t esp_devid = 0, xbootldr_devid = 0; int r, k; - r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, &esp_uuid); + r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, &esp_uuid, &esp_devid); if (arg_print_esp_path) { if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only * error the find_esp_and_warn() won't log on its own) */ @@ -1433,7 +1642,7 @@ static int verb_status(int argc, char *argv[], void *userdata) { puts(arg_esp_path); } - r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid); + r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid, &xbootldr_devid); if (arg_print_dollar_boot_path) { if (r == -EACCES) return log_error_errno(r, "Failed to determine XBOOTLDR location: %m"); @@ -1567,7 +1776,14 @@ static int verb_status(int argc, char *argv[], void *userdata) { } if (arg_esp_path || arg_xbootldr_path) { - k = status_entries(arg_esp_path, esp_uuid, arg_xbootldr_path, xbootldr_uuid); + /* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would find the same entries twice */ + bool same = arg_esp_path && arg_xbootldr_path && devid_set_and_equal(esp_devid, xbootldr_devid); + + k = status_entries( + arg_esp_path, + esp_uuid, + same ? NULL : arg_xbootldr_path, + same ? SD_ID128_NULL : xbootldr_uuid); if (k < 0) r = k; } @@ -1576,27 +1792,31 @@ static int verb_status(int argc, char *argv[], void *userdata) { } static int verb_list(int argc, char *argv[], void *userdata) { - _cleanup_(boot_config_free) BootConfig config = {}; + _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL; _cleanup_strv_free_ char **efi_entries = NULL; + dev_t esp_devid = 0, xbootldr_devid = 0; int r; /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn * off logging about access errors and turn off potentially privileged device probing. Here we're interested in * the latter but not the former, hence request the mode, and log about EACCES. */ - r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, NULL); + r = acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL, NULL, NULL, NULL, &esp_devid); if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */ return log_error_errno(r, "Failed to determine ESP: %m"); if (r < 0) return r; - r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, NULL); + r = acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, NULL, &xbootldr_devid); if (r == -EACCES) return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m"); if (r < 0) return r; - r = boot_entries_load_config(arg_esp_path, arg_xbootldr_path, &config); + /* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would find the same entries twice */ + bool same = arg_esp_path && arg_xbootldr_path && devid_set_and_equal(esp_devid, xbootldr_devid); + + r = boot_config_load(&config, arg_esp_path, same ? NULL : arg_xbootldr_path); if (r < 0) return r; @@ -1606,9 +1826,50 @@ static int verb_list(int argc, char *argv[], void *userdata) { else if (r < 0) log_warning_errno(r, "Failed to determine entries reported by boot loader, ignoring: %m"); else - (void) boot_entries_augment_from_loader(&config, efi_entries, false); + (void) boot_config_augment_from_loader(&config, efi_entries, /* only_auto= */ false); - if (config.n_entries == 0) + r = boot_config_select_special_entries(&config); + if (r < 0) + return r; + + if (!FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) { + + pager_open(arg_pager_flags); + + for (size_t i = 0; i < config.n_entries; i++) { + _cleanup_free_ char *opts = NULL; + BootEntry *e = config.entries + i; + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + + if (!strv_isempty(e->options)) { + opts = strv_join(e->options, " "); + if (!opts) + return log_oom(); + } + + r = json_build(&v, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_CONDITION(e->id, "id", JSON_BUILD_STRING(e->id)), + JSON_BUILD_PAIR_CONDITION(e->path, "path", JSON_BUILD_STRING(e->path)), + JSON_BUILD_PAIR_CONDITION(e->root, "root", JSON_BUILD_STRING(e->root)), + JSON_BUILD_PAIR_CONDITION(e->title, "title", JSON_BUILD_STRING(e->title)), + JSON_BUILD_PAIR_CONDITION(boot_entry_title(e), "showTitle", JSON_BUILD_STRING(boot_entry_title(e))), + JSON_BUILD_PAIR_CONDITION(e->sort_key, "sortKey", JSON_BUILD_STRING(e->sort_key)), + JSON_BUILD_PAIR_CONDITION(e->version, "version", JSON_BUILD_STRING(e->version)), + JSON_BUILD_PAIR_CONDITION(e->machine_id, "machineId", JSON_BUILD_STRING(e->machine_id)), + JSON_BUILD_PAIR_CONDITION(e->architecture, "architecture", JSON_BUILD_STRING(e->architecture)), + JSON_BUILD_PAIR_CONDITION(opts, "options", JSON_BUILD_STRING(opts)), + JSON_BUILD_PAIR_CONDITION(e->kernel, "linux", JSON_BUILD_STRING(e->kernel)), + JSON_BUILD_PAIR_CONDITION(e->efi, "efi", JSON_BUILD_STRING(e->efi)), + JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->initrd), "initrd", JSON_BUILD_STRV(e->initrd)), + JSON_BUILD_PAIR_CONDITION(e->device_tree, "devicetree", JSON_BUILD_STRING(e->device_tree)), + JSON_BUILD_PAIR_CONDITION(!strv_isempty(e->device_tree_overlay), "devicetreeOverlay", JSON_BUILD_STRV(e->device_tree_overlay)))); + if (r < 0) + return log_oom(); + + json_variant_dump(v, arg_json_format_flags, stdout, NULL); + } + + } else if (config.n_entries == 0) log_info("No boot loader entries found."); else { pager_open(arg_pager_flags); @@ -1616,7 +1877,11 @@ static int verb_list(int argc, char *argv[], void *userdata) { printf("Boot Loader Entries:\n"); for (size_t n = 0; n < config.n_entries; n++) { - r = boot_entry_show(config.entries + n, n == (size_t) config.default_entry); + r = boot_entry_show( + config.entries + n, + /* show_as_default= */ n == (size_t) config.default_entry, + /* show_as_selected= */ n == (size_t) config.selected_entry, + /* show_discovered= */ true); if (r < 0) return r; @@ -1785,10 +2050,12 @@ static int verb_install(int argc, char *argv[], void *userdata) { bool install, graceful; int r; + /* Invoked for both "update" and "install" */ + install = streq(argv[0], "install"); graceful = !install && arg_graceful; /* support graceful mode for updates */ - r = acquire_esp(/* unprivileged_mode= */ false, graceful, &part, &pstart, &psize, &uuid); + r = acquire_esp(/* unprivileged_mode= */ false, graceful, &part, &pstart, &psize, &uuid, NULL); if (graceful && r == -ENOKEY) return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */ if (r < 0) @@ -1805,11 +2072,11 @@ static int verb_install(int argc, char *argv[], void *userdata) { } } - r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL); + r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL, NULL); if (r < 0) return r; - r = settle_install_machine_id(); + r = settle_make_entry_directory(); if (r < 0) return r; @@ -1836,11 +2103,11 @@ static int verb_install(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = install_machine_id_directory(arg_dollar_boot_path()); + r = install_entry_directory(arg_dollar_boot_path()); if (r < 0) return r; - r = install_machine_info_config(); + r = install_entry_token(); if (r < 0) return r; @@ -1848,6 +2115,10 @@ static int verb_install(int argc, char *argv[], void *userdata) { if (r < 0) return r; } + + r = install_loader_specification(arg_dollar_boot_path()); + if (r < 0) + return r; } (void) sync_everything(); @@ -1865,15 +2136,15 @@ static int verb_remove(int argc, char *argv[], void *userdata) { sd_id128_t uuid = SD_ID128_NULL; int r, q; - r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, &uuid); + r = acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL, NULL, NULL, &uuid, NULL); if (r < 0) return r; - r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL); + r = acquire_xbootldr(/* unprivileged_mode= */ false, NULL, NULL); if (r < 0) return r; - r = settle_install_machine_id(); + r = settle_make_entry_directory(); if (r < 0) return r; @@ -1887,6 +2158,10 @@ static int verb_remove(int argc, char *argv[], void *userdata) { if (q < 0 && r >= 0) r = q; + q = remove_file(arg_esp_path, "/loader/entries.srel"); + if (q < 0 && r >= 0) + r = q; + q = remove_subdirs(arg_esp_path, esp_subdirs); if (q < 0 && r >= 0) r = q; @@ -1895,17 +2170,22 @@ static int verb_remove(int argc, char *argv[], void *userdata) { if (q < 0 && r >= 0) r = q; - q = remove_machine_id_directory(arg_esp_path); + q = remove_entry_directory(arg_esp_path); if (q < 0 && r >= 0) - r = 1; + r = q; if (arg_xbootldr_path) { - /* Remove the latter two also in the XBOOTLDR partition if it exists */ + /* Remove a subset of these also from the XBOOTLDR partition if it exists */ + + q = remove_file(arg_xbootldr_path, "/loader/entries.srel"); + if (q < 0 && r >= 0) + r = q; + q = remove_subdirs(arg_xbootldr_path, dollar_boot_subdirs); if (q < 0 && r >= 0) r = q; - q = remove_machine_id_directory(arg_xbootldr_path); + q = remove_entry_directory(arg_xbootldr_path); if (q < 0 && r >= 0) r = q; } @@ -2078,7 +2358,7 @@ static int verb_set_efivar(int argc, char *argv[], void *userdata) { static int verb_random_seed(int argc, char *argv[], void *userdata) { int r; - r = find_esp_and_warn(arg_esp_path, false, &arg_esp_path, NULL, NULL, NULL, NULL); + r = find_esp_and_warn(arg_esp_path, false, &arg_esp_path, NULL, NULL, NULL, NULL, NULL); if (r == -ENOKEY) { /* find_esp_and_warn() doesn't warn about ENOKEY, so let's do that on our own */ if (!arg_graceful) diff --git a/src/boot/efi/assert.c b/src/boot/efi/assert.c index 7a25526de..bb16d2bf9 100644 --- a/src/boot/efi/assert.c +++ b/src/boot/efi/assert.c @@ -1,9 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#ifndef NDEBUG - #include #include + #include "util.h" void efi_assert(const char *expr, const char *file, unsigned line, const char *function) { @@ -11,5 +10,3 @@ void efi_assert(const char *expr, const char *file, unsigned line, const char *f for (;;) BS->Stall(60 * 1000 * 1000); } - -#endif diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index e3dc336f3..e7e5a6a36 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -18,6 +18,7 @@ #include "random-seed.h" #include "secure-boot.h" #include "shim.h" +#include "ticks.h" #include "util.h" #include "xbootldr.h" @@ -42,14 +43,17 @@ _used_ _section_(".osrel") static const char osrel[] = enum loader_type { LOADER_UNDEFINED, + LOADER_AUTO, LOADER_EFI, - LOADER_LINUX, + LOADER_LINUX, /* Boot loader spec type #1 entries */ + LOADER_UNIFIED_LINUX, /* Boot loader spec type #2 entries */ }; typedef struct { CHAR16 *id; /* The unique identifier for this entry (typically the filename of the file defining the entry) */ CHAR16 *title_show; /* The string to actually display (this is made unique before showing) */ CHAR16 *title; /* The raw (human readable) title string of the entry (not necessarily unique) */ + CHAR16 *sort_key; /* The string to use as primary sort key, usually ID= from os-release, possibly suffixed */ CHAR16 *version; /* The raw (human readable) version string of the entry */ CHAR16 *machine_id; EFI_HANDLE *device; @@ -59,8 +63,6 @@ typedef struct { CHAR16 *options; CHAR16 key; EFI_STATUS (*call)(void); - BOOLEAN no_autoselect; - BOOLEAN non_unique; UINTN tries_done; UINTN tries_left; CHAR16 *path; @@ -71,8 +73,8 @@ typedef struct { typedef struct { ConfigEntry **entries; UINTN entry_count; - INTN idx_default; - INTN idx_default_efivar; + UINTN idx_default; + UINTN idx_default_efivar; UINT32 timeout_sec; /* Actual timeout used (efi_main() override > efivar > config). */ UINT32 timeout_sec_config; UINT32 timeout_sec_efivar; @@ -84,9 +86,11 @@ typedef struct { BOOLEAN editor; BOOLEAN auto_entries; BOOLEAN auto_firmware; + BOOLEAN reboot_for_bitlocker; BOOLEAN force_menu; BOOLEAN use_saved_entry; BOOLEAN use_saved_entry_efivar; + BOOLEAN beep; INT64 console_mode; INT64 console_mode_efivar; RandomSeedMode random_seed_mode; @@ -104,6 +108,11 @@ enum { TIMEOUT_TYPE_MAX = UINT32_MAX, }; +enum { + IDX_MAX = INT16_MAX, + IDX_INVALID, +}; + static void cursor_left(UINTN *cursor, UINTN *first) { assert(cursor); assert(first); @@ -136,27 +145,20 @@ static BOOLEAN line_edit( UINTN y_pos) { _cleanup_freepool_ CHAR16 *line = NULL, *print = NULL; - UINTN size, len, first, cursor, clear; - BOOLEAN exit, enter; + UINTN size, len, first = 0, cursor = 0, clear = 0; assert(line_out); if (!line_in) line_in = L""; - size = StrLen(line_in) + 1024; + len = StrLen(line_in); + size = len + 1024; line = xnew(CHAR16, size); - - StrCpy(line, line_in); - len = StrLen(line); print = xnew(CHAR16, x_max + 1); + StrCpy(line, line_in); - first = 0; - cursor = 0; - clear = 0; - enter = FALSE; - exit = FALSE; - while (!exit) { + for (;;) { EFI_STATUS err; UINT64 key; UINTN j; @@ -193,8 +195,7 @@ static BOOLEAN line_edit( case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'g'): case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('c')): case KEYPRESS(EFI_CONTROL_PRESSED, 0, CHAR_CTRL('g')): - exit = TRUE; - break; + return FALSE; case KEYPRESS(0, SCAN_HOME, 0): case KEYPRESS(EFI_CONTROL_PRESSED, 0, 'a'): @@ -321,9 +322,7 @@ static BOOLEAN line_edit( case KEYPRESS(0, CHAR_CARRIAGE_RETURN, CHAR_CARRIAGE_RETURN): /* Teclast X98+ II firmware sends malformed events */ if (StrCmp(line, line_in) != 0) *line_out = TAKE_PTR(line); - enter = TRUE; - exit = TRUE; - break; + return TRUE; case KEYPRESS(0, 0, CHAR_BACKSPACE): if (len == 0) @@ -370,15 +369,13 @@ static BOOLEAN line_edit( continue; } } - - return enter; } static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) { assert(config); if (key == 0) - return -1; + return IDX_INVALID; /* select entry by number key */ if (key >= '1' && key <= '9') { @@ -397,7 +394,7 @@ static UINTN entry_lookup_key(Config *config, UINTN start, CHAR16 key) { if (config->entries[i]->key == key) return i; - return -1; + return IDX_INVALID; } static CHAR16 *update_timeout_efivar(UINT32 *t, BOOLEAN inc) { @@ -432,6 +429,17 @@ static CHAR16 *update_timeout_efivar(UINT32 *t, BOOLEAN inc) { } } +static BOOLEAN unicode_supported(void) { + static INTN cache = -1; + + if (cache < 0) + /* Basic unicode box drawing support is mandated by the spec, but it does + * not hurt to make sure it works. */ + cache = !EFI_ERROR(ST->ConOut->TestString(ST->ConOut, (CHAR16 *) L"─")); + + return cache; +} + static void ps_string(const CHAR16 *fmt, const void *value) { assert(fmt); if (value) @@ -447,13 +455,18 @@ static BOOLEAN ps_continue(void) { UINT64 key; EFI_STATUS err; - Print(L"\n--- Press any key to continue, ESC or q to quit. ---\n\n"); + if (unicode_supported()) + Print(L"\n─── Press any key to continue, ESC or q to quit. ───\n\n"); + else + Print(L"\n--- Press any key to continue, ESC or q to quit. ---\n\n"); + err = console_key_read(&key, UINT64_MAX); return !EFI_ERROR(err) && !IN_SET(key, KEYPRESS(0, SCAN_ESC, 0), KEYPRESS(0, 0, 'q'), KEYPRESS(0, 0, 'Q')); } static void print_status(Config *config, CHAR16 *loaded_image_path) { UINTN x_max, y_max; + UINT32 screen_width = 0, screen_height = 0; SecureBootMode secure; _cleanup_freepool_ CHAR16 *device_part_uuid = NULL; @@ -462,6 +475,7 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) { clear_screen(COLOR_NORMAL); console_query_mode(&x_max, &y_max); + query_screen_resolution(&screen_width, &screen_height); secure = secure_boot_mode(); (void) efivar_get(LOADER_GUID, L"LoaderDevicePartUUID", &device_part_uuid); @@ -478,7 +492,8 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) { Print(L" OS indications: %lu\n", get_os_indications_supported()); Print(L" secure boot: %s (%s)\n", yes_no(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)), secure_boot_mode_to_string(secure)); ps_bool(L" shim: %s\n", shim_loaded()); - Print(L" console mode: %d/%d (%lu x %lu)\n", ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - 1LL, x_max, y_max); + ps_bool(L" TPM: %s\n", tpm_present()); + Print(L" console mode: %d/%d (%lux%lu @%ux%u)\n", ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - 1LL, x_max, y_max, screen_width, screen_height); if (!ps_continue()) return; @@ -512,6 +527,8 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) { ps_bool(L" editor: %s\n", config->editor); ps_bool(L" auto-entries: %s\n", config->auto_entries); ps_bool(L" auto-firmware: %s\n", config->auto_firmware); + ps_bool(L" beep: %s\n", config->beep); + ps_bool(L" reboot-for-bitlocker: %s\n", config->reboot_for_bitlocker); ps_string(L" random-seed-mode: %s\n", random_seed_modes_table[config->random_seed_mode]); switch (config->console_mode) { @@ -539,6 +556,7 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) { ps_string(L" id: %s\n", entry->id); ps_string(L" title: %s\n", entry->title); ps_string(L" title show: %s\n", streq_ptr(entry->title, entry->title_show) ? NULL : entry->title_show); + ps_string(L" sort key: %s\n", entry->sort_key); ps_string(L" version: %s\n", entry->version); ps_string(L" machine-id: %s\n", entry->machine_id); if (entry->device) @@ -546,7 +564,6 @@ static void print_status(Config *config, CHAR16 *loaded_image_path) { ps_string(L" loader: %s\n", entry->loader); ps_string(L" devicetree: %s\n", entry->devicetree); ps_string(L" options: %s\n", entry->options); - ps_bool(L" auto-select: %s\n", !entry->no_autoselect); ps_bool(L" internal call: %s\n", !!entry->call); ps_bool(L"counting boots: %s\n", entry->tries_left != UINTN_MAX); @@ -592,19 +609,18 @@ static BOOLEAN menu_run( UINTN visible_max = 0; UINTN idx_highlight = config->idx_default; UINTN idx_highlight_prev = 0; - UINTN idx_first = 0, idx_last = 0; + UINTN idx, idx_first = 0, idx_last = 0; BOOLEAN new_mode = TRUE, clear = TRUE; BOOLEAN refresh = TRUE, highlight = FALSE; UINTN x_start = 0, y_start = 0, y_status = 0; UINTN x_max, y_max; _cleanup_(strv_freep) CHAR16 **lines = NULL; - _cleanup_freepool_ CHAR16 *clearline = NULL, *status = NULL; + _cleanup_freepool_ CHAR16 *clearline = NULL, *separator = NULL, *status = NULL; UINT32 timeout_efivar_saved = config->timeout_sec_efivar; UINT32 timeout_remain = config->timeout_sec == TIMEOUT_MENU_FORCE ? 0 : config->timeout_sec; - INT16 idx; BOOLEAN exit = FALSE, run = TRUE, firmware_setup = FALSE; INT64 console_mode_initial = ST->ConOut->Mode->Mode, console_mode_efivar_saved = config->console_mode_efivar; - INTN default_efivar_saved = config->idx_default_efivar; + UINTN default_efivar_saved = config->idx_default_efivar; graphics_mode(FALSE); ST->ConIn->Reset(ST->ConIn, FALSE); @@ -620,12 +636,11 @@ static BOOLEAN menu_run( log_error_stall(L"Error switching console mode: %r", err); } + UINTN line_width = 0, entry_padding = 3; while (!exit) { UINT64 key; if (new_mode) { - UINTN line_width = 0, entry_padding = 3; - console_query_mode(&x_max, &y_max); /* account for padding+status */ @@ -644,6 +659,7 @@ static BOOLEAN menu_run( idx_last = idx_first + visible_max - 1; /* length of the longest entry */ + line_width = 0; for (UINTN i = 0; i < config->entry_count; i++) line_width = MAX(line_width, StrLen(config->entries[i]->title_show)); line_width = MIN(line_width + 2 * entry_padding, x_max); @@ -656,10 +672,11 @@ static BOOLEAN menu_run( y_start = 0; /* Put status line after the entry list, but give it some breathing room. */ - y_status = MIN(y_start + MIN(visible_max, config->entry_count) + 4, y_max - 1); + y_status = MIN(y_start + MIN(visible_max, config->entry_count) + 1, y_max - 1); lines = strv_free(lines); clearline = mfree(clearline); + separator = mfree(separator); /* menu entries title lines */ lines = xnew(CHAR16*, config->entry_count + 1); @@ -683,9 +700,13 @@ static BOOLEAN menu_run( lines[config->entry_count] = NULL; clearline = xnew(CHAR16, x_max + 1); - for (UINTN i = 0; i < x_max; i++) + separator = xnew(CHAR16, x_max + 1); + for (UINTN i = 0; i < x_max; i++) { clearline[i] = ' '; + separator[i] = unicode_supported() ? L'─' : L'-'; + } clearline[x_max] = 0; + separator[x_max] = 0; new_mode = FALSE; clear = TRUE; @@ -702,19 +723,19 @@ static BOOLEAN menu_run( print_at(x_start, y_start + i - idx_first, (i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY, lines[i]); - if ((INTN)i == config->idx_default_efivar) + if (i == config->idx_default_efivar) print_at(x_start, y_start + i - idx_first, (i == idx_highlight) ? COLOR_HIGHLIGHT : COLOR_ENTRY, - (CHAR16*) L"=>"); + (CHAR16*) (unicode_supported() ? L" ►" : L"=>")); } refresh = FALSE; } else if (highlight) { print_at(x_start, y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, lines[idx_highlight_prev]); print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, lines[idx_highlight]); - if ((INTN)idx_highlight_prev == config->idx_default_efivar) - print_at(x_start , y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, (CHAR16*) L"=>"); - if ((INTN)idx_highlight == config->idx_default_efivar) - print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, (CHAR16*) L"=>"); + if (idx_highlight_prev == config->idx_default_efivar) + print_at(x_start , y_start + idx_highlight_prev - idx_first, COLOR_ENTRY, (CHAR16*) (unicode_supported() ? L" ►" : L"=>")); + if (idx_highlight == config->idx_default_efivar) + print_at(x_start, y_start + idx_highlight - idx_first, COLOR_HIGHLIGHT, (CHAR16*) (unicode_supported() ? L" ►" : L"=>")); highlight = FALSE; } @@ -723,22 +744,30 @@ static BOOLEAN menu_run( status = xpool_print(L"Boot in %u s.", timeout_remain); } - /* print status at last line of screen */ if (status) { - UINTN len; - UINTN x; - - /* center line */ - len = StrLen(status); - if (len < x_max) - x = (x_max - len) / 2; - else - x = 0; - print_at(0, y_status, COLOR_NORMAL, clearline + (x_max - x)); + /* If we draw the last char of the last line, the screen will scroll and break our + * input. Therefore, draw one less character then we could for the status message. + * Note that the same does not apply for the separator line as it will never be drawn + * on the last line. */ + UINTN len = StrnLen(status, x_max - 1); + UINTN x = (x_max - len) / 2; + status[len] = '\0'; + print_at(0, y_status, COLOR_NORMAL, clearline + x_max - x); ST->ConOut->OutputString(ST->ConOut, status); ST->ConOut->OutputString(ST->ConOut, clearline + 1 + x + len); + + len = MIN(MAX(len, line_width) + 2 * entry_padding, x_max); + x = (x_max - len) / 2; + print_at(x, y_status - 1, COLOR_NORMAL, separator + x_max - len); + } else { + print_at(0, y_status - 1, COLOR_NORMAL, clearline); + print_at(0, y_status, COLOR_NORMAL, clearline + 1); /* See comment above. */ } + /* Beep several times so that the selected entry can be distinguished. */ + if (config->beep) + beep(idx_highlight + 1); + err = console_key_read(&key, timeout_remain > 0 ? 1000 * 1000 : UINT64_MAX); if (err == EFI_NOT_READY) /* No input device returned a key, try again. This @@ -763,11 +792,7 @@ static BOOLEAN menu_run( timeout_remain = 0; /* clear status after keystroke */ - if (status) { - FreePool(status); - status = NULL; - print_at(0, y_status, COLOR_NORMAL, clearline + 1); - } + status = mfree(status); idx_highlight_prev = idx_highlight; @@ -845,14 +870,14 @@ static BOOLEAN menu_run( case KEYPRESS(0, 0, 'd'): case KEYPRESS(0, 0, 'D'): - if (config->idx_default_efivar != (INTN)idx_highlight) { + if (config->idx_default_efivar != idx_highlight) { FreePool(config->entry_default_efivar); config->entry_default_efivar = xstrdup(config->entries[idx_highlight]->id); config->idx_default_efivar = idx_highlight; status = xstrdup(L"Default boot entry selected."); } else { config->entry_default_efivar = mfree(config->entry_default_efivar); - config->idx_default_efivar = -1; + config->idx_default_efivar = IDX_INVALID; status = xstrdup(L"Default boot entry cleared."); } config->use_saved_entry_efivar = FALSE; @@ -872,8 +897,18 @@ static BOOLEAN menu_run( case KEYPRESS(0, 0, 'e'): case KEYPRESS(0, 0, 'E'): /* only the options of configured entries can be edited */ - if (!config->editor || config->entries[idx_highlight]->type == LOADER_UNDEFINED) + if (!config->editor || !IN_SET(config->entries[idx_highlight]->type, + LOADER_EFI, LOADER_LINUX, LOADER_UNIFIED_LINUX)) break; + + /* Unified kernels that are signed as a whole will not accept command line options + * when secure boot is enabled unless there is none embedded in the image. Do not try + * to pretend we can edit it to only have it be ignored. */ + if (config->entries[idx_highlight]->type == LOADER_UNIFIED_LINUX && + secure_boot_enabled() && + config->entries[idx_highlight]->options) + break; + /* The edit line may end up on the last line of the screen. And even though we're * not telling the firmware to advance the line, it still does in this one case, * causing a scroll to happen that screws with our beautiful boot loader output. @@ -942,7 +977,7 @@ static BOOLEAN menu_run( default: /* jump with a hotkey directly to a matching entry */ idx = entry_lookup_key(config, idx_highlight+1, KEYCHAR(key)); - if (idx < 0) + if (idx == IDX_INVALID) break; idx_highlight = idx; refresh = TRUE; @@ -993,6 +1028,9 @@ static void config_add_entry(Config *config, ConfigEntry *entry) { assert(config); assert(entry); + /* This is just for paranoia. */ + assert(config->entry_count < IDX_MAX); + if ((config->entry_count & 15) == 0) { UINTN i = config->entry_count + 16; config->entries = xreallocate_pool( @@ -1010,6 +1048,7 @@ static void config_entry_free(ConfigEntry *entry) { FreePool(entry->id); FreePool(entry->title_show); FreePool(entry->title); + FreePool(entry->sort_key); FreePool(entry->version); FreePool(entry->machine_id); FreePool(entry->loader); @@ -1041,61 +1080,62 @@ static CHAR8 *line_get_key_value( assert(key_ret); assert(value_ret); -skip: - line = content + *pos; - if (*line == '\0') - return NULL; + for (;;) { + line = content + *pos; + if (*line == '\0') + return NULL; - linelen = 0; - while (line[linelen] && !strchra((CHAR8 *)"\n\r", line[linelen])) - linelen++; + linelen = 0; + while (line[linelen] && !strchra((CHAR8 *) "\n\r", line[linelen])) + linelen++; - /* move pos to next line */ - *pos += linelen; - if (content[*pos]) - (*pos)++; + /* move pos to next line */ + *pos += linelen; + if (content[*pos]) + (*pos)++; - /* empty line */ - if (linelen == 0) - goto skip; + /* empty line */ + if (linelen == 0) + continue; - /* terminate line */ - line[linelen] = '\0'; + /* terminate line */ + line[linelen] = '\0'; - /* remove leading whitespace */ - while (strchra((CHAR8 *)" \t", *line)) { - line++; - linelen--; + /* remove leading whitespace */ + while (strchra((CHAR8 *) " \t", *line)) { + line++; + linelen--; + } + + /* remove trailing whitespace */ + while (linelen > 0 && strchra((CHAR8 *) " \t", line[linelen - 1])) + linelen--; + line[linelen] = '\0'; + + if (*line == '#') + continue; + + /* split key/value */ + value = line; + while (*value && !strchra(sep, *value)) + value++; + if (*value == '\0') + continue; + *value = '\0'; + value++; + while (*value && strchra(sep, *value)) + value++; + + /* unquote */ + if (value[0] == '"' && line[linelen - 1] == '"') { + value++; + line[linelen - 1] = '\0'; + } + + *key_ret = line; + *value_ret = value; + return line; } - - /* remove trailing whitespace */ - while (linelen > 0 && strchra((CHAR8 *)" \t", line[linelen-1])) - linelen--; - line[linelen] = '\0'; - - if (*line == '#') - goto skip; - - /* split key/value */ - value = line; - while (*value && !strchra(sep, *value)) - value++; - if (*value == '\0') - goto skip; - *value = '\0'; - value++; - while (*value && strchra(sep, *value)) - value++; - - /* unquote */ - if (value[0] == '"' && line[linelen-1] == '"') { - value++; - line[linelen-1] = '\0'; - } - - *key_ret = line; - *value_ret = value; - return line; } static void config_defaults_load_from_file(Config *config, CHAR8 *content) { @@ -1154,6 +1194,19 @@ static void config_defaults_load_from_file(Config *config, CHAR8 *content) { continue; } + if (strcmpa((CHAR8 *)"beep", key) == 0) { + err = parse_boolean(value, &config->beep); + if (EFI_ERROR(err)) + log_error_stall(L"Error parsing 'beep' config option: %a", value); + } + + if (strcmpa((CHAR8 *)"reboot-for-bitlocker", key) == 0) { + err = parse_boolean(value, &config->reboot_for_bitlocker); + if (EFI_ERROR(err)) + log_error_stall(L"Error parsing 'reboot-for-bitlocker' config option: %a", value); + continue; + } + if (strcmpa((CHAR8 *)"console-mode", key) == 0) { if (strcmpa((CHAR8 *)"auto", value) == 0) config->console_mode = CONSOLE_MODE_AUTO; @@ -1312,12 +1365,9 @@ good: entry->next_name = xpool_print(L"%s+%u-%u%s", prefix, next_left, next_done, suffix ?: L""); } -static void config_entry_bump_counters( - ConfigEntry *entry, - EFI_FILE_HANDLE root_dir) { - +static void config_entry_bump_counters(ConfigEntry *entry, EFI_FILE *root_dir) { _cleanup_freepool_ CHAR16* old_path = NULL, *new_path = NULL; - _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL; + _cleanup_(file_closep) EFI_FILE *handle = NULL; _cleanup_freepool_ EFI_FILE_INFO *file_info = NULL; UINTN file_info_size; EFI_STATUS err; @@ -1378,7 +1428,6 @@ static void config_entry_add_from_file( UINTN pos = 0; CHAR8 *key, *value; EFI_STATUS err; - EFI_FILE_HANDLE handle; _cleanup_freepool_ CHAR16 *initrd = NULL; assert(config); @@ -1401,6 +1450,12 @@ static void config_entry_add_from_file( continue; } + if (strcmpa((CHAR8 *)"sort-key", key) == 0) { + FreePool(entry->sort_key); + entry->sort_key = xstra_to_str(value); + continue; + } + if (strcmpa((CHAR8 *)"version", key) == 0) { FreePool(entry->version); entry->version = xstra_to_str(value); @@ -1486,10 +1541,10 @@ static void config_entry_add_from_file( return; /* check existence */ + _cleanup_(file_closep) EFI_FILE *handle = NULL; err = root_dir->Open(root_dir, &handle, entry->loader, EFI_FILE_MODE_READ, 0ULL); if (EFI_ERROR(err)) return; - handle->Close(handle); /* add initrd= to options */ if (entry->type == LOADER_LINUX && initrd) { @@ -1524,8 +1579,9 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) { .editor = TRUE, .auto_entries = TRUE, .auto_firmware = TRUE, + .reboot_for_bitlocker = FALSE, .random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN, - .idx_default_efivar = -1, + .idx_default_efivar = IDX_INVALID, .console_mode = CONSOLE_MODE_KEEP, .console_mode_efivar = CONSOLE_MODE_KEEP, .timeout_sec_config = TIMEOUT_UNSET, @@ -1574,7 +1630,7 @@ static void config_load_entries( EFI_FILE *root_dir, const CHAR16 *loaded_image_path) { - _cleanup_(FileHandleClosep) EFI_FILE_HANDLE entries_dir = NULL; + _cleanup_(file_closep) EFI_FILE *entries_dir = NULL; _cleanup_freepool_ EFI_FILE_INFO *f = NULL; UINTN f_size = 0; EFI_STATUS err; @@ -1583,6 +1639,8 @@ static void config_load_entries( assert(device); assert(root_dir); + /* Adds Boot Loader Type #1 entries (i.e. /loader/entries/….conf) */ + err = open_directory(root_dir, L"\\loader\\entries", &entries_dir); if (EFI_ERROR(err)) return; @@ -1616,67 +1674,80 @@ static INTN config_entry_compare(const ConfigEntry *a, const ConfigEntry *b) { assert(a); assert(b); - /* Order entries that have no tries left to the beginning of the list */ - if (a->tries_left != 0 && b->tries_left == 0) - return 1; - if (a->tries_left == 0 && b->tries_left != 0) - return -1; - - r = strverscmp_improved(a->id, b->id); + /* Order entries that have no tries left to the end of the list */ + r = CMP(a->tries_left == 0, b->tries_left == 0); if (r != 0) return r; - if (a->tries_left == UINTN_MAX || - b->tries_left == UINTN_MAX) + /* If there's a sort key defined for *both* entries, then we do new-style ordering, i.e. by + * sort-key/machine-id/version, with a final fallback to id. If there's no sort key for either, we do + * old-style ordering, i.e. by id only. If one has sort key and the other does not, we put new-style + * before old-style. */ + r = CMP(!a->sort_key, !b->sort_key); + if (r != 0) /* one is old-style, one new-style */ + return r; + + if (a->sort_key && b->sort_key) { + r = strcmp(a->sort_key, b->sort_key); + if (r != 0) + return r; + + /* If multiple installations of the same OS are around, group by machine ID */ + r = strcmp_ptr(a->machine_id, b->machine_id); + if (r != 0) + return r; + + /* If the sort key was defined, then order by version now (downwards, putting the newest first) */ + r = -strverscmp_improved(a->version, b->version); + if (r != 0) + return r; + } + + /* Now order by ID. The version is likely part of the ID, thus note that this will generatelly put + * the newer versions earlier. Specifying a sort key explicitly is preferable, because it gives an + * explicit sort order. */ + r = -strverscmp_improved(a->id, b->id); + if (r != 0) + return r; + + if (a->tries_left == UINTN_MAX || b->tries_left == UINTN_MAX) return 0; - /* If both items have boot counting, and otherwise are identical, put the entry with more tries left last */ - if (a->tries_left > b->tries_left) - return 1; - if (a->tries_left < b->tries_left) - return -1; + /* If both items have boot counting, and otherwise are identical, put the entry with more tries left first */ + r = -CMP(a->tries_left, b->tries_left); + if (r != 0) + return r; /* If they have the same number of tries left, then let the one win which was tried fewer times so far */ - if (a->tries_done < b->tries_done) - return 1; - if (a->tries_done > b->tries_done) - return -1; - - return 0; + return CMP(a->tries_done, b->tries_done); } -static void config_sort_entries(Config *config) { - assert(config); - - sort_pointer_array((void**) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare); -} - -static INTN config_entry_find(Config *config, const CHAR16 *needle) { +static UINTN config_entry_find(Config *config, const CHAR16 *needle) { assert(config); if (!needle) - return -1; + return IDX_INVALID; - for (INTN i = config->entry_count - 1; i >= 0; i--) + for (UINTN i = 0; i < config->entry_count; i++) if (MetaiMatch(config->entries[i]->id, (CHAR16*) needle)) return i; - return -1; + return IDX_INVALID; } static void config_default_entry_select(Config *config) { - INTN i; + UINTN i; assert(config); i = config_entry_find(config, config->entry_oneshot); - if (i >= 0) { + if (i != IDX_INVALID) { config->idx_default = i; return; } i = config_entry_find(config, config->use_saved_entry_efivar ? config->entry_saved : config->entry_default_efivar); - if (i >= 0) { + if (i != IDX_INVALID) { config->idx_default = i; config->idx_default_efivar = i; return; @@ -1684,109 +1755,106 @@ static void config_default_entry_select(Config *config) { if (config->use_saved_entry) /* No need to do the same thing twice. */ - i = config->use_saved_entry_efivar ? -1 : config_entry_find(config, config->entry_saved); + i = config->use_saved_entry_efivar ? IDX_INVALID : config_entry_find(config, config->entry_saved); else i = config_entry_find(config, config->entry_default_config); - if (i >= 0) { + if (i != IDX_INVALID) { config->idx_default = i; return; } - /* select the last suitable entry */ - i = config->entry_count; - while (i--) { - if (config->entries[i]->no_autoselect) + /* select the first suitable entry */ + for (i = 0; i < config->entry_count; i++) { + if (config->entries[i]->type == LOADER_AUTO || config->entries[i]->call) continue; config->idx_default = i; return; } - /* no entry found */ - config->idx_default = -1; + /* If no configured entry to select from was found, enable the menu. */ + config->idx_default = 0; + if (config->timeout_sec == 0) + config->timeout_sec = 10; } -static BOOLEAN find_nonunique(ConfigEntry **entries, UINTN entry_count) { - BOOLEAN non_unique = FALSE; +static BOOLEAN entries_unique(ConfigEntry **entries, BOOLEAN *unique, UINTN entry_count) { + BOOLEAN is_unique = TRUE; assert(entries); + assert(unique); for (UINTN i = 0; i < entry_count; i++) - entries[i]->non_unique = FALSE; - - for (UINTN i = 0; i < entry_count; i++) - for (UINTN k = 0; k < entry_count; k++) { - if (i == k) - continue; + for (UINTN k = i + 1; k < entry_count; k++) { if (StrCmp(entries[i]->title_show, entries[k]->title_show) != 0) continue; - non_unique = entries[i]->non_unique = entries[k]->non_unique = TRUE; + is_unique = unique[i] = unique[k] = FALSE; } - return non_unique; + return is_unique; } /* generate a unique title, avoiding non-distinguishable menu entries */ static void config_title_generate(Config *config) { assert(config); + BOOLEAN unique[config->entry_count]; + /* set title */ for (UINTN i = 0; i < config->entry_count; i++) { - FreePool(config->entries[i]->title_show); - config->entries[i]->title_show = xstrdup( - config->entries[i]->title ?: config->entries[i]->id); + assert(!config->entries[i]->title_show); + unique[i] = TRUE; + config->entries[i]->title_show = xstrdup(config->entries[i]->title ?: config->entries[i]->id); } - if (!find_nonunique(config->entries, config->entry_count)) + if (entries_unique(config->entries, unique, config->entry_count)) return; /* add version to non-unique titles */ for (UINTN i = 0; i < config->entry_count; i++) { - CHAR16 *s; - - if (!config->entries[i]->non_unique) + if (unique[i]) continue; + + unique[i] = TRUE; + if (!config->entries[i]->version) continue; - s = xpool_print(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->version); - FreePool(config->entries[i]->title_show); - config->entries[i]->title_show = s; + _cleanup_freepool_ CHAR16 *t = config->entries[i]->title_show; + config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->version); } - if (!find_nonunique(config->entries, config->entry_count)) + if (entries_unique(config->entries, unique, config->entry_count)) return; /* add machine-id to non-unique titles */ for (UINTN i = 0; i < config->entry_count; i++) { - CHAR16 *s; - _cleanup_freepool_ CHAR16 *m = NULL; - - if (!config->entries[i]->non_unique) + if (unique[i]) continue; + + unique[i] = TRUE; + if (!config->entries[i]->machine_id) continue; - m = xstrdup(config->entries[i]->machine_id); - m[8] = '\0'; - s = xpool_print(L"%s (%s)", config->entries[i]->title_show, m); - FreePool(config->entries[i]->title_show); - config->entries[i]->title_show = s; + _cleanup_freepool_ CHAR16 *t = config->entries[i]->title_show; + config->entries[i]->title_show = xpool_print( + L"%s (%.*s)", + t, + StrnLen(config->entries[i]->machine_id, 8), + config->entries[i]->machine_id); } - if (!find_nonunique(config->entries, config->entry_count)) + if (entries_unique(config->entries, unique, config->entry_count)) return; /* add file name to non-unique titles */ for (UINTN i = 0; i < config->entry_count; i++) { - CHAR16 *s; - - if (!config->entries[i]->non_unique) + if (unique[i]) continue; - s = xpool_print(L"%s (%s)", config->entries[i]->title_show, config->entries[i]->id); - FreePool(config->entries[i]->title_show); - config->entries[i]->title_show = s; - config->entries[i]->non_unique = FALSE; + + _cleanup_freepool_ CHAR16 *t = config->entries[i]->title_show; + config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->id); } } @@ -1808,7 +1876,6 @@ static BOOLEAN config_entry_add_call( .id = xstrdup(id), .title = xstrdup(title), .call = call, - .no_autoselect = TRUE, .tries_done = UINTN_MAX, .tries_left = UINTN_MAX, }; @@ -1825,6 +1892,7 @@ static ConfigEntry *config_entry_add_loader( CHAR16 key, const CHAR16 *title, const CHAR16 *loader, + const CHAR16 *sort_key, const CHAR16 *version) { ConfigEntry *entry; @@ -1843,6 +1911,7 @@ static ConfigEntry *config_entry_add_loader( .device = device, .loader = xstrdup(loader), .id = xstrdup(id), + .sort_key = xstrdup(sort_key), .key = key, .tries_done = UINTN_MAX, .tries_left = UINTN_MAX, @@ -1877,7 +1946,7 @@ static BOOLEAN is_sd_boot(EFI_FILE *root_dir, const CHAR16 *loader_path) { return CompareMem(content, magic, sizeof(magic)) == 0; } -static BOOLEAN config_entry_add_loader_auto( +static ConfigEntry *config_entry_add_loader_auto( Config *config, EFI_HANDLE *device, EFI_FILE *root_dir, @@ -1887,10 +1956,6 @@ static BOOLEAN config_entry_add_loader_auto( const CHAR16 *title, const CHAR16 *loader) { - EFI_FILE_HANDLE handle; - ConfigEntry *entry; - EFI_STATUS err; - assert(config); assert(device); assert(root_dir); @@ -1899,7 +1964,7 @@ static BOOLEAN config_entry_add_loader_auto( assert(loader || loaded_image_path); if (!config->auto_entries) - return FALSE; + return NULL; if (loaded_image_path) { loader = L"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME ".efi"; @@ -1912,28 +1977,21 @@ static BOOLEAN config_entry_add_loader_auto( if (StriCmp(loader, loaded_image_path) == 0 || is_sd_boot(root_dir, loader) || is_sd_boot(root_dir, L"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME L".EFI")) - return FALSE; + return NULL; } /* check existence */ - err = root_dir->Open(root_dir, &handle, (CHAR16*) loader, EFI_FILE_MODE_READ, 0ULL); + _cleanup_(file_closep) EFI_FILE *handle = NULL; + EFI_STATUS err = root_dir->Open(root_dir, &handle, (CHAR16*) loader, EFI_FILE_MODE_READ, 0ULL); if (EFI_ERROR(err)) - return FALSE; - handle->Close(handle); + return NULL; - entry = config_entry_add_loader(config, device, LOADER_UNDEFINED, id, key, title, loader, NULL); - if (!entry) - return FALSE; - - /* do not boot right away into auto-detected entries */ - entry->no_autoselect = TRUE; - - return TRUE; + return config_entry_add_loader(config, device, LOADER_AUTO, id, key, title, loader, NULL, NULL); } static void config_entry_add_osx(Config *config) { EFI_STATUS err; - UINTN handle_count = 0; + UINTN n_handles = 0; _cleanup_freepool_ EFI_HANDLE *handles = NULL; assert(config); @@ -1941,22 +1999,106 @@ static void config_entry_add_osx(Config *config) { if (!config->auto_entries) return; - err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &handle_count, &handles); - if (!EFI_ERROR(err)) { - for (UINTN i = 0; i < handle_count; i++) { - EFI_FILE *root; - BOOLEAN found; + err = LibLocateHandle(ByProtocol, &FileSystemProtocol, NULL, &n_handles, &handles); + if (EFI_ERROR(err)) + return; - root = LibOpenRoot(handles[i]); - if (!root) - continue; - found = config_entry_add_loader_auto(config, handles[i], root, NULL, L"auto-osx", 'a', L"macOS", - L"\\System\\Library\\CoreServices\\boot.efi"); - root->Close(root); - if (found) - break; + for (UINTN i = 0; i < n_handles; i++) { + _cleanup_(file_closep) EFI_FILE *root = LibOpenRoot(handles[i]); + if (!root) + continue; + + if (config_entry_add_loader_auto( + config, + handles[i], + root, + NULL, + L"auto-osx", + 'a', + L"macOS", + L"\\System\\Library\\CoreServices\\boot.efi")) + break; + } +} + +static EFI_STATUS boot_windows_bitlocker(void) { + _cleanup_freepool_ EFI_HANDLE *handles = NULL; + UINTN n_handles; + EFI_STATUS err; + + // FIXME: Experimental for now. Should be generalized, and become a per-entry option that can be + // enabled independently of BitLocker, and without a BootXXXX entry pre-existing. + + /* BitLocker key cannot be sealed without a TPM present. */ + if (!tpm_present()) + return EFI_NOT_FOUND; + + err = BS->LocateHandleBuffer(ByProtocol, &BlockIoProtocol, NULL, &n_handles, &handles); + if (EFI_ERROR(err)) + return err; + + /* Look for BitLocker magic string on all block drives. */ + BOOLEAN found = FALSE; + for (UINTN i = 0; i < n_handles; i++) { + EFI_BLOCK_IO *block_io; + err = BS->HandleProtocol(handles[i], &BlockIoProtocol, (void **) &block_io); + if (EFI_ERROR(err) || block_io->Media->BlockSize < 512) + continue; + + CHAR8 buf[block_io->Media->BlockSize]; + err = block_io->ReadBlocks(block_io, block_io->Media->MediaId, 0, sizeof(buf), buf); + if (EFI_ERROR(err)) + continue; + + if (CompareMem(buf + 3, "-FVE-FS-", STRLEN("-FVE-FS-")) == 0) { + found = TRUE; + break; } } + + /* If no BitLocker drive was found, we can just chainload bootmgfw.efi directly. */ + if (!found) + return EFI_NOT_FOUND; + + _cleanup_freepool_ UINT16 *boot_order = NULL; + UINTN boot_order_size; + + /* There can be gaps in Boot#### entries. Instead of iterating over the full + * EFI var list or UINT16 namespace, just look for "Windows Boot Manager" in BootOrder. */ + err = efivar_get_raw(EFI_GLOBAL_GUID, L"BootOrder", (CHAR8 **) &boot_order, &boot_order_size); + if (EFI_ERROR(err) || boot_order_size % sizeof(UINT16) != 0) + return err; + + for (UINTN i = 0; i < boot_order_size / sizeof(UINT16); i++) { + _cleanup_freepool_ CHAR8 *buf = NULL; + CHAR16 name[sizeof(L"Boot0000")]; + UINTN buf_size; + + SPrint(name, sizeof(name), L"Boot%04x", boot_order[i]); + err = efivar_get_raw(EFI_GLOBAL_GUID, name, &buf, &buf_size); + if (EFI_ERROR(err)) + continue; + + /* Boot#### are EFI_LOAD_OPTION. But we really are only interested + * for the description, which is at this offset. */ + UINTN offset = sizeof(UINT32) + sizeof(UINT16); + if (buf_size < offset + sizeof(CHAR16)) + continue; + + if (streq((CHAR16 *) (buf + offset), L"Windows Boot Manager")) { + err = efivar_set_raw( + EFI_GLOBAL_GUID, + L"BootNext", + boot_order + i, + sizeof(boot_order[i]), + EFI_VARIABLE_NON_VOLATILE); + if (EFI_ERROR(err)) + return err; + return RT->ResetSystem(EfiResetWarm, EFI_SUCCESS, 0, NULL); + } + } + + return EFI_NOT_FOUND; } static void config_entry_add_windows(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir) { @@ -1978,9 +2120,12 @@ static void config_entry_add_windows(Config *config, EFI_HANDLE *device, EFI_FIL if (!EFI_ERROR(err)) title = get_bcd_title((UINT8 *) bcd, len); - config_entry_add_loader_auto(config, device, root_dir, NULL, - L"auto-windows", 'w', title ?: L"Windows Boot Manager", - L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi"); + ConfigEntry *e = config_entry_add_loader_auto(config, device, root_dir, NULL, + L"auto-windows", 'w', title ?: L"Windows Boot Manager", + L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi"); + + if (config->reboot_for_bitlocker) + e->call = boot_windows_bitlocker; #endif } @@ -1989,12 +2134,14 @@ static void config_entry_add_linux( EFI_HANDLE *device, EFI_FILE *root_dir) { - _cleanup_(FileHandleClosep) EFI_FILE_HANDLE linux_dir = NULL; + _cleanup_(file_closep) EFI_FILE *linux_dir = NULL; _cleanup_freepool_ EFI_FILE_INFO *f = NULL; ConfigEntry *entry; UINTN f_size = 0; EFI_STATUS err; + /* Adds Boot Loader Type #2 entries (i.e. /EFI/Linux/….efi) */ + assert(config); assert(device); assert(root_dir); @@ -2019,7 +2166,7 @@ static void config_entry_add_linux( _cleanup_freepool_ CHAR16 *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL, *os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL, *path = NULL; - const CHAR16 *good_name, *good_version; + const CHAR16 *good_name, *good_version, *good_sort_key; _cleanup_freepool_ CHAR8 *content = NULL; UINTN offs[_SECTION_MAX] = {}; UINTN szs[_SECTION_MAX] = {}; @@ -2100,7 +2247,7 @@ static void config_entry_add_linux( } } - if (!bootspec_pick_name_version( + if (!bootspec_pick_name_version_sort_key( os_pretty_name, os_image_id, os_name, @@ -2110,18 +2257,20 @@ static void config_entry_add_linux( os_version_id, os_build_id, &good_name, - &good_version)) + &good_version, + &good_sort_key)) continue; path = xpool_print(L"\\EFI\\Linux\\%s", f->FileName); entry = config_entry_add_loader( config, device, - LOADER_LINUX, - f->FileName, + LOADER_UNIFIED_LINUX, + /* id= */ f->FileName, /* key= */ 'l', - good_name, - path, + /* title= */ good_name, + /* loader= */ path, + /* sort_key= */ good_sort_key, good_version); config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi"); @@ -2147,8 +2296,8 @@ static void config_load_xbootldr( Config *config, EFI_HANDLE *device) { + _cleanup_(file_closep) EFI_FILE *root_dir = NULL; EFI_HANDLE new_device; - EFI_FILE *root_dir; EFI_STATUS err; assert(config); @@ -2163,7 +2312,7 @@ static void config_load_xbootldr( } static EFI_STATUS image_start( - EFI_FILE_HANDLE root_dir, + EFI_FILE *root_dir, EFI_HANDLE parent_image, const Config *config, const ConfigEntry *entry) { @@ -2177,6 +2326,10 @@ static EFI_STATUS image_start( assert(config); assert(entry); + /* If this loader entry has a special way to boot, try that first. */ + if (entry->call) + (void) entry->call(); + path = FileDevicePath(entry->device, entry->loader); if (!path) return log_error_status_stall(EFI_INVALID_PARAMETER, L"Error getting device path."); @@ -2260,7 +2413,7 @@ static void config_write_entries_to_variable(Config *config) { static void save_selected_entry(const Config *config, const ConfigEntry *entry) { assert(config); assert(entry); - assert(!entry->call); + assert(entry->loader || !entry->call); /* Always export the selected boot entry to the system in a volatile var. */ (void) efivar_set(LOADER_GUID, L"LoaderEntrySelected", entry->id, 0); @@ -2344,7 +2497,7 @@ static void config_load_all_entries( config_load_xbootldr(config, loaded_image->DeviceHandle); /* sort entries after version number */ - config_sort_entries(config); + sort_pointer_array((void **) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare); /* if we find some well-known loaders, add them to the end of the list */ config_entry_add_osx(config); @@ -2359,11 +2512,21 @@ static void config_load_all_entries( L"auto-reboot-to-firmware-setup", L"Reboot Into Firmware Interface", reboot_into_firmware); + + if (config->entry_count == 0) + return; + + config_write_entries_to_variable(config); + + config_title_generate(config); + + /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */ + config_default_entry_select(config); } EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_LOADED_IMAGE *loaded_image; - _cleanup_(FileHandleClosep) EFI_FILE *root_dir = NULL; + _cleanup_(file_closep) EFI_FILE *root_dir = NULL; _cleanup_(config_free) Config config = {}; CHAR16 *loaded_image_path; EFI_STATUS err; @@ -2372,6 +2535,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { InitializeLib(image, sys_table); init_usec = time_usec(); + debug_hook(L"systemd-boot"); + /* Uncomment the next line if you need to wait for debugger. */ + // debug_break(); err = BS->OpenProtocol(image, &LoadedImageProtocol, @@ -2407,20 +2573,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { goto out; } - config_write_entries_to_variable(&config); - - config_title_generate(&config); - - /* select entry by configured pattern or EFI LoaderDefaultEntry= variable */ - config_default_entry_select(&config); - - /* if no configured entry to select from was found, enable the menu */ - if (config.idx_default == -1) { - config.idx_default = 0; - if (config.timeout_sec == 0) - config.timeout_sec = 10; - } - /* select entry or show menu when key is pressed or timeout is set */ if (config.force_menu || config.timeout_sec > 0) menu = TRUE; @@ -2430,11 +2582,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { /* Block up to 100ms to give firmware time to get input working. */ err = console_key_read(&key, 100 * 1000); if (!EFI_ERROR(err)) { - INT16 idx; - /* find matching key in config entries */ - idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key)); - if (idx >= 0) + UINTN idx = entry_lookup_key(&config, config.idx_default, KEYCHAR(key)); + if (idx != IDX_INVALID) config.idx_default = idx; else menu = TRUE; @@ -2451,8 +2601,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { break; } - /* run special entry like "reboot" */ - if (entry->call) { + /* Run special entry like "reboot" now. Those that have a loader + * will be handled by image_start() instead. */ + if (entry->call && !entry->loader) { entry->call(); continue; } diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c index df7a06682..937ad7ddf 100644 --- a/src/boot/efi/console.c +++ b/src/boot/efi/console.c @@ -12,7 +12,7 @@ #define VERTICAL_MAX_OK 1080 #define VIEWPORT_RATIO 10 -static inline void EventClosep(EFI_EVENT *event) { +static inline void event_closep(EFI_EVENT *event) { if (!*event) return; @@ -32,7 +32,7 @@ static inline void EventClosep(EFI_EVENT *event) { * Also, multiple input protocols can be backed by the same device, but they can be out of * sync. Falling back on a different protocol can end up with double input. * - * Therefore, we will perferrably use TextInputEx for ConIn if that is available. Additionally, + * Therefore, we will preferably use TextInputEx for ConIn if that is available. Additionally, * we look for the first TextInputEx device the firmware gives us as a fallback option. It * will replace ConInEx permanently if it ever reports a key press. * Lastly, a timer event allows us to provide a input timeout without having to call into @@ -42,7 +42,7 @@ EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec) { static BOOLEAN checked = FALSE; UINTN index; EFI_STATUS err; - _cleanup_(EventClosep) EFI_EVENT timer = NULL; + _cleanup_(event_closep) EFI_EVENT timer = NULL; assert(key); @@ -181,19 +181,32 @@ static EFI_STATUS change_mode(INT64 mode) { return err; } -static INT64 get_auto_mode(void) { - EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; +EFI_STATUS query_screen_resolution(UINT32 *ret_w, UINT32 *ret_h) { EFI_STATUS err; + EFI_GRAPHICS_OUTPUT_PROTOCOL *go; - err = LibLocateProtocol(&GraphicsOutputProtocol, (void **)&GraphicsOutput); - if (!EFI_ERROR(err) && GraphicsOutput->Mode && GraphicsOutput->Mode->Info) { - EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info = GraphicsOutput->Mode->Info; + err = LibLocateProtocol(&GraphicsOutputProtocol, (void **) &go); + if (EFI_ERROR(err)) + return err; + + if (!go->Mode || !go->Mode->Info) + return EFI_DEVICE_ERROR; + + *ret_w = go->Mode->Info->HorizontalResolution; + *ret_h = go->Mode->Info->VerticalResolution; + return EFI_SUCCESS; +} + +static INT64 get_auto_mode(void) { + UINT32 screen_width, screen_height; + + if (!EFI_ERROR(query_screen_resolution(&screen_width, &screen_height))) { BOOLEAN keep = FALSE; /* Start verifying if we are in a resolution larger than Full HD * (1920x1080). If we're not, assume we're in a good mode and do not * try to change it. */ - if (Info->HorizontalResolution <= HORIZONTAL_MAX_OK && Info->VerticalResolution <= VERTICAL_MAX_OK) + if (screen_width <= HORIZONTAL_MAX_OK && screen_height <= VERTICAL_MAX_OK) keep = TRUE; /* For larger resolutions, calculate the ratio of the total screen * area to the text viewport area. If it's less than 10 times bigger, @@ -201,7 +214,7 @@ static INT64 get_auto_mode(void) { else { UINT64 text_area; UINTN x_max, y_max; - UINT64 screen_area = (UINT64)Info->HorizontalResolution * (UINT64)Info->VerticalResolution; + UINT64 screen_area = (UINT64)screen_width * (UINT64)screen_height; console_query_mode(&x_max, &y_max); text_area = SYSTEM_FONT_WIDTH * SYSTEM_FONT_HEIGHT * (UINT64)x_max * (UINT64)y_max; diff --git a/src/boot/efi/console.h b/src/boot/efi/console.h index 59578f789..cada64307 100644 --- a/src/boot/efi/console.h +++ b/src/boot/efi/console.h @@ -34,3 +34,4 @@ enum { EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec); EFI_STATUS console_set_mode(INT64 mode); EFI_STATUS console_query_mode(UINTN *x_max, UINTN *y_max); +EFI_STATUS query_screen_resolution(UINT32 *ret_width, UINT32 *ret_height); diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c index cf7a3ec13..14957130b 100644 --- a/src/boot/efi/cpio.c +++ b/src/boot/efi/cpio.c @@ -311,12 +311,13 @@ EFI_STATUS pack_cpio( const CHAR8 *target_dir_prefix, UINT32 dir_mode, UINT32 access_mode, - UINTN tpm_pcr, + const UINT32 tpm_pcr[], + UINTN n_tpm_pcr, const CHAR16 *tpm_description, void **ret_buffer, UINTN *ret_buffer_size) { - _cleanup_(FileHandleClosep) EFI_FILE_HANDLE root = NULL, extra_dir = NULL; + _cleanup_(file_closep) EFI_FILE *root = NULL, *extra_dir = NULL; UINTN dirent_size = 0, buffer_size = 0, n_items = 0, n_allocated = 0; _cleanup_freepool_ CHAR16 *rel_dropin_dir = NULL; _cleanup_freepool_ EFI_FILE_INFO *dirent = NULL; @@ -328,6 +329,7 @@ EFI_STATUS pack_cpio( assert(loaded_image); assert(target_dir_prefix); + assert(tpm_pcr || n_tpm_pcr == 0); assert(ret_buffer); assert(ret_buffer_size); @@ -449,13 +451,15 @@ EFI_STATUS pack_cpio( if (EFI_ERROR(err)) return log_error_status_stall(err, L"Failed to pack cpio trailer: %r"); - err = tpm_log_event( - tpm_pcr, - POINTER_TO_PHYSICAL_ADDRESS(buffer), - buffer_size, - tpm_description); - if (EFI_ERROR(err)) - log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr, tpm_description, err); + for (UINTN i = 0; i < n_tpm_pcr; i++) { + err = tpm_log_event( + tpm_pcr[i], + POINTER_TO_PHYSICAL_ADDRESS(buffer), + buffer_size, + tpm_description); + if (EFI_ERROR(err)) + log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr[i], tpm_description, err); + } *ret_buffer = TAKE_PTR(buffer); *ret_buffer_size = buffer_size; diff --git a/src/boot/efi/cpio.h b/src/boot/efi/cpio.h index a272d2892..5f6698fd1 100644 --- a/src/boot/efi/cpio.h +++ b/src/boot/efi/cpio.h @@ -10,8 +10,8 @@ EFI_STATUS pack_cpio( const CHAR8 *target_dir_prefix, UINT32 dir_mode, UINT32 access_mode, - UINTN tpm_pcr, + const UINT32 tpm_pcr[], + UINTN n_tpm_pcr, const CHAR16 *tpm_description, void **ret_buffer, UINTN *ret_buffer_size); - diff --git a/src/boot/efi/devicetree.c b/src/boot/efi/devicetree.c index 30ba88c4f..b8ba52a52 100644 --- a/src/boot/efi/devicetree.c +++ b/src/boot/efi/devicetree.c @@ -64,9 +64,8 @@ static EFI_STATUS devicetree_fixup(struct devicetree_state *state, UINTN len) { return err; } -EFI_STATUS devicetree_install(struct devicetree_state *state, - EFI_FILE_HANDLE root_dir, CHAR16 *name) { - _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL; +EFI_STATUS devicetree_install(struct devicetree_state *state, EFI_FILE *root_dir, CHAR16 *name) { + _cleanup_(file_closep) EFI_FILE *handle = NULL; _cleanup_freepool_ EFI_FILE_INFO *info = NULL; UINTN len; EFI_STATUS err; diff --git a/src/boot/efi/devicetree.h b/src/boot/efi/devicetree.h index fa8a1be6e..71ce10502 100644 --- a/src/boot/efi/devicetree.h +++ b/src/boot/efi/devicetree.h @@ -7,7 +7,7 @@ struct devicetree_state { void *orig; }; -EFI_STATUS devicetree_install(struct devicetree_state *state, EFI_FILE_HANDLE root_dir, CHAR16 *name); +EFI_STATUS devicetree_install(struct devicetree_state *state, EFI_FILE *root_dir, CHAR16 *name); EFI_STATUS devicetree_install_from_memory( struct devicetree_state *state, const VOID *dtb_buffer, UINTN dtb_length); void devicetree_cleanup(struct devicetree_state *state); diff --git a/src/boot/efi/drivers.c b/src/boot/efi/drivers.c index 61df20e24..376745f52 100644 --- a/src/boot/efi/drivers.c +++ b/src/boot/efi/drivers.c @@ -36,11 +36,11 @@ static EFI_STATUS load_one_driver( err = BS->HandleProtocol(image, &LoadedImageProtocol, (void **)&loaded_image); if (EFI_ERROR(err)) - return log_error_status_stall(err, L"Failed to find protocol in driver image s: %r", fname, err); + return log_error_status_stall(err, L"Failed to find protocol in driver image %s: %r", fname, err); if (loaded_image->ImageCodeType != EfiBootServicesCode && loaded_image->ImageCodeType != EfiRuntimeServicesCode) - return log_error_status_stall(EFI_INVALID_PARAMETER, L"Image %s is not a driver, refusing: %r", fname); + return log_error_status_stall(EFI_INVALID_PARAMETER, L"Image %s is not a driver, refusing.", fname); err = BS->StartImage(image, NULL, NULL); if (EFI_ERROR(err)) { @@ -80,9 +80,9 @@ static EFI_STATUS reconnect(void) { EFI_STATUS load_drivers( EFI_HANDLE parent_image, EFI_LOADED_IMAGE *loaded_image, - EFI_FILE_HANDLE root_dir) { + EFI_FILE *root_dir) { - _cleanup_(FileHandleClosep) EFI_FILE_HANDLE drivers_dir = NULL; + _cleanup_(file_closep) EFI_FILE *drivers_dir = NULL; _cleanup_freepool_ EFI_FILE_INFO *dirent = NULL; UINTN dirent_size = 0, n_succeeded = 0; EFI_STATUS err; diff --git a/src/boot/efi/drivers.h b/src/boot/efi/drivers.h index c192c6d44..242aedcdd 100644 --- a/src/boot/efi/drivers.h +++ b/src/boot/efi/drivers.h @@ -6,4 +6,4 @@ EFI_STATUS load_drivers( EFI_HANDLE parent_image, EFI_LOADED_IMAGE *loaded_image, - EFI_FILE_HANDLE root_dir); + EFI_FILE *root_dir); diff --git a/src/boot/efi/fuzz-bcd.c b/src/boot/efi/fuzz-bcd.c new file mode 100644 index 000000000..3df55a5c3 --- /dev/null +++ b/src/boot/efi/fuzz-bcd.c @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "alloc-util.h" +#include "fd-util.h" +#include "fuzz.h" +#include "utf8.h" + +#include "bcd.c" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_free_ void *p = NULL; + + /* This limit was borrowed from src/boot/efi/boot.c */ + if (size > 100*1024) + return 0; + + if (!getenv("SYSTEMD_LOG_LEVEL")) + log_set_max_level(LOG_CRIT); + + p = memdup(data, size); + assert_se(p); + + char16_t *title = get_bcd_title(p, size); + if (title) + (void) char16_strlen(title); + return 0; +} diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c index 4384c9dbf..da4fd18ea 100644 --- a/src/boot/efi/measure.c +++ b/src/boot/efi/measure.c @@ -135,12 +135,20 @@ static EFI_TCG2 * tcg2_interface_check(void) { return tcg; } -EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) { +BOOLEAN tpm_present(void) { + return tcg2_interface_check() || tcg1_interface_check(); +} + +EFI_STATUS tpm_log_event(UINT32 pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) { EFI_TCG *tpm1; EFI_TCG2 *tpm2; assert(description); + /* PCR disabled */ + if (pcrindex == UINT32_MAX) + return EFI_SUCCESS; + tpm2 = tcg2_interface_check(); if (tpm2) return tpm2_measure_to_pcr_and_event_log(tpm2, pcrindex, buffer, buffer_size, description); @@ -158,11 +166,15 @@ EFI_STATUS tpm_log_load_options(const CHAR16 *load_options) { /* Measures a load options string into the TPM2, i.e. the kernel command line */ - err = tpm_log_event(TPM_PCR_INDEX_KERNEL_PARAMETERS, - POINTER_TO_PHYSICAL_ADDRESS(load_options), - StrSize(load_options), load_options); - if (EFI_ERROR(err)) - return log_error_status_stall(err, L"Unable to add load options (i.e. kernel command) line measurement: %r", err); + for (UINTN i = 0; i < 2; i++) { + UINT32 pcr = i == 0 ? TPM_PCR_INDEX_KERNEL_PARAMETERS : TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT; + + err = tpm_log_event(pcr, + POINTER_TO_PHYSICAL_ADDRESS(load_options), + StrSize(load_options), load_options); + if (EFI_ERROR(err)) + return log_error_status_stall(err, L"Unable to add load options (i.e. kernel command) line measurement to PCR %u: %r", pcr, err); + } return EFI_SUCCESS; } diff --git a/src/boot/efi/measure.h b/src/boot/efi/measure.h index b92d0574c..e951ff7ef 100644 --- a/src/boot/efi/measure.h +++ b/src/boot/efi/measure.h @@ -3,16 +3,36 @@ #include +/* This TPM PCR is where we extend the kernel command line and any passed credentials here. */ +#define TPM_PCR_INDEX_KERNEL_PARAMETERS 12U + +/* We used to write the kernel command line/credentials into PCR 8, in systemd <= 250. Let's provide for + * some compatibility. (Remove in 2023!) */ +#if EFI_TPM_PCR_COMPAT +#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT 8U +#else +#define TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT UINT32_MAX +#endif + +/* This TPM PCR is where most Linux infrastructure extends the initrd binary images into, and so do we. */ +#define TPM_PCR_INDEX_INITRD 4U + #if ENABLE_TPM -EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description); +BOOLEAN tpm_present(void); +EFI_STATUS tpm_log_event(UINT32 pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description); EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline); #else -static inline EFI_STATUS tpm_log_event(UINT32 pcrindex, const EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) { +static inline BOOLEAN tpm_present(void) { + return FALSE; +} + +static inline EFI_STATUS tpm_log_event(UINT32 pcrindex, EFI_PHYSICAL_ADDRESS buffer, UINTN buffer_size, const CHAR16 *description) { return EFI_SUCCESS; } + static inline EFI_STATUS tpm_log_load_options(const CHAR16 *cmdline) { return EFI_SUCCESS; } diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 2c283b8c7..ffee98bbb 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -43,11 +43,7 @@ if not cc.has_header_symbol('efi.h', 'EFI_IMAGE_MACHINE_X64', subdir_done() endif -objcopy = find_program('objcopy') -efi_cc = get_option('efi-cc') -if efi_cc.length() == 0 - efi_cc = cc.cmd_array() -endif +objcopy = run_command(cc.cmd_array(), '-print-prog-name=objcopy', check: true).stdout().strip() efi_ld = get_option('efi-ld') if efi_ld == 'auto' @@ -61,8 +57,11 @@ endif efi_libdir = '' foreach dir : [get_option('efi-libdir'), '/usr/lib/gnuefi' / efi_arch[0], - run_command('realpath', '-e', - '/usr/lib' / run_command(efi_cc, '-print-multi-os-directory').stdout().strip()).stdout().strip()] + run_command( + 'realpath', '-e', + '/usr/lib' / run_command(cc.cmd_array(), '-print-multi-os-directory', check: false).stdout().strip(), + check: false + ).stdout().strip()] if dir != '' and fs.is_dir(dir) efi_libdir = dir break @@ -105,6 +104,7 @@ conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0]) efi_conf = configuration_data() efi_conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0]) efi_conf.set10('ENABLE_TPM', get_option('tpm')) +efi_conf.set10('EFI_TPM_PCR_COMPAT', get_option('efi-tpm-pcr-compat')) foreach ctype : ['color-normal', 'color-entry', 'color-highlight', 'color-edit'] c = get_option('efi-' + ctype).split(',') @@ -129,8 +129,7 @@ elif get_option('sbat-distro') != '' value = get_option(sbatvar[0]) if (value == '' or value == 'auto') and not meson.is_cross_build() cmd = 'if [ -e /etc/os-release ]; then . /etc/os-release; else . /usr/lib/os-release; fi; echo $@0@'.format(sbatvar[1]) - value = run_command(sh, '-c', cmd).stdout().strip() - message('@0@ (from @1@): @2@'.format(sbatvar[0], sbatvar[1], value)) + value = run_command(sh, '-c', cmd, check: true).stdout().strip() endif if value == '' error('Required @0@ option not set and autodetection failed'.format(sbatvar[0])) @@ -147,8 +146,11 @@ elif get_option('sbat-distro') != '' pkgver = get_option('sbat-distro-version') if pkgver == '' efi_conf.set('SBAT_DISTRO_VERSION', 'GIT_VERSION') + # This is determined during build, not configuration, so we can't display it yet. + sbat_distro_version_display = '(git version)' else efi_conf.set_quoted('SBAT_DISTRO_VERSION', pkgver) + sbat_distro_version_display = pkgver endif endif @@ -255,20 +257,14 @@ else endif if efi_arch[1] == 'arm' - # On arm, the compiler (correctly) gives us the following warning: - # libgcc.a(_popcountsi2.o) uses 4-byte wchar_t yet the output is to - # use 2-byte wchar_t; use of wchar_t values across objects may fail - # - # libgcc does not have any occurrences of wchar_t in its sources or the - # documentation, so it's safe to assume that we can ignore this warning. - # - # So far, this only happens with arm due to popcount even though x86 and - # x86_64 also have to rely on libgcc's popcount. Therefore, we only disable - # this for arm to make sure this doesn't mask other issues in the future. - efi_ldflags += ['-Wl,--no-warn-mismatch'] + # On arm, the compiler (correctly) warns about wchar_t size mismatch. This + # is because libgcc is not compiled with -fshort-wchar, but it does not + # have any occurrences of wchar_t in its sources or the documentation, so + # it is safe to assume that we can ignore this warning. + efi_ldflags += ['-Wl,--no-wchar-size-warning'] endif -if run_command('grep', '-q', '__CTOR_LIST__', efi_lds).returncode() == 0 +if run_command('grep', '-q', '__CTOR_LIST__', efi_lds, check: false).returncode() == 0 # fedora has a patched gnu-efi that adds support for ELF constructors. # If ld is called by gcc something about these symbols breaks, resulting # in sd-boot freezing when gnu-efi runs the constructors. Force defining @@ -285,12 +281,29 @@ if run_command('grep', '-q', '__CTOR_LIST__', efi_lds).returncode() == 0 ] endif -efi_cc_version = run_command(efi_cc, '--version').stdout().split('\n')[0] -if efi_cc_version.contains('clang') and efi_cc_version.split('.')[0].split(' ')[-1].to_int() <= 10 +if cc.get_id() == 'clang' and cc.version().split('.')[0].to_int() <= 10 # clang <= 10 doesn't pass -T to the linker and then even complains about it being unused efi_ldflags += ['-Wl,-T,' + efi_lds, '-Wno-unused-command-line-argument'] endif +summary({ + 'EFI machine type' : efi_arch[0], + 'EFI LD' : efi_ld, + 'EFI lds' : efi_lds, + 'EFI crt0' : efi_crt0, + 'EFI include directory' : efi_incdir}, + section : 'Extensible Firmware Interface') + +if efi_conf.get('SBAT_DISTRO', '') != '' + summary({ + 'SBAT distro': efi_conf.get('SBAT_DISTRO'), + 'SBAT distro generation': efi_conf.get('SBAT_DISTRO_GENERATION'), + 'SBAT distro version': sbat_distro_version_display, + 'SBAT distro summary': efi_conf.get('SBAT_DISTRO_SUMMARY'), + 'SBAT distro URL': efi_conf.get('SBAT_DISTRO_URL')}, + section : 'Extensible Firmware Interface') +endif + ############################################################ efi_headers = files( @@ -301,13 +314,16 @@ efi_headers = files( 'disk.h', 'drivers.h', 'graphics.h', + 'initrd.h', 'linux.h', 'measure.h', 'missing_efi.h', 'pe.h', 'random-seed.h', + 'secure-boot.h', 'shim.h', 'splash.h', + 'ticks.h', 'util.h', 'xbootldr.h', ) @@ -320,6 +336,7 @@ common_sources = files( 'measure.c', 'pe.c', 'secure-boot.c', + 'ticks.c', 'util.c', ) @@ -349,12 +366,15 @@ endif if efi_arch[1] in ['ia32', 'x86_64', 'arm', 'aarch64'] systemd_boot_sources += files('bcd.c') tests += [ - [['src/boot/efi/test-bcd.c'], + [files('test-bcd.c'), [], [libzstd], [], 'HAVE_ZSTD'], ] + fuzzers += [ + [files('fuzz-bcd.c')], + ] endif systemd_boot_objects = [] @@ -364,7 +384,7 @@ foreach file : fundamental_source_paths + common_sources + systemd_boot_sources o_file = custom_target('@0@.o'.format(file).split('/')[-1], input : file, output : '@0@.o'.format(file).split('/')[-1], - command : [efi_cc, '-c', '@INPUT@', '-o', '@OUTPUT@', efi_cflags], + command : [cc.cmd_array(), '-c', '@INPUT@', '-o', '@OUTPUT@', efi_cflags], depend_files : efi_headers + fundamental_headers) if (fundamental_source_paths + common_sources + systemd_boot_sources).contains(file) systemd_boot_objects += o_file @@ -374,25 +394,27 @@ foreach file : fundamental_source_paths + common_sources + systemd_boot_sources endif endforeach -systemd_boot_efi_name = 'systemd-boot@0@.efi'.format(efi_arch[0]) -stub_elf_name = 'linux@0@.elf.stub'.format(efi_arch[0]) -stub_efi_name = 'linux@0@.efi.stub'.format(efi_arch[0]) - -efi_stubs = [] -foreach tuple : [['systemd_boot.so', systemd_boot_efi_name, systemd_boot_objects, false], - [stub_elf_name, stub_efi_name, stub_objects, true]] - so = custom_target( - tuple[0], - input : tuple[2], - output : tuple[0], - command : [efi_cc, '-o', '@OUTPUT@', efi_ldflags, efi_cflags, tuple[2], '-lefi', '-lgnuefi', '-lgcc'], - install : tuple[3], +foreach tuple : [['systemd-boot@0@.@1@', systemd_boot_objects, false], + ['linux@0@.@1@.stub', stub_objects, true]] + elf = custom_target( + tuple[0].format(efi_arch[0], 'elf'), + input : tuple[1], + output : tuple[0].format(efi_arch[0], 'elf'), + command : [cc.cmd_array(), + '-o', '@OUTPUT@', + efi_cflags, + efi_ldflags, + '@INPUT@', + '-lefi', + '-lgnuefi', + '-lgcc'], + install : tuple[2], install_dir : bootlibdir) - stub = custom_target( - tuple[1], - input : so, - output : tuple[1], + custom_target( + tuple[0].format(efi_arch[0], 'efi'), + input : elf, + output : tuple[0].format(efi_arch[0], 'efi'), command : [objcopy, '-j', '.bss*', '-j', '.data', @@ -409,14 +431,4 @@ foreach tuple : [['systemd_boot.so', systemd_boot_efi_name, systemd_boot_objects '@INPUT@', '@OUTPUT@'], install : true, install_dir : bootlibdir) - - efi_stubs += [[so, stub]] endforeach - -############################################################ - -test_efi_disk_img = custom_target( - 'test-efi-disk.img', - input : [efi_stubs[0][0], efi_stubs[1][1]], - output : 'test-efi-disk.img', - command : [test_efi_create_disk_sh, '@OUTPUT@','@INPUT@', splash_bmp]) diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c index ed3b0b8e9..e16716498 100644 --- a/src/boot/efi/pe.c +++ b/src/boot/efi/pe.c @@ -224,7 +224,7 @@ EFI_STATUS pe_file_locate_sections( UINTN *offsets, UINTN *sizes) { _cleanup_freepool_ struct PeSectionHeader *section_table = NULL; - _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL; + _cleanup_(file_closep) EFI_FILE *handle = NULL; struct DosFileHeader dos; struct PeFileHeader pe; UINTN len, section_table_len; diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c index dbf309b72..b007f41f7 100644 --- a/src/boot/efi/random-seed.c +++ b/src/boot/efi/random-seed.c @@ -225,7 +225,7 @@ static void validate_sha256(void) { EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) { _cleanup_freepool_ void *seed = NULL, *new_seed = NULL, *rng = NULL, *for_kernel = NULL, *system_token = NULL; - _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL; + _cleanup_(file_closep) EFI_FILE *handle = NULL; UINTN size, rsize, wsize, system_token_size = 0; _cleanup_freepool_ EFI_FILE_INFO *info = NULL; EFI_STATUS err; diff --git a/src/boot/efi/shim.c b/src/boot/efi/shim.c index fd9c48947..fb4aecaee 100644 --- a/src/boot/efi/shim.c +++ b/src/boot/efi/shim.c @@ -105,7 +105,6 @@ static EFIAPI EFI_STATUS security_policy_authentication (const EFI_SECURITY_PROT _cleanup_freepool_ EFI_DEVICE_PATH *dev_path = NULL; _cleanup_freepool_ CHAR16 *dev_path_str = NULL; EFI_HANDLE h; - EFI_FILE *root; _cleanup_freepool_ CHAR8 *file_buffer = NULL; UINTN file_size; @@ -123,8 +122,10 @@ static EFIAPI EFI_STATUS security_policy_authentication (const EFI_SECURITY_PROT if (EFI_ERROR(status)) return status; - /* No need to check return value, this already happened in efi_main() */ - root = LibOpenRoot(h); + _cleanup_(file_closep) EFI_FILE *root = LibOpenRoot(h); + if (!root) + return EFI_NOT_FOUND; + dev_path_str = DevicePathToStr(dp); if (!dev_path_str) return EFI_OUT_OF_RESOURCES; @@ -132,7 +133,6 @@ static EFIAPI EFI_STATUS security_policy_authentication (const EFI_SECURITY_PROT status = file_read(root, dev_path_str, 0, 0, &file_buffer, &file_size); if (EFI_ERROR(status)) return status; - root->Close(root); if (shim_validate(file_buffer, file_size)) return EFI_SUCCESS; diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 0b1f276c4..73d06de7f 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -181,6 +181,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { EFI_STATUS err; InitializeLib(image, sys_table); + debug_hook(L"systemd-stub"); + /* Uncomment the next line if you need to wait for debugger. */ + // debug_break(); err = BS->OpenProtocol( image, @@ -231,7 +234,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { (const CHAR8*) ".extra/credentials", /* dir_mode= */ 0500, /* access_mode= */ 0400, - /* tpm_pcr= */ TPM_PCR_INDEX_KERNEL_PARAMETERS, + /* tpm_pcr= */ (UINT32[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT }, + /* n_tpm_pcr= */ 2, L"Credentials initrd", &credential_initrd, &credential_initrd_size); @@ -242,7 +246,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { (const CHAR8*) ".extra/global_credentials", /* dir_mode= */ 0500, /* access_mode= */ 0400, - /* tpm_pcr= */ TPM_PCR_INDEX_KERNEL_PARAMETERS, + /* tpm_pcr= */ (UINT32[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT }, + /* n_tpm_pcr= */ 2, L"Global credentials initrd", &global_credential_initrd, &global_credential_initrd_size); @@ -253,7 +258,8 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { (const CHAR8*) ".extra/sysext", /* dir_mode= */ 0555, /* access_mode= */ 0444, - /* tpm_pcr= */ TPM_PCR_INDEX_INITRD, + /* tpm_pcr= */ (UINT32[]) { TPM_PCR_INDEX_INITRD }, + /* n_tpm_pcr= */ 1, L"System extension initrd", &sysext_initrd, &sysext_initrd_size); diff --git a/src/boot/efi/test-bcd.c b/src/boot/efi/test-bcd.c index 5d3d6c2c4..d04d1ee45 100644 --- a/src/boot/efi/test-bcd.c +++ b/src/boot/efi/test-bcd.c @@ -39,8 +39,7 @@ static void test_get_bcd_title_one( } TEST(get_bcd_title) { - const char16_t win10[] = { 'W', 'i', 'n', 'd', 'o', 'w', 's', ' ', '1', '0', '\0' }; - test_get_bcd_title_one("test-bcd/win10.bcd.zst", win10, sizeof(win10)); + test_get_bcd_title_one("test-bcd/win10.bcd.zst", u"Windows 10", sizeof(u"Windows 10")); test_get_bcd_title_one("test-bcd/description-bad-type.bcd.zst", NULL, 0); test_get_bcd_title_one("test-bcd/description-empty.bcd.zst", NULL, 0); diff --git a/src/boot/efi/ticks.c b/src/boot/efi/ticks.c new file mode 100644 index 000000000..45980bafe --- /dev/null +++ b/src/boot/efi/ticks.c @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#if defined(__i386__) || defined(__x86_64__) +#include +#endif + +#include "ticks.h" + +#if defined(__i386__) || defined(__x86_64__) +static BOOLEAN in_hypervisor(void) { + uint32_t eax, ebx, ecx, edx; + + /* The TSC might or might not be virtualized in VMs (and thus might not be accurate or start at zero + * at boot), depending on hypervisor and CPU functionality. If it's not virtualized it's not useful + * for keeping time, hence don't attempt to use it. + * + * This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI + * environment. */ + + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0) + return FALSE; + + return !!(ecx & 0x80000000U); +} +#endif + +#ifdef __x86_64__ +static UINT64 ticks_read(void) { + UINT64 a, d; + + if (in_hypervisor()) + return 0; + + __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); + return (d << 32) | a; +} +#elif defined(__i386__) +static UINT64 ticks_read(void) { + UINT64 val; + + if (in_hypervisor()) + return 0; + + __asm__ volatile ("rdtsc" : "=A" (val)); + return val; +} +#elif defined(__aarch64__) +static UINT64 ticks_read(void) { + UINT64 val; + __asm__ volatile ("mrs %0, cntpct_el0" : "=r" (val)); + return val; +} +#else +static UINT64 ticks_read(void) { + return 0; +} +#endif + +#if defined(__aarch64__) +static UINT64 ticks_freq(void) { + UINT64 freq; + __asm__ volatile ("mrs %0, cntfrq_el0": "=r" (freq)); + return freq; +} +#else +/* count TSC ticks during a millisecond delay */ +static UINT64 ticks_freq(void) { + UINT64 ticks_start, ticks_end; + static UINT64 cache = 0; + + if (cache != 0) + return cache; + + ticks_start = ticks_read(); + BS->Stall(1000); + ticks_end = ticks_read(); + + if (ticks_end < ticks_start) /* Check for an overflow (which is not that unlikely, given on some + * archs the value is 32bit) */ + return 0; + + cache = (ticks_end - ticks_start) * 1000UL; + return cache; +} +#endif + +UINT64 time_usec(void) { + UINT64 ticks, freq; + + ticks = ticks_read(); + if (ticks == 0) + return 0; + + freq = ticks_freq(); + if (freq == 0) + return 0; + + return 1000UL * 1000UL * ticks / freq; +} diff --git a/src/boot/efi/ticks.h b/src/boot/efi/ticks.h new file mode 100644 index 000000000..ba259a6cc --- /dev/null +++ b/src/boot/efi/ticks.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include + +UINT64 time_usec(void); diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index 71639721b..e6250207f 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -3,69 +3,9 @@ #include #include +#include "ticks.h" #include "util.h" -#ifdef __x86_64__ -UINT64 ticks_read(void) { - UINT64 a, d; - __asm__ volatile ("rdtsc" : "=a" (a), "=d" (d)); - return (d << 32) | a; -} -#elif defined(__i386__) -UINT64 ticks_read(void) { - UINT64 val; - __asm__ volatile ("rdtsc" : "=A" (val)); - return val; -} -#elif defined(__aarch64__) -UINT64 ticks_read(void) { - UINT64 val; - __asm__ volatile ("mrs %0, cntpct_el0" : "=r" (val)); - return val; -} -#else -UINT64 ticks_read(void) { - UINT64 val = 1; - return val; -} -#endif - -#if defined(__aarch64__) -UINT64 ticks_freq(void) { - UINT64 freq; - __asm__ volatile ("mrs %0, cntfrq_el0": "=r" (freq)); - return freq; -} -#else -/* count TSC ticks during a millisecond delay */ -UINT64 ticks_freq(void) { - UINT64 ticks_start, ticks_end; - - ticks_start = ticks_read(); - BS->Stall(1000); - ticks_end = ticks_read(); - - return (ticks_end - ticks_start) * 1000UL; -} -#endif - -UINT64 time_usec(void) { - UINT64 ticks; - static UINT64 freq; - - ticks = ticks_read(); - if (ticks == 0) - return 0; - - if (freq == 0) { - freq = ticks_freq(); - if (freq == 0) - return 0; - } - - return 1000UL * 1000UL * ticks / freq; -} - EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b) { assert(b); @@ -435,11 +375,12 @@ CHAR8 *strchra(const CHAR8 *s, CHAR8 c) { return NULL; } -EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **ret, UINTN *ret_size) { - _cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL; +EFI_STATUS file_read(EFI_FILE *dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **ret, UINTN *ret_size) { + _cleanup_(file_closep) EFI_FILE *handle = NULL; _cleanup_freepool_ CHAR8 *buf = NULL; EFI_STATUS err; + assert(dir); assert(name); assert(ret); @@ -454,7 +395,7 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s if (EFI_ERROR(err)) return err; - size = info->FileSize+1; + size = info->FileSize; } if (off > 0) { @@ -463,12 +404,16 @@ EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN s return err; } - buf = xallocate_pool(size + 1); + /* Allocate some extra bytes to guarantee the result is NUL-terminated for CHAR8 and CHAR16 strings. */ + UINTN extra = size % sizeof(CHAR16) + sizeof(CHAR16); + + buf = xallocate_pool(size + extra); err = handle->Read(handle, &size, buf); if (EFI_ERROR(err)) return err; - buf[size] = '\0'; + /* Note that handle->Read() changes size to reflect the actually bytes read. */ + ZeroMem(buf + size, extra); *ret = TAKE_PTR(buf); if (ret_size) @@ -529,26 +474,22 @@ void sort_pointer_array( return; for (UINTN i = 1; i < n_members; i++) { - BOOLEAN more = FALSE; + UINTN k; + void *entry = array[i]; - for (UINTN k = 0; k < n_members - i; k++) { - void *entry; + for (k = i; k > 0; k--) { + if (compare(array[k - 1], entry) <= 0) + break; - if (compare(array[k], array[k+1]) <= 0) - continue; - - entry = array[k]; - array[k] = array[k+1]; - array[k+1] = entry; - more = TRUE; + array[k] = array[k - 1]; } - if (!more) - break; + + array[k] = entry; } } EFI_STATUS get_file_info_harder( - EFI_FILE_HANDLE handle, + EFI_FILE *handle, EFI_FILE_INFO **ret, UINTN *ret_size) { @@ -581,7 +522,7 @@ EFI_STATUS get_file_info_harder( } EFI_STATUS readdir_harder( - EFI_FILE_HANDLE handle, + EFI_FILE *handle, EFI_FILE_INFO **buffer, UINTN *buffer_size) { @@ -704,11 +645,11 @@ CHAR16 **strv_free(CHAR16 **v) { } EFI_STATUS open_directory( - EFI_FILE_HANDLE root, + EFI_FILE *root, const CHAR16 *path, - EFI_FILE_HANDLE *ret) { + EFI_FILE **ret) { - _cleanup_(FileHandleClosep) EFI_FILE_HANDLE dir = NULL; + _cleanup_(file_closep) EFI_FILE *dir = NULL; _cleanup_freepool_ EFI_FILE_INFO *file_info = NULL; EFI_STATUS err; @@ -743,3 +684,71 @@ UINT64 get_os_indications_supported(void) { return osind; } + +#ifdef EFI_DEBUG +__attribute__((noinline)) void debug_break(void) { + /* This is a poor programmer's breakpoint to wait until a debugger + * has attached to us. Just "set variable wait = 0" or "return" to continue. */ + volatile BOOLEAN wait = TRUE; + while (wait) + /* Prefer asm based stalling so that gdb has a source location to present. */ +#if defined(__i386__) || defined(__x86_64__) + asm volatile("pause"); +#elif defined(__aarch64__) + asm volatile("wfi"); +#else + BS->Stall(5000); +#endif +} +#endif + +#if defined(__i386__) || defined(__x86_64__) +static inline UINT8 inb(UINT16 port) { + UINT8 value; + asm volatile("inb %1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static inline void outb(UINT16 port, UINT8 value) { + asm volatile("outb %0, %1" : : "a"(value), "Nd"(port)); +} + +void beep(UINTN beep_count) { + enum { + PITCH = 500, + BEEP_DURATION_USEC = 100 * 1000, + WAIT_DURATION_USEC = 400 * 1000, + + PIT_FREQUENCY = 0x1234dd, + SPEAKER_CONTROL_PORT = 0x61, + SPEAKER_ON_MASK = 0x03, + TIMER_PORT_MAGIC = 0xB6, + TIMER_CONTROL_PORT = 0x43, + TIMER_CONTROL2_PORT = 0x42, + }; + + /* Set frequency. */ + UINT32 counter = PIT_FREQUENCY / PITCH; + outb(TIMER_CONTROL_PORT, TIMER_PORT_MAGIC); + outb(TIMER_CONTROL2_PORT, counter & 0xFF); + outb(TIMER_CONTROL2_PORT, (counter >> 8) & 0xFF); + + UINT8 value = inb(SPEAKER_CONTROL_PORT); + + while (beep_count > 0) { + /* Turn speaker on. */ + value |= SPEAKER_ON_MASK; + outb(SPEAKER_CONTROL_PORT, value); + + BS->Stall(BEEP_DURATION_USEC); + + /* Turn speaker off. */ + value &= ~SPEAKER_ON_MASK; + outb(SPEAKER_CONTROL_PORT, value); + + beep_count--; + if (beep_count > 0) + BS->Stall(WAIT_DURATION_USEC); + } +} +#endif diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index b40f05eae..93dd2da56 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -6,13 +6,6 @@ #include "string-util-fundamental.h" -/* This TPM PCR is where most Linux infrastructure extends the kernel command line into, and so do we. We also extend - * any passed credentials here. */ -#define TPM_PCR_INDEX_KERNEL_PARAMETERS 8 - -/* This TPM PCR is where most Linux infrastructure extends the initrd binary images into, and so do we. */ -#define TPM_PCR_INDEX_INITRD 4 - #define offsetof(type, member) __builtin_offsetof(type, member) #define UINTN_MAX (~(UINTN)0) @@ -24,35 +17,23 @@ #define UINT64_MAX ((UINT64) -1) #endif -#define assert_alloc_ret(p) \ - ({ \ - void *_p = (p); \ - assert(_p); \ - _p; \ - }) - #define xnew_alloc(type, n, alloc) \ ({ \ UINTN _alloc_size; \ - if (__builtin_mul_overflow(sizeof(type), (n), &_alloc_size)) \ - assert_not_reached(); \ + assert_se(!__builtin_mul_overflow(sizeof(type), (n), &_alloc_size)); \ (type *) alloc(_alloc_size); \ }) -#define xallocate_pool(size) assert_alloc_ret(AllocatePool(size)) -#define xallocate_zero_pool(size) assert_alloc_ret(AllocateZeroPool(size)) -#define xreallocate_pool(p, old_size, new_size) assert_alloc_ret(ReallocatePool((p), (old_size), (new_size))) -#define xpool_print(fmt, ...) ((CHAR16 *) assert_alloc_ret(PoolPrint((fmt), ##__VA_ARGS__))) -#define xstrdup(str) ((CHAR16 *) assert_alloc_ret(StrDuplicate(str))) +#define xallocate_pool(size) ASSERT_SE_PTR(AllocatePool(size)) +#define xallocate_zero_pool(size) ASSERT_SE_PTR(AllocateZeroPool(size)) +#define xreallocate_pool(p, old_size, new_size) ASSERT_SE_PTR(ReallocatePool((p), (old_size), (new_size))) +#define xpool_print(fmt, ...) ((CHAR16 *) ASSERT_SE_PTR(PoolPrint((fmt), ##__VA_ARGS__))) +#define xstrdup(str) ((CHAR16 *) ASSERT_SE_PTR(StrDuplicate(str))) #define xnew(type, n) xnew_alloc(type, (n), xallocate_pool) #define xnew0(type, n) xnew_alloc(type, (n), xallocate_zero_pool) EFI_STATUS parse_boolean(const CHAR8 *v, BOOLEAN *b); -UINT64 ticks_read(void); -UINT64 ticks_freq(void); -UINT64 time_usec(void); - EFI_STATUS efivar_set(const EFI_GUID *vendor, const CHAR16 *name, const CHAR16 *value, UINT32 flags); EFI_STATUS efivar_set_raw(const EFI_GUID *vendor, const CHAR16 *name, const void *buf, UINTN size, UINT32 flags); EFI_STATUS efivar_set_uint_string(const EFI_GUID *vendor, const CHAR16 *name, UINTN i, UINT32 flags); @@ -71,20 +52,20 @@ CHAR8 *strchra(const CHAR8 *s, CHAR8 c); CHAR16 *xstra_to_path(const CHAR8 *stra); CHAR16 *xstra_to_str(const CHAR8 *stra); -EFI_STATUS file_read(EFI_FILE_HANDLE dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **content, UINTN *content_size); +EFI_STATUS file_read(EFI_FILE *dir, const CHAR16 *name, UINTN off, UINTN size, CHAR8 **content, UINTN *content_size); -static inline void FreePoolp(void *p) { +static inline void free_poolp(void *p) { void *q = *(void**) p; if (!q) return; - FreePool(q); + (void) BS->FreePool(q); } -#define _cleanup_freepool_ _cleanup_(FreePoolp) +#define _cleanup_freepool_ _cleanup_(free_poolp) -static inline void FileHandleClosep(EFI_FILE_HANDLE *handle) { +static inline void file_closep(EFI_FILE **handle) { if (!*handle) return; @@ -117,9 +98,9 @@ void clear_screen(UINTN attr); typedef INTN (*compare_pointer_func_t)(const void *a, const void *b); void sort_pointer_array(void **array, UINTN n_members, compare_pointer_func_t compare); -EFI_STATUS get_file_info_harder(EFI_FILE_HANDLE handle, EFI_FILE_INFO **ret, UINTN *ret_size); +EFI_STATUS get_file_info_harder(EFI_FILE *handle, EFI_FILE_INFO **ret, UINTN *ret_size); -EFI_STATUS readdir_harder(EFI_FILE_HANDLE handle, EFI_FILE_INFO **buffer, UINTN *buffer_size); +EFI_STATUS readdir_harder(EFI_FILE *handle, EFI_FILE_INFO **buffer, UINTN *buffer_size); UINTN strnlena(const CHAR8 *p, UINTN maxlen); CHAR8 *xstrndup8(const CHAR8 *p, UINTN sz); @@ -136,7 +117,7 @@ static inline void strv_freep(CHAR16 ***p) { strv_free(*p); } -EFI_STATUS open_directory(EFI_FILE_HANDLE root_dir, const CHAR16 *path, EFI_FILE_HANDLE *ret); +EFI_STATUS open_directory(EFI_FILE *root_dir, const CHAR16 *path, EFI_FILE **ret); /* Conversion between EFI_PHYSICAL_ADDRESS and pointers is not obvious. The former is always 64bit, even on * 32bit archs. And gcc complains if we cast a pointer to an integer of a different size. Hence let's do the @@ -159,3 +140,19 @@ static inline void *PHYSICAL_ADDRESS_TO_POINTER(EFI_PHYSICAL_ADDRESS addr) { } UINT64 get_os_indications_supported(void); + +#ifdef EFI_DEBUG +void debug_break(void); +extern UINT8 _text, _data; +/* Report the relocated position of text and data sections so that a debugger + * can attach to us. See debug-sd-boot.sh for how this can be done. */ +# define debug_hook(identity) Print(identity L"@0x%x,0x%x\n", &_text, &_data) +#else +# define debug_hook(identity) +#endif + +#if defined(__i386__) || defined(__x86_64__) +void beep(UINTN beep_count); +#else +static inline void beep(UINTN beep_count) {} +#endif diff --git a/src/boot/efi/xbootldr.c b/src/boot/efi/xbootldr.c index 4972877d2..6fa7e49e5 100644 --- a/src/boot/efi/xbootldr.c +++ b/src/boot/efi/xbootldr.c @@ -12,20 +12,17 @@ union GptHeaderBuffer { uint8_t space[CONST_ALIGN_TO(sizeof(EFI_PARTITION_TABLE_HEADER), 512)]; }; -static EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) { - EFI_DEVICE_PATH *parent; - UINTN len; - +static EFI_DEVICE_PATH *path_chop(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) { assert(path); assert(node); - len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path; - parent = (EFI_DEVICE_PATH*) xallocate_pool(len + sizeof(EFI_DEVICE_PATH)); + UINTN len = (UINT8 *) node - (UINT8 *) path; + EFI_DEVICE_PATH *chopped = xallocate_pool(len + END_DEVICE_PATH_LENGTH); - CopyMem(parent, path, len); - CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH)); + CopyMem(chopped, path, len); + SetDevicePathEndNode((EFI_DEVICE_PATH *) ((UINT8 *) chopped + len)); - return parent; + return chopped; } static BOOLEAN verify_gpt(union GptHeaderBuffer *gpt_header_buffer, EFI_LBA lba_expected) { @@ -75,10 +72,7 @@ static EFI_STATUS try_gpt( EFI_BLOCK_IO *block_io, EFI_LBA lba, EFI_LBA *ret_backup_lba, /* May be changed even on error! */ - UINT32 *ret_part_number, - UINT64 *ret_part_start, - UINT64 *ret_part_size, - EFI_GUID *ret_part_uuid) { + HARDDRIVE_DEVICE_PATH *ret_hd) { _cleanup_freepool_ EFI_PARTITION_ENTRY *entries = NULL; union GptHeaderBuffer gpt; @@ -87,10 +81,7 @@ static EFI_STATUS try_gpt( UINTN size; assert(block_io); - assert(ret_part_number); - assert(ret_part_start); - assert(ret_part_size); - assert(ret_part_uuid); + assert(ret_hd); /* Read the GPT header */ err = block_io->ReadBlocks( @@ -142,10 +133,12 @@ static EFI_STATUS try_gpt( if (end < start) /* Bogus? */ continue; - *ret_part_number = i + 1; - *ret_part_start = start; - *ret_part_size = end - start + 1; - CopyMem(ret_part_uuid, &entry->UniquePartitionGUID, sizeof(*ret_part_uuid)); + ret_hd->PartitionNumber = i + 1; + ret_hd->PartitionStart = start; + ret_hd->PartitionSize = end - start + 1; + ret_hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; + ret_hd->SignatureType = SIGNATURE_TYPE_GUID; + CopyMem(ret_hd->Signature, &entry->UniquePartitionGUID, sizeof(ret_hd->Signature)); return EFI_SUCCESS; } @@ -155,95 +148,88 @@ static EFI_STATUS try_gpt( return EFI_NOT_FOUND; } -static EFI_STATUS find_device( - EFI_HANDLE *device, - EFI_DEVICE_PATH **ret_device_path, - UINT32 *ret_part_number, - UINT64 *ret_part_start, - UINT64 *ret_part_size, - EFI_GUID *ret_part_uuid) { - - EFI_DEVICE_PATH *partition_path; +static EFI_STATUS find_device(EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_path) { EFI_STATUS err; assert(device); assert(ret_device_path); - assert(ret_part_number); - assert(ret_part_start); - assert(ret_part_size); - assert(ret_part_uuid); - partition_path = DevicePathFromHandle(device); + EFI_DEVICE_PATH *partition_path = DevicePathFromHandle(device); if (!partition_path) return EFI_NOT_FOUND; + /* Find the (last) partition node itself. */ + EFI_DEVICE_PATH *part_node = NULL; for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) { - _cleanup_freepool_ EFI_DEVICE_PATH *disk_path = NULL; - EFI_HANDLE disk_handle; - EFI_BLOCK_IO *block_io; - EFI_DEVICE_PATH *p; - - /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media - * devices */ - if (DevicePathType(node) != MESSAGING_DEVICE_PATH) + if (DevicePathType(node) != MEDIA_DEVICE_PATH) continue; - /* Determine the device path one level up */ - disk_path = p = path_parent(partition_path, node); - if (!disk_path) + if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) continue; - err = BS->LocateDevicePath(&BlockIoProtocol, &p, &disk_handle); - if (EFI_ERROR(err)) + part_node = node; + } + + if (!part_node) + return EFI_NOT_FOUND; + + /* Chop off the partition part, leaving us with the full path to the disk itself. */ + _cleanup_freepool_ EFI_DEVICE_PATH *disk_path = NULL; + EFI_DEVICE_PATH *p = disk_path = path_chop(partition_path, part_node); + + EFI_HANDLE disk_handle; + EFI_BLOCK_IO *block_io; + err = BS->LocateDevicePath(&BlockIoProtocol, &p, &disk_handle); + if (EFI_ERROR(err)) + return err; + + err = BS->HandleProtocol(disk_handle, &BlockIoProtocol, (void **)&block_io); + if (EFI_ERROR(err)) + return err; + + /* Filter out some block devices early. (We only care about block devices that aren't + * partitions themselves — we look for GPT partition tables to parse after all —, and only + * those which contain a medium and have at least 2 blocks.) */ + if (block_io->Media->LogicalPartition || + !block_io->Media->MediaPresent || + block_io->Media->LastBlock <= 1) + return EFI_NOT_FOUND; + + /* Try several copies of the GPT header, in case one is corrupted */ + EFI_LBA backup_lba = 0; + HARDDRIVE_DEVICE_PATH hd = *((HARDDRIVE_DEVICE_PATH *) part_node); + for (UINTN nr = 0; nr < 3; nr++) { + EFI_LBA lba; + + /* Read the first copy at LBA 1 and then try the backup GPT header pointed + * to by the first header if that one was corrupted. As a last resort, + * try the very last LBA of this block device. */ + if (nr == 0) + lba = 1; + else if (nr == 1 && backup_lba != 0) + lba = backup_lba; + else if (nr == 2 && backup_lba != block_io->Media->LastBlock) + lba = block_io->Media->LastBlock; + else continue; - err = BS->HandleProtocol(disk_handle, &BlockIoProtocol, (void **)&block_io); - if (EFI_ERROR(err)) - continue; - - /* Filter out some block devices early. (We only care about block devices that aren't - * partitions themselves — we look for GPT partition tables to parse after all —, and only - * those which contain a medium and have at least 2 blocks.) */ - if (block_io->Media->LogicalPartition || - !block_io->Media->MediaPresent || - block_io->Media->LastBlock <= 1) - continue; - - /* Try several copies of the GPT header, in case one is corrupted */ - EFI_LBA backup_lba = 0; - for (UINTN nr = 0; nr < 3; nr++) { - EFI_LBA lba; - - /* Read the first copy at LBA 1 and then try the backup GPT header pointed - * to by the first header if that one was corrupted. As a last resort, - * try the very last LBA of this block device. */ - if (nr == 0) - lba = 1; - else if (nr == 1 && backup_lba != 0) - lba = backup_lba; - else if (nr == 2 && backup_lba != block_io->Media->LastBlock) - lba = block_io->Media->LastBlock; - else - continue; - - err = try_gpt( - block_io, lba, - nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */ - ret_part_number, - ret_part_start, - ret_part_size, - ret_part_uuid); - if (!EFI_ERROR(err)) { - *ret_device_path = DuplicateDevicePath(partition_path); - if (!*ret_device_path) - return EFI_OUT_OF_RESOURCES; - return EFI_SUCCESS; - } - + err = try_gpt( + block_io, lba, + nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */ + &hd); + if (EFI_ERROR(err)) { /* GPT was valid but no XBOOT loader partition found. */ if (err == EFI_NOT_FOUND) break; + /* Bad GPT, try next one. */ + continue; } + + /* Patch in the data we found */ + EFI_DEVICE_PATH *xboot_path = ASSERT_SE_PTR(DuplicateDevicePath(partition_path)); + CopyMem((UINT8 *) xboot_path + ((UINT8 *) part_node - (UINT8 *) partition_path), &hd, sizeof(hd)); + *ret_device_path = xboot_path; + return EFI_SUCCESS; } /* No xbootloader partition found */ @@ -252,40 +238,18 @@ static EFI_STATUS find_device( EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir) { _cleanup_freepool_ EFI_DEVICE_PATH *partition_path = NULL; - UINT32 part_number = UINT32_MAX; - UINT64 part_start = UINT64_MAX, part_size = UINT64_MAX; EFI_HANDLE new_device; EFI_FILE *root_dir; - EFI_GUID part_uuid; EFI_STATUS err; assert(device); assert(ret_device); assert(ret_root_dir); - err = find_device(device, &partition_path, &part_number, &part_start, &part_size, &part_uuid); + err = find_device(device, &partition_path); if (EFI_ERROR(err)) return err; - /* Patch in the data we found */ - for (EFI_DEVICE_PATH *node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) { - HARDDRIVE_DEVICE_PATH *hd; - - if (DevicePathType(node) != MEDIA_DEVICE_PATH) - continue; - - if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) - continue; - - hd = (HARDDRIVE_DEVICE_PATH*) node; - hd->PartitionNumber = part_number; - hd->PartitionStart = part_start; - hd->PartitionSize = part_size; - CopyMem(hd->Signature, &part_uuid, sizeof(hd->Signature)); - hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; - hd->SignatureType = SIGNATURE_TYPE_GUID; - } - EFI_DEVICE_PATH *dp = partition_path; err = BS->LocateDevicePath(&BlockIoProtocol, &dp, &new_device); if (EFI_ERROR(err)) diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index c0c9b23ae..0f97015bd 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -20,6 +20,7 @@ #include "json.h" #include "log.h" #include "main-func.h" +#include "os-util.h" #include "pager.h" #include "parse-argument.h" #include "parse-util.h" @@ -31,6 +32,7 @@ #include "terminal-util.h" #include "user-util.h" #include "verbs.h" +#include "version.h" static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; static PagerFlags arg_pager_flags = 0; @@ -141,7 +143,7 @@ static int list_bus_names(int argc, char **argv, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_hashmap_free_ Hashmap *names = NULL; _cleanup_(table_unrefp) Table *table = NULL; - char **i, *k; + char *k; void *v; int r; @@ -502,7 +504,6 @@ static int tree_one(sd_bus *bus, const char *service) { static int tree(int argc, char **argv, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - char **i; int r; /* Do superficial verification of arguments before even opening the bus */ @@ -1206,7 +1207,6 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *message = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char **i; uint32_t flags = 0; const char *unique_name; bool is_monitor = false; @@ -1329,13 +1329,20 @@ static int verb_monitor(int argc, char **argv, void *userdata) { } static int verb_capture(int argc, char **argv, void *userdata) { + _cleanup_free_ char *osname = NULL; + static const char info[] = + "busctl (systemd) " STRINGIFY(PROJECT_VERSION) " (Git " GIT_VERSION ")"; int r; if (isatty(fileno(stdout)) > 0) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Refusing to write message data to console, please redirect output to a file."); - bus_pcap_header(arg_snaplen, stdout); + r = parse_os_release(NULL, "PRETTY_NAME", &osname); + if (r < 0) + log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_INFO, r, + "Failed to read os-release file, ignoring: %m"); + bus_pcap_header(arg_snaplen, osname, info, stdout); r = monitor(argc, argv, message_pcap); if (r < 0) @@ -2115,7 +2122,6 @@ static int emit_signal(int argc, char **argv, void *userdata) { static int get_property(int argc, char **argv, void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - char **i; int r; r = acquire_bus(false, &bus); diff --git a/src/busctl/meson.build b/src/busctl/meson.build index f463436fa..295dc0926 100644 --- a/src/busctl/meson.build +++ b/src/busctl/meson.build @@ -6,7 +6,7 @@ busctl_sources = files( 'busctl.c') tests += [ - [['src/busctl/test-busctl-introspect.c', - 'src/busctl/busctl-introspect.c', - 'src/busctl/busctl-introspect.h']], + [files('test-busctl-introspect.c', + 'busctl-introspect.c', + 'busctl-introspect.h')], ] diff --git a/src/busctl/test-busctl-introspect.c b/src/busctl/test-busctl-introspect.c index 216a9a3c3..d0800d236 100644 --- a/src/busctl/test-busctl-introspect.c +++ b/src/busctl/test-busctl-introspect.c @@ -313,7 +313,7 @@ static int on_path(const char *path, void *userdata) { return 0; } -static void test_introspect_on_path(void) { +TEST(introspect_on_path) { static const XMLIntrospectOps ops = { .on_path = on_path, }; @@ -321,8 +321,6 @@ static void test_introspect_on_path(void) { _cleanup_set_free_ Set *paths = NULL; _cleanup_free_ char **l = NULL; - log_info("/* %s */", __func__); - assert_se(set_put_strdup(&paths, "/") > 0); log_debug("/* parse_xml_introspect(\"/\") */"); @@ -363,10 +361,4 @@ static void test_introspect_on_path(void) { assert_se(strv_equal(l, expected)); } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_DEBUG); - - test_introspect_on_path(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index 775bd84ad..6af95204e 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -201,14 +201,17 @@ static int run(int argc, char *argv[]) { if (arg_names) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *root = NULL; - char **name; STRV_FOREACH(name, arg_names) { int q; if (arg_show_unit != SHOW_UNIT_NONE) { /* Command line arguments are unit names */ - _cleanup_free_ char *cgroup = NULL; + _cleanup_free_ char *cgroup = NULL, *unit_name = NULL; + + r = unit_name_mangle(*name, UNIT_NAME_MANGLE_WARN, &unit_name); + if (r < 0) + return log_error_errno(r, "Failed to mangle unit name: %m"); if (!bus) { /* Connect to the bus only if necessary */ @@ -219,16 +222,16 @@ static int run(int argc, char *argv[]) { return bus_log_connect_error(r, BUS_TRANSPORT_LOCAL); } - q = show_cgroup_get_unit_path_and_warn(bus, *name, &cgroup); + q = show_cgroup_get_unit_path_and_warn(bus, unit_name, &cgroup); if (q < 0) goto failed; if (isempty(cgroup)) { - q = log_warning_errno(SYNTHETIC_ERRNO(ENOENT), "Unit %s not found.", *name); + q = log_warning_errno(SYNTHETIC_ERRNO(ENOENT), "Unit %s not found.", unit_name); goto failed; } - printf("Unit %s (%s):\n", *name, cgroup); + printf("Unit %s (%s):\n", unit_name, cgroup); fflush(stdout); q = show_cgroup_by_path(cgroup, NULL, 0, arg_output_flags); diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index e5ab904c4..b023e7175 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -510,7 +510,6 @@ static int refresh_one( } static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) { - const char *c; int r; FOREACH_STRING(c, SYSTEMD_CGROUP_CONTROLLER, "cpu", "cpuacct", "memory", "io", "blkio", "pids") { diff --git a/src/core/apparmor-setup.c b/src/core/apparmor-setup.c index 304a3e6aa..3426a1035 100644 --- a/src/core/apparmor-setup.c +++ b/src/core/apparmor-setup.c @@ -24,7 +24,6 @@ int mac_apparmor_setup(void) { #if HAVE_APPARMOR _cleanup_(aa_policy_cache_unrefp) aa_policy_cache *policy_cache = NULL; _cleanup_(aa_features_unrefp) aa_features *features = NULL; - const char *current_file; _cleanup_free_ char *current_profile = NULL, *cache_dir_path = NULL; int r; diff --git a/src/core/bpf-devices.c b/src/core/bpf-devices.c index 4d86e8665..f62c6f193 100644 --- a/src/core/bpf-devices.c +++ b/src/core/bpf-devices.c @@ -192,7 +192,7 @@ int bpf_devices_cgroup_init( if (policy == CGROUP_DEVICE_POLICY_AUTO && !allow_list) return 0; - r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &prog); + r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, "sd_devices", &prog); if (r < 0) return log_error_errno(r, "Loading device control BPF program failed: %m"); @@ -306,7 +306,7 @@ int bpf_devices_supported(void) { return supported = 0; } - r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, &program); + r = bpf_program_new(BPF_PROG_TYPE_CGROUP_DEVICE, "sd_devices", &program); if (r < 0) { log_debug_errno(r, "Can't allocate CGROUP DEVICE BPF program, BPF device control is not supported: %m"); return supported = 0; diff --git a/src/core/bpf-firewall.c b/src/core/bpf-firewall.c index 3c1c02e44..258d09dd4 100644 --- a/src/core/bpf-firewall.c +++ b/src/core/bpf-firewall.c @@ -145,6 +145,7 @@ static int add_instructions_for_ip_any( static int bpf_firewall_compile_bpf( Unit *u, + const char *prog_name, bool is_ingress, BPFProgram **ret, bool ip_allow_any, @@ -216,7 +217,7 @@ static int bpf_firewall_compile_bpf( return 0; } - r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p); + r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, prog_name, &p); if (r < 0) return r; @@ -526,9 +527,10 @@ static int bpf_firewall_prepare_accounting_maps(Unit *u, bool enabled, int *fd_i } int bpf_firewall_compile(Unit *u) { + const char *ingress_name = NULL, *egress_name = NULL; + bool ip_allow_any = false, ip_deny_any = false; CGroupContext *cc; int r, supported; - bool ip_allow_any = false, ip_deny_any = false; assert(u); @@ -551,6 +553,13 @@ int bpf_firewall_compile(Unit *u) { return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP), "BPF_F_ALLOW_MULTI is not supported on this manager, not doing BPF firewall on slice units."); + /* If BPF_F_ALLOW_MULTI flag is supported program name is also supported (both were added to v4.15 + * kernel). */ + if (supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI) { + ingress_name = "sd_fw_ingress"; + egress_name = "sd_fw_egress"; + } + /* Note that when we compile a new firewall we first flush out the access maps and the BPF programs themselves, * but we reuse the accounting maps. That way the firewall in effect always maps to the actual * configuration, but we don't flush out the accounting unnecessarily */ @@ -584,11 +593,11 @@ int bpf_firewall_compile(Unit *u) { if (r < 0) return log_unit_error_errno(u, r, "Preparation of eBPF accounting maps failed: %m"); - r = bpf_firewall_compile_bpf(u, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any); + r = bpf_firewall_compile_bpf(u, ingress_name, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any); if (r < 0) return log_unit_error_errno(u, r, "Compilation for ingress BPF program failed: %m"); - r = bpf_firewall_compile_bpf(u, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any); + r = bpf_firewall_compile_bpf(u, egress_name, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any); if (r < 0) return log_unit_error_errno(u, r, "Compilation for egress BPF program failed: %m"); @@ -596,15 +605,13 @@ int bpf_firewall_compile(Unit *u) { } static int load_bpf_progs_from_fs_to_set(Unit *u, char **filter_paths, Set **set) { - char **bpf_fs_path; - set_clear(*set); STRV_FOREACH(bpf_fs_path, filter_paths) { _cleanup_(bpf_program_freep) BPFProgram *prog = NULL; int r; - r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &prog); + r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &prog); if (r < 0) return log_unit_error_errno(u, r, "Can't allocate CGROUP SKB BPF program: %m"); @@ -825,7 +832,8 @@ int bpf_firewall_supported(void) { return supported = BPF_FIREWALL_UNSUPPORTED; } - r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &program); + /* prog_name is NULL since it is supported only starting from v4.15 kernel. */ + r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, NULL, &program); if (r < 0) { bpf_firewall_unsupported_reason = log_debug_errno(r, "Can't allocate CGROUP SKB BPF program, BPF firewalling is not supported: %m"); @@ -882,7 +890,9 @@ int bpf_firewall_supported(void) { /* So now we know that the BPF program is generally available, let's see if BPF_F_ALLOW_MULTI is also supported * (which was added in kernel 4.15). We use a similar logic as before, but this time we use the BPF_PROG_ATTACH * bpf() call and the BPF_F_ALLOW_MULTI flags value. Since the flags are checked early in the system call we'll - * get EINVAL if it's not supported, and EBADF as before if it is available. */ + * get EINVAL if it's not supported, and EBADF as before if it is available. + * Use probe result as the indicator that program name is also supported since they both were + * added in kernel 4.15. */ zero(attr); attr.attach_type = BPF_CGROUP_INET_EGRESS; @@ -917,16 +927,17 @@ void emit_bpf_firewall_warning(Unit *u) { assert(u); assert(u->manager); - if (!warned && !MANAGER_IS_TEST_RUN(u->manager)) { - bool quiet = bpf_firewall_unsupported_reason == -EPERM && detect_container() > 0; + if (warned || MANAGER_IS_TEST_RUN(u->manager)) + return; - log_unit_full_errno(u, quiet ? LOG_DEBUG : LOG_WARNING, bpf_firewall_unsupported_reason, - "unit configures an IP firewall, but %s.\n" - "(This warning is only shown for the first unit using IP firewalling.)", - getuid() != 0 ? "not running as root" : - "the local system does not support BPF/cgroup firewalling"); - warned = true; - } + bool quiet = ERRNO_IS_PRIVILEGE(bpf_firewall_unsupported_reason) && detect_container() > 0; + + log_unit_full_errno(u, quiet ? LOG_DEBUG : LOG_WARNING, bpf_firewall_unsupported_reason, + "unit configures an IP firewall, but %s.\n" + "(This warning is only shown for the first unit using IP firewalling.)", + getuid() != 0 ? "not running as root" : + "the local system does not support BPF/cgroup firewalling"); + warned = true; } void bpf_firewall_close(Unit *u) { diff --git a/src/core/bpf-foreign.c b/src/core/bpf-foreign.c index 8538792b6..7f50f5738 100644 --- a/src/core/bpf-foreign.c +++ b/src/core/bpf-foreign.c @@ -123,7 +123,6 @@ static int bpf_foreign_prepare( int bpf_foreign_install(Unit *u) { _cleanup_free_ char *cgroup_path = NULL; - CGroupBPFForeignProgram *p; CGroupContext *cc; int r; diff --git a/src/core/bpf-socket-bind.c b/src/core/bpf-socket-bind.c index c5176aa48..09f83dc66 100644 --- a/src/core/bpf-socket-bind.c +++ b/src/core/bpf-socket-bind.c @@ -27,7 +27,6 @@ static int update_rules_map( int map_fd, CGroupSocketBindItem *head) { - CGroupSocketBindItem *item; uint32_t i = 0; assert(map_fd >= 0); @@ -58,7 +57,6 @@ static int prepare_socket_bind_bpf( _cleanup_(socket_bind_bpf_freep) struct socket_bind_bpf *obj = NULL; size_t allow_count = 0, deny_count = 0; int allow_map_fd, deny_map_fd, r; - CGroupSocketBindItem *item; assert(ret_obj); @@ -78,8 +76,7 @@ static int prepare_socket_bind_bpf( obj = socket_bind_bpf__open(); if (!obj) - return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, SYNTHETIC_ERRNO(ENOMEM), - "Failed to open BPF object"); + return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "Failed to open BPF object: %m"); if (sym_bpf_map__resize(obj->maps.sd_bind_allow, MAX(allow_count, 1u)) != 0) return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno, diff --git a/src/core/bpf/meson.build b/src/core/bpf/meson.build index c2465a845..849f092fc 100644 --- a/src/core/bpf/meson.build +++ b/src/core/bpf/meson.build @@ -65,17 +65,27 @@ bpf_o_unstripped_cmd += [ '@OUTPUT@' ] -bpf_o_cmd = [ - llvm_strip, - '-g', - '@INPUT@', - '-o', - '@OUTPUT@' -] +if bpftool_strip + bpf_o_cmd = [ + bpftool, + 'gen', + 'object', + '@OUTPUT@', + '@INPUT@' + ] +else + bpf_o_cmd = [ + llvm_strip, + '-g', + '@INPUT@', + '-o', + '@OUTPUT@' + ] +endif skel_h_cmd = [ bpftool, - 'g', - 's', + 'gen', + 'skeleton', '@INPUT@' ] diff --git a/src/core/bpf/restrict_fs/restrict-fs.bpf.c b/src/core/bpf/restrict_fs/restrict-fs.bpf.c index cdc0613a0..99940bedf 100644 --- a/src/core/bpf/restrict_fs/restrict-fs.bpf.c +++ b/src/core/bpf/restrict_fs/restrict-fs.bpf.c @@ -16,7 +16,7 @@ #include struct super_block { - long unsigned int s_magic; + unsigned long int s_magic; } __attribute__((preserve_access_index)); struct inode { diff --git a/src/core/cgroup.c b/src/core/cgroup.c index f58de95a4..cf10f31d3 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -45,12 +45,6 @@ #define CGROUP_CPU_QUOTA_DEFAULT_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) -/* Special values for the bfq.weight attribute */ -#define CGROUP_BFQ_WEIGHT_INVALID UINT64_MAX -#define CGROUP_BFQ_WEIGHT_MIN UINT64_C(1) -#define CGROUP_BFQ_WEIGHT_MAX UINT64_C(1000) -#define CGROUP_BFQ_WEIGHT_DEFAULT UINT64_C(100) - /* Returns the log level to use when cgroup attribute writes fail. When an attribute is missing or we have access * problems we downgrade to LOG_DEBUG. This is supposed to be nice to container managers and kernels which want to mask * out specific attributes from us. */ @@ -420,17 +414,8 @@ static char *format_cgroup_memory_limit_comparison(char *buf, size_t l, Unit *u, void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) { _cleanup_free_ char *disable_controllers_str = NULL, *cpuset_cpus = NULL, *cpuset_mems = NULL, *startup_cpuset_cpus = NULL, *startup_cpuset_mems = NULL; - CGroupIODeviceLimit *il; - CGroupIODeviceWeight *iw; - CGroupIODeviceLatency *l; - CGroupBlockIODeviceBandwidth *b; - CGroupBlockIODeviceWeight *w; - CGroupBPFForeignProgram *p; - CGroupDeviceAllow *a; CGroupContext *c; - CGroupSocketBindItem *bi; struct in_addr_prefix *iaai; - char **path; char cda[FORMAT_CGROUP_DIFF_MAX]; char cdb[FORMAT_CGROUP_DIFF_MAX]; @@ -742,9 +727,44 @@ int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_low); UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(memory_min); +static void unit_set_xattr_graceful(Unit *u, const char *cgroup_path, const char *name, const void *data, size_t size) { + int r; + + assert(u); + assert(name); + + if (!cgroup_path) { + if (!u->cgroup_path) + return; + + cgroup_path = u->cgroup_path; + } + + r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, name, data, size, 0); + if (r < 0) + log_unit_debug_errno(u, r, "Failed to set '%s' xattr on control group %s, ignoring: %m", name, empty_to_root(cgroup_path)); +} + +static void unit_remove_xattr_graceful(Unit *u, const char *cgroup_path, const char *name) { + int r; + + assert(u); + assert(name); + + if (!cgroup_path) { + if (!u->cgroup_path) + return; + + cgroup_path = u->cgroup_path; + } + + r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, name); + if (r < 0 && r != -ENODATA) + log_unit_debug_errno(u, r, "Failed to remove '%s' xattr flag on control group %s, ignoring: %m", name, empty_to_root(cgroup_path)); +} + void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path) { CGroupContext *c; - int r; assert(u); @@ -752,59 +772,49 @@ void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path) { if (!c) return; - if (c->moom_preference == MANAGED_OOM_PREFERENCE_OMIT) { - r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_omit", "1", 1, 0); - if (r < 0) - log_unit_debug_errno(u, r, "Failed to set oomd_omit flag on control group %s, ignoring: %m", empty_to_root(cgroup_path)); - } + if (c->moom_preference == MANAGED_OOM_PREFERENCE_OMIT) + unit_set_xattr_graceful(u, cgroup_path, "user.oomd_omit", "1", 1); - if (c->moom_preference == MANAGED_OOM_PREFERENCE_AVOID) { - r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_avoid", "1", 1, 0); - if (r < 0) - log_unit_debug_errno(u, r, "Failed to set oomd_avoid flag on control group %s, ignoring: %m", empty_to_root(cgroup_path)); - } + if (c->moom_preference == MANAGED_OOM_PREFERENCE_AVOID) + unit_set_xattr_graceful(u, cgroup_path, "user.oomd_avoid", "1", 1); - if (c->moom_preference != MANAGED_OOM_PREFERENCE_AVOID) { - r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_avoid"); - if (r < 0 && r != -ENODATA) - log_unit_debug_errno(u, r, "Failed to remove oomd_avoid flag on control group %s, ignoring: %m", empty_to_root(cgroup_path)); - } + if (c->moom_preference != MANAGED_OOM_PREFERENCE_AVOID) + unit_remove_xattr_graceful(u, cgroup_path, "user.oomd_avoid"); - if (c->moom_preference != MANAGED_OOM_PREFERENCE_OMIT) { - r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, cgroup_path, "user.oomd_omit"); - if (r < 0 && r != -ENODATA) - log_unit_debug_errno(u, r, "Failed to remove oomd_omit flag on control group %s, ignoring: %m", empty_to_root(cgroup_path)); - } + if (c->moom_preference != MANAGED_OOM_PREFERENCE_OMIT) + unit_remove_xattr_graceful(u, cgroup_path, "user.oomd_omit"); } static void cgroup_xattr_apply(Unit *u) { - int r; + bool b; assert(u); if (!MANAGER_IS_SYSTEM(u->manager)) return; - if (!sd_id128_is_null(u->invocation_id)) { - r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, - "trusted.invocation_id", - SD_ID128_TO_STRING(u->invocation_id), 32, - 0); - if (r < 0) - log_unit_debug_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", empty_to_root(u->cgroup_path)); + b = !sd_id128_is_null(u->invocation_id); + FOREACH_STRING(xn, "trusted.invocation_id", "user.invocation_id") { + if (b) + unit_set_xattr_graceful(u, NULL, xn, SD_ID128_TO_STRING(u->invocation_id), 32); + else + unit_remove_xattr_graceful(u, NULL, xn); } - if (unit_cgroup_delegate(u)) { - r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, - "trusted.delegate", - "1", 1, - 0); - if (r < 0) - log_unit_debug_errno(u, r, "Failed to set delegate flag on control group %s, ignoring: %m", empty_to_root(u->cgroup_path)); - } else { - r = cg_remove_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "trusted.delegate"); - if (r < 0 && r != -ENODATA) - log_unit_debug_errno(u, r, "Failed to remove delegate flag on control group %s, ignoring: %m", empty_to_root(u->cgroup_path)); + /* Indicate on the cgroup whether delegation is on, via an xattr. This is best-effort, as old kernels + * didn't support xattrs on cgroups at all. Later they got support for setting 'trusted.*' xattrs, + * and even later 'user.*' xattrs. We started setting this field when 'trusted.*' was added, and + * given this is now pretty much API, let's continue to support that. But also set 'user.*' as well, + * since it is readable by any user, not just CAP_SYS_ADMIN. This hence comes with slightly weaker + * security (as users who got delegated cgroups could turn it off if they like), but this shouldn't + * be a big problem given this communicates delegation state to clients, but the manager never reads + * it. */ + b = unit_cgroup_delegate(u); + FOREACH_STRING(xn, "trusted.delegate", "user.delegate") { + if (b) + unit_set_xattr_graceful(u, NULL, xn, "1", 1); + else + unit_remove_xattr_graceful(u, NULL, xn); } cgroup_oomd_xattr_apply(u, u->cgroup_path); @@ -1053,6 +1063,26 @@ static uint64_t cgroup_weight_io_to_blkio(uint64_t io_weight) { CGROUP_BLKIO_WEIGHT_MIN, CGROUP_BLKIO_WEIGHT_MAX); } +static void set_bfq_weight(Unit *u, const char *controller, uint64_t io_weight) { + char buf[DECIMAL_STR_MAX(uint64_t)+STRLEN("\n")]; + const char *p; + uint64_t bfq_weight; + + /* FIXME: drop this function when distro kernels properly support BFQ through "io.weight" + * See also: https://github.com/systemd/systemd/pull/13335 and + * https://github.com/torvalds/linux/commit/65752aef0a407e1ef17ec78a7fc31ba4e0b360f9. */ + p = strjoina(controller, ".bfq.weight"); + /* Adjust to kernel range is 1..1000, the default is 100. */ + bfq_weight = BFQ_WEIGHT(io_weight); + + xsprintf(buf, "%" PRIu64 "\n", bfq_weight); + + if (set_attribute_and_warn(u, controller, p, buf) >= 0 && io_weight != bfq_weight) + log_unit_debug(u, "%sIOWeight=%" PRIu64 " scaled to %s=%" PRIu64, + streq(controller, "blkio") ? "Block" : "", + io_weight, p, bfq_weight); +} + static void cgroup_apply_io_device_weight(Unit *u, const char *dev_path, uint64_t io_weight) { char buf[DECIMAL_STR_MAX(dev_t)*2+2+DECIMAL_STR_MAX(uint64_t)+1]; dev_t dev; @@ -1179,7 +1209,6 @@ static int cgroup_apply_devices(Unit *u) { _cleanup_(bpf_program_freep) BPFProgram *prog = NULL; const char *path; CGroupContext *c; - CGroupDeviceAllow *a; CGroupDevicePolicy policy; int r; @@ -1264,30 +1293,12 @@ static int cgroup_apply_devices(Unit *u) { return r; } -/* Convert the normal io.weight value to io.bfq.weight */ -#define BFQ_WEIGHT(weight) \ - (weight <= CGROUP_WEIGHT_DEFAULT ? \ - CGROUP_BFQ_WEIGHT_DEFAULT - (CGROUP_WEIGHT_DEFAULT - weight) * (CGROUP_BFQ_WEIGHT_DEFAULT - CGROUP_BFQ_WEIGHT_MIN) / (CGROUP_WEIGHT_DEFAULT - CGROUP_WEIGHT_MIN) : \ - CGROUP_BFQ_WEIGHT_DEFAULT + (weight - CGROUP_WEIGHT_DEFAULT) * (CGROUP_BFQ_WEIGHT_MAX - CGROUP_BFQ_WEIGHT_DEFAULT) / (CGROUP_WEIGHT_MAX - CGROUP_WEIGHT_DEFAULT)) - -assert_cc(BFQ_WEIGHT(1) == 1); -assert_cc(BFQ_WEIGHT(50) == 50); -assert_cc(BFQ_WEIGHT(100) == 100); -assert_cc(BFQ_WEIGHT(500) == 136); -assert_cc(BFQ_WEIGHT(5000) == 545); -assert_cc(BFQ_WEIGHT(10000) == 1000); - static void set_io_weight(Unit *u, uint64_t weight) { char buf[STRLEN("default \n")+DECIMAL_STR_MAX(uint64_t)]; assert(u); - /* FIXME: drop this when distro kernels properly support BFQ through "io.weight" - * See also: https://github.com/systemd/systemd/pull/13335 and - * https://github.com/torvalds/linux/commit/65752aef0a407e1ef17ec78a7fc31ba4e0b360f9. - * The range is 1..1000 apparently, and the default is 100. */ - xsprintf(buf, "%" PRIu64 "\n", BFQ_WEIGHT(weight)); - (void) set_attribute_and_warn(u, "io", "io.bfq.weight", buf); + set_bfq_weight(u, "io", weight); xsprintf(buf, "default %" PRIu64 "\n", weight); (void) set_attribute_and_warn(u, "io", "io.weight", buf); @@ -1298,9 +1309,7 @@ static void set_blkio_weight(Unit *u, uint64_t weight) { assert(u); - /* FIXME: see comment in set_io_weight(). */ - xsprintf(buf, "%" PRIu64 "\n", BFQ_WEIGHT(weight)); - (void) set_attribute_and_warn(u, "blkio", "blkio.bfq.weight", buf); + set_bfq_weight(u, "blkio", weight); xsprintf(buf, "%" PRIu64 "\n", weight); (void) set_attribute_and_warn(u, "blkio", "blkio.weight", buf); @@ -1420,10 +1429,6 @@ static void cgroup_context_apply( set_io_weight(u, weight); if (has_io) { - CGroupIODeviceLatency *latency; - CGroupIODeviceLimit *limit; - CGroupIODeviceWeight *w; - LIST_FOREACH(device_weights, w, c->io_device_weights) cgroup_apply_io_device_weight(u, w->path, w->weight); @@ -1434,9 +1439,6 @@ static void cgroup_context_apply( cgroup_apply_io_device_latency(u, latency->path, latency->target_usec); } else if (has_blockio) { - CGroupBlockIODeviceWeight *w; - CGroupBlockIODeviceBandwidth *b; - LIST_FOREACH(device_weights, w, c->blockio_device_weights) { weight = cgroup_weight_blkio_to_io(w->weight); @@ -1489,9 +1491,7 @@ static void cgroup_context_apply( set_blkio_weight(u, weight); - if (has_io) { - CGroupIODeviceWeight *w; - + if (has_io) LIST_FOREACH(device_weights, w, c->io_device_weights) { weight = cgroup_weight_io_to_blkio(w->weight); @@ -1500,32 +1500,24 @@ static void cgroup_context_apply( cgroup_apply_blkio_device_weight(u, w->path, weight); } - } else if (has_blockio) { - CGroupBlockIODeviceWeight *w; - + else if (has_blockio) LIST_FOREACH(device_weights, w, c->blockio_device_weights) cgroup_apply_blkio_device_weight(u, w->path, w->weight); - } } /* The bandwidth limits are something that make sense to be applied to the host's root but not container * roots, as there we want the container manager to handle it */ if (is_host_root || !is_local_root) { - if (has_io) { - CGroupIODeviceLimit *l; - + if (has_io) LIST_FOREACH(device_limits, l, c->io_device_limits) { log_cgroup_compat(u, "Applying IO{Read|Write}Bandwidth=%" PRIu64 " %" PRIu64 " as BlockIO{Read|Write}BandwidthMax= for %s", l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX], l->path); cgroup_apply_blkio_device_limit(u, l->path, l->limits[CGROUP_IO_RBPS_MAX], l->limits[CGROUP_IO_WBPS_MAX]); } - } else if (has_blockio) { - CGroupBlockIODeviceBandwidth *b; - + else if (has_blockio) LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) cgroup_apply_blkio_device_limit(u, b->path, b->rbps, b->wbps); - } } } @@ -2140,7 +2132,6 @@ static int unit_update_cgroup( bool created, is_root_slice; CGroupMask migrate_mask = 0; _cleanup_free_ char *cgroup_full_path = NULL; - uint64_t cgroup_id = 0; int r; assert(u); @@ -2160,11 +2151,14 @@ static int unit_update_cgroup( created = r; if (cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER) > 0) { + uint64_t cgroup_id = 0; + r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_full_path); if (r == 0) { r = cg_path_get_cgroupid(cgroup_full_path, &cgroup_id); if (r < 0) - log_unit_warning_errno(u, r, "Failed to get cgroup ID on cgroup %s, ignoring: %m", cgroup_full_path); + log_unit_full_errno(u, ERRNO_IS_NOT_SUPPORTED(r) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to get cgroup ID of cgroup %s, ignoring: %m", cgroup_full_path); } else log_unit_warning_errno(u, r, "Failed to get full cgroup path on cgroup %s, ignoring: %m", empty_to_root(u->cgroup_path)); @@ -2175,7 +2169,6 @@ static int unit_update_cgroup( (void) unit_watch_cgroup(u); (void) unit_watch_cgroup_memory(u); - /* For v2 we preserve enabled controllers in delegated units, adjust others, * for v1 we figure out which controller hierarchies need migration. */ if (created || !u->cgroup_realized || !unit_cgroup_delegate(u)) { @@ -2964,6 +2957,10 @@ static int on_cgroup_empty_event(sd_event_source *s, void *userdata) { log_debug_errno(r, "Failed to reenable cgroup empty event source, ignoring: %m"); } + /* Update state based on OOM kills before we notify about cgroup empty event */ + (void) unit_check_oom(u); + (void) unit_check_oomd_kill(u); + unit_add_to_gc_queue(u); if (UNIT_VTABLE(u)->notify_cgroup_empty) @@ -3043,7 +3040,7 @@ int unit_check_oomd_kill(Unit *u) { else if (r == 0) return 0; - r = cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "user.oomd_kill", &value); + r = cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "user.oomd_ooms", &value); if (r < 0 && r != -ENODATA) return r; @@ -3059,11 +3056,25 @@ int unit_check_oomd_kill(Unit *u) { if (!increased) return 0; + n = 0; + value = mfree(value); + r = cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "user.oomd_kill", &value); + if (r >= 0 && !isempty(value)) + (void) safe_atou64(value, &n); + if (n > 0) log_unit_struct(u, LOG_NOTICE, "MESSAGE_ID=" SD_MESSAGE_UNIT_OOMD_KILL_STR, LOG_UNIT_INVOCATION_ID(u), - LOG_UNIT_MESSAGE(u, "systemd-oomd killed %"PRIu64" process(es) in this unit.", n)); + LOG_UNIT_MESSAGE(u, "systemd-oomd killed %"PRIu64" process(es) in this unit.", n), + "N_PROCESSES=%" PRIu64, n); + else + log_unit_struct(u, LOG_NOTICE, + "MESSAGE_ID=" SD_MESSAGE_UNIT_OOMD_KILL_STR, + LOG_UNIT_INVOCATION_ID(u), + LOG_UNIT_MESSAGE(u, "systemd-oomd killed some process(es) in this unit.")); + + unit_notify_cgroup_oom(u, /* ManagedOOM= */ true); return 1; } @@ -3099,8 +3110,7 @@ int unit_check_oom(Unit *u) { LOG_UNIT_INVOCATION_ID(u), LOG_UNIT_MESSAGE(u, "A process of this unit has been killed by the OOM killer.")); - if (UNIT_VTABLE(u)->notify_cgroup_oom) - UNIT_VTABLE(u)->notify_cgroup_oom(u); + unit_notify_cgroup_oom(u, /* ManagedOOM= */ false); return 1; } @@ -3217,7 +3227,6 @@ static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, for (;;) { union inotify_event_buffer buffer; - struct inotify_event *e; ssize_t l; l = read(fd, &buffer, sizeof(buffer)); @@ -3228,7 +3237,7 @@ static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, return log_error_errno(errno, "Failed to read control group inotify events: %m"); } - FOREACH_INOTIFY_EVENT(e, buffer, l) { + FOREACH_INOTIFY_EVENT_WARN(e, buffer, l) { Unit *u; if (e->wd < 0) diff --git a/src/core/core-varlink.c b/src/core/core-varlink.c index a75f9fb66..40cfd0cc7 100644 --- a/src/core/core-varlink.c +++ b/src/core/core-varlink.c @@ -157,9 +157,7 @@ static int build_managed_oom_cgroups_json(Manager *m, JsonVariant **ret) { if (r < 0) return r; - for (size_t i = 0; i < ELEMENTSOF(supported_unit_types); i++) { - Unit *u; - + for (size_t i = 0; i < ELEMENTSOF(supported_unit_types); i++) LIST_FOREACH(units_by_type, u, m->units_by_type[supported_unit_types[i]]) { CGroupContext *c; @@ -188,7 +186,6 @@ static int build_managed_oom_cgroups_json(Manager *m, JsonVariant **ret) { return r; } } - } r = json_build(&v, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("cgroups", JSON_BUILD_VARIANT(arr)))); if (r < 0) diff --git a/src/core/crash-handler.c b/src/core/crash-handler.c new file mode 100644 index 000000000..561b7fc19 --- /dev/null +++ b/src/core/crash-handler.c @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "crash-handler.h" +#include "exit-status.h" +#include "macro.h" +#include "main.h" +#include "missing_syscall.h" +#include "process-util.h" +#include "raw-clone.h" +#include "rlimit-util.h" +#include "signal-util.h" +#include "terminal-util.h" +#include "virt.h" + +_noreturn_ void freeze_or_exit_or_reboot(void) { + + /* If we are running in a container, let's prefer exiting, after all we can propagate an exit code to + * the container manager, and thus inform it that something went wrong. */ + if (detect_container() > 0) { + log_emergency("Exiting PID 1..."); + _exit(EXIT_EXCEPTION); + } + + if (arg_crash_reboot) { + log_notice("Rebooting in 10s..."); + (void) sleep(10); + + log_notice("Rebooting now..."); + (void) reboot(RB_AUTOBOOT); + log_emergency_errno(errno, "Failed to reboot: %m"); + } + + log_emergency("Freezing execution."); + sync(); + freeze(); +} + +_noreturn_ static void crash(int sig, siginfo_t *siginfo, void *context) { + struct sigaction sa; + pid_t pid; + + /* NB: 💣 💣 💣 This is a signal handler, most likely executed in a situation where we have corrupted + * memory. Thus: please avoid any libc memory allocation here, or any functions that internally use + * memory allocation, as we cannot rely on memory allocation still working at this point! (Note that + * memory allocation is not async-signal-safe anyway — see signal-safety(7) for details —, and thus + * is not permissible in signal handlers.) */ + + if (getpid_cached() != 1) + /* Pass this on immediately, if this is not PID 1 */ + (void) raise(sig); + else if (!arg_dump_core) + log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig)); + else { + sa = (struct sigaction) { + .sa_handler = nop_signal_handler, + .sa_flags = SA_NOCLDSTOP|SA_RESTART, + }; + + /* We want to wait for the core process, hence let's enable SIGCHLD */ + (void) sigaction(SIGCHLD, &sa, NULL); + + pid = raw_clone(SIGCHLD); + if (pid < 0) + log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig)); + else if (pid == 0) { + /* Enable default signal handler for core dump */ + + sa = (struct sigaction) { + .sa_handler = SIG_DFL, + }; + (void) sigaction(sig, &sa, NULL); + + /* Don't limit the coredump size */ + (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)); + + /* Just to be sure... */ + (void) chdir("/"); + + /* Raise the signal again */ + pid = raw_getpid(); + (void) kill(pid, sig); /* raise() would kill the parent */ + + assert_not_reached(); + _exit(EXIT_EXCEPTION); + } else { + siginfo_t status; + int r; + + if (siginfo) { + if (siginfo->si_pid == 0) + log_emergency("Caught <%s> from unknown sender process.", signal_to_string(sig)); + else if (siginfo->si_pid == 1) + log_emergency("Caught <%s> from our own process.", signal_to_string(sig)); + else + log_emergency("Caught <%s> from PID "PID_FMT".", signal_to_string(sig), siginfo->si_pid); + } + + /* Order things nicely. */ + r = wait_for_terminate(pid, &status); + if (r < 0) + log_emergency_errno(r, "Caught <%s>, waitpid() failed: %m", signal_to_string(sig)); + else if (status.si_code != CLD_DUMPED) { + const char *s = status.si_code == CLD_EXITED + ? exit_status_to_string(status.si_status, EXIT_STATUS_LIBC) + : signal_to_string(status.si_status); + + log_emergency("Caught <%s>, core dump failed (child "PID_FMT", code=%s, status=%i/%s).", + signal_to_string(sig), + pid, + sigchld_code_to_string(status.si_code), + status.si_status, strna(s)); + } else + log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", + signal_to_string(sig), pid); + } + } + + if (arg_crash_chvt >= 0) + (void) chvt(arg_crash_chvt); + + sa = (struct sigaction) { + .sa_handler = SIG_IGN, + .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART, + }; + + /* Let the kernel reap children for us */ + (void) sigaction(SIGCHLD, &sa, NULL); + + if (arg_crash_shell) { + log_notice("Executing crash shell in 10s..."); + (void) sleep(10); + + pid = raw_clone(SIGCHLD); + if (pid < 0) + log_emergency_errno(errno, "Failed to fork off crash shell: %m"); + else if (pid == 0) { + (void) setsid(); + (void) make_console_stdio(); + (void) rlimit_nofile_safe(); + (void) execle("/bin/sh", "/bin/sh", NULL, environ); + + log_emergency_errno(errno, "execle() failed: %m"); + _exit(EXIT_EXCEPTION); + } else { + log_info("Spawned crash shell as PID "PID_FMT".", pid); + (void) wait_for_terminate(pid, NULL); + } + } + + freeze_or_exit_or_reboot(); +} + +void install_crash_handler(void) { + static const struct sigaction sa = { + .sa_sigaction = crash, + .sa_flags = SA_NODEFER | SA_SIGINFO, /* So that we can raise the signal again from the signal handler */ + }; + int r; + + /* We ignore the return value here, since, we don't mind if we cannot set up a crash handler */ + r = sigaction_many(&sa, SIGNALS_CRASH_HANDLER); + if (r < 0) + log_debug_errno(r, "I had trouble setting up the crash handler, ignoring: %m"); +} diff --git a/src/core/crash-handler.h b/src/core/crash-handler.h new file mode 100644 index 000000000..dc143354d --- /dev/null +++ b/src/core/crash-handler.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "macro.h" + +_noreturn_ void freeze_or_exit_or_reboot(void); +void install_crash_handler(void); diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index f0d8759e8..9a31355a4 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -112,7 +112,6 @@ static int property_get_io_device_weight( sd_bus_error *error) { CGroupContext *c = userdata; - CGroupIODeviceWeight *w; int r; assert(bus); @@ -142,7 +141,6 @@ static int property_get_io_device_limits( sd_bus_error *error) { CGroupContext *c = userdata; - CGroupIODeviceLimit *l; int r; assert(bus); @@ -178,7 +176,6 @@ static int property_get_io_device_latency( sd_bus_error *error) { CGroupContext *c = userdata; - CGroupIODeviceLatency *l; int r; assert(bus); @@ -208,7 +205,6 @@ static int property_get_blockio_device_weight( sd_bus_error *error) { CGroupContext *c = userdata; - CGroupBlockIODeviceWeight *w; int r; assert(bus); @@ -238,7 +234,6 @@ static int property_get_blockio_device_bandwidths( sd_bus_error *error) { CGroupContext *c = userdata; - CGroupBlockIODeviceBandwidth *b; int r; assert(bus); @@ -278,7 +273,6 @@ static int property_get_device_allow( sd_bus_error *error) { CGroupContext *c = userdata; - CGroupDeviceAllow *a; int r; assert(bus); @@ -364,7 +358,6 @@ static int property_get_bpf_foreign_program( void *userdata, sd_bus_error *error) { CGroupContext *c = userdata; - CGroupBPFForeignProgram *p; int r; r = sd_bus_message_open_container(reply, 'a', "(ss)"); @@ -390,7 +383,8 @@ static int property_get_socket_bind( sd_bus_message *reply, void *userdata, sd_bus_error *error) { - CGroupSocketBindItem **items = userdata, *i; + + CGroupSocketBindItem **items = userdata; int r; assert(items); @@ -640,7 +634,6 @@ static int bus_cgroup_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; - char **entry; size_t size = 0; if (n == 0) @@ -720,7 +713,6 @@ static int bus_cgroup_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; - CGroupBPFForeignProgram *fp; size_t size = 0; if (n == 0) @@ -1228,14 +1220,13 @@ int bus_cgroup_set_property( return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - CGroupIODeviceLimit *a = NULL, *b; + CGroupIODeviceLimit *a = NULL; - LIST_FOREACH(device_limits, b, c->io_device_limits) { + LIST_FOREACH(device_limits, b, c->io_device_limits) if (path_equal(path, b->path)) { a = b; break; } - } if (!a) { CGroupIOLimitType type; @@ -1269,15 +1260,13 @@ int bus_cgroup_set_property( return r; if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - CGroupIODeviceLimit *a; _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; size_t size = 0; - if (n == 0) { + if (n == 0) LIST_FOREACH(device_limits, a, c->io_device_limits) a->limits[iol_type] = cgroup_io_limit_defaults[iol_type]; - } unit_invalidate_cgroup(u, CGROUP_MASK_IO); @@ -1287,8 +1276,8 @@ int bus_cgroup_set_property( fprintf(f, "%s=\n", name); LIST_FOREACH(device_limits, a, c->io_device_limits) - if (a->limits[iol_type] != cgroup_io_limit_defaults[iol_type]) - fprintf(f, "%s=%s %" PRIu64 "\n", name, a->path, a->limits[iol_type]); + if (a->limits[iol_type] != cgroup_io_limit_defaults[iol_type]) + fprintf(f, "%s=%s %" PRIu64 "\n", name, a->path, a->limits[iol_type]); r = fflush_and_check(f); if (r < 0) @@ -1316,14 +1305,13 @@ int bus_cgroup_set_property( return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "IODeviceWeight= value out of range"); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - CGroupIODeviceWeight *a = NULL, *b; + CGroupIODeviceWeight *a = NULL; - LIST_FOREACH(device_weights, b, c->io_device_weights) { + LIST_FOREACH(device_weights, b, c->io_device_weights) if (path_equal(b->path, path)) { a = b; break; } - } if (!a) { a = new0(CGroupIODeviceWeight, 1); @@ -1351,13 +1339,11 @@ int bus_cgroup_set_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; - CGroupIODeviceWeight *a; size_t size = 0; - if (n == 0) { + if (n == 0) while (c->io_device_weights) cgroup_context_free_io_device_weight(c, c->io_device_weights); - } unit_invalidate_cgroup(u, CGROUP_MASK_IO); @@ -1392,14 +1378,13 @@ int bus_cgroup_set_property( return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - CGroupIODeviceLatency *a = NULL, *b; + CGroupIODeviceLatency *a = NULL; - LIST_FOREACH(device_latencies, b, c->io_device_latencies) { + LIST_FOREACH(device_latencies, b, c->io_device_latencies) if (path_equal(b->path, path)) { a = b; break; } - } if (!a) { a = new0(CGroupIODeviceLatency, 1); @@ -1427,13 +1412,11 @@ int bus_cgroup_set_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; - CGroupIODeviceLatency *a; size_t size = 0; - if (n == 0) { + if (n == 0) while (c->io_device_latencies) cgroup_context_free_io_device_latency(c, c->io_device_latencies); - } unit_invalidate_cgroup(u, CGROUP_MASK_IO); @@ -1473,14 +1456,13 @@ int bus_cgroup_set_property( return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path '%s' specified in %s= is not normalized.", name, path); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - CGroupBlockIODeviceBandwidth *a = NULL, *b; + CGroupBlockIODeviceBandwidth *a = NULL; - LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { + LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) if (path_equal(path, b->path)) { a = b; break; } - } if (!a) { a = new0(CGroupBlockIODeviceBandwidth, 1); @@ -1514,19 +1496,17 @@ int bus_cgroup_set_property( return r; if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - CGroupBlockIODeviceBandwidth *a; _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; size_t size = 0; - if (n == 0) { + if (n == 0) LIST_FOREACH(device_bandwidths, a, c->blockio_device_bandwidths) { if (read) a->rbps = CGROUP_LIMIT_MAX; else a->wbps = CGROUP_LIMIT_MAX; } - } unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); @@ -1573,14 +1553,13 @@ int bus_cgroup_set_property( return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "BlockIODeviceWeight= out of range"); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - CGroupBlockIODeviceWeight *a = NULL, *b; + CGroupBlockIODeviceWeight *a = NULL; - LIST_FOREACH(device_weights, b, c->blockio_device_weights) { + LIST_FOREACH(device_weights, b, c->blockio_device_weights) if (path_equal(b->path, path)) { a = b; break; } - } if (!a) { a = new0(CGroupBlockIODeviceWeight, 1); @@ -1608,13 +1587,11 @@ int bus_cgroup_set_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; - CGroupBlockIODeviceWeight *a; size_t size = 0; - if (n == 0) { + if (n == 0) while (c->blockio_device_weights) cgroup_context_free_blockio_device_weight(c, c->blockio_device_weights); - } unit_invalidate_cgroup(u, CGROUP_MASK_BLKIO); @@ -1674,14 +1651,13 @@ int bus_cgroup_set_property( return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "DeviceAllow= requires combination of rwm flags"); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - CGroupDeviceAllow *a = NULL, *b; + CGroupDeviceAllow *a = NULL; - LIST_FOREACH(device_allow, b, c->device_allow) { + LIST_FOREACH(device_allow, b, c->device_allow) if (path_equal(b->path, path)) { a = b; break; } - } if (!a) { a = new0(CGroupDeviceAllow, 1); @@ -1714,13 +1690,11 @@ int bus_cgroup_set_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; - CGroupDeviceAllow *a; size_t size = 0; - if (n == 0) { + if (n == 0) while (c->device_allow) cgroup_context_free_device_allow(c, c->device_allow); - } unit_invalidate_cgroup(u, CGROUP_MASK_DEVICES); @@ -1995,7 +1969,6 @@ int bus_cgroup_set_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; - CGroupSocketBindItem *item; size_t size = 0; if (n == 0) @@ -2050,7 +2023,6 @@ int bus_cgroup_set_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; - char **s; if (strv_isempty(l)) { c->restrict_network_interfaces_is_allow_list = false; diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 5c499e5d0..65eec8f6b 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -74,7 +74,6 @@ static int property_get_environment_files( sd_bus_error *error) { ExecContext *c = userdata; - char **j; int r; assert(bus); @@ -960,7 +959,6 @@ static int property_get_root_image_options( sd_bus_error *error) { ExecContext *c = userdata; - MountOptions *m; int r; assert(bus); @@ -1005,8 +1003,6 @@ static int property_get_mount_images( return r; for (size_t i = 0; i < c->n_mount_images; i++) { - MountOptions *m; - r = sd_bus_message_open_container(reply, SD_BUS_TYPE_STRUCT, "ssba(ss)"); if (r < 0) return r; @@ -1060,8 +1056,6 @@ static int property_get_extension_images( return r; for (size_t i = 0; i < c->n_extension_images; i++) { - MountOptions *m; - r = sd_bus_message_open_container(reply, SD_BUS_TYPE_STRUCT, "sba(ss)"); if (r < 0) return r; @@ -1143,15 +1137,12 @@ static int bus_property_get_exec_dir_symlink( if (r < 0) return r; - for (size_t i = 0; i < d->n_items; i++) { - char **dst; - + for (size_t i = 0; i < d->n_items; i++) STRV_FOREACH(dst, d->items[i].symlinks) { r = sd_bus_message_append(reply, "(sst)", d->items[i].path, *dst, 0 /* flags, unused for now */); if (r < 0) return r; } - } return sd_bus_message_close_container(reply); } @@ -1204,6 +1195,7 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("RootHashSignature", "ay", property_get_root_hash_sig, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootHashSignaturePath", "s", NULL, offsetof(ExecContext, root_hash_sig_path), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RootVerity", "s", NULL, offsetof(ExecContext, root_verity), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ExtensionDirectories", "as", NULL, offsetof(ExecContext, extension_directories), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("ExtensionImages", "a(sba(ss))", property_get_extension_images, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MountImages", "a(ssba(ss))", property_get_mount_images, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("OOMScoreAdjust", "i", property_get_oom_score_adjust, 0, SD_BUS_VTABLE_PROPERTY_CONST), @@ -1448,7 +1440,7 @@ int bus_property_get_exec_command_list( void *userdata, sd_bus_error *ret_error) { - ExecCommand *c = *(ExecCommand**) userdata; + ExecCommand *exec_command = *(ExecCommand**) userdata; int r; assert(bus); @@ -1458,7 +1450,7 @@ int bus_property_get_exec_command_list( if (r < 0) return r; - LIST_FOREACH(command, c, c) { + LIST_FOREACH(command, c, exec_command) { r = append_exec_command(reply, c); if (r < 0) return r; @@ -1476,7 +1468,7 @@ int bus_property_get_exec_ex_command_list( void *userdata, sd_bus_error *ret_error) { - ExecCommand *c, *exec_command = *(ExecCommand**) userdata; + ExecCommand *exec_command = *(ExecCommand**) userdata; int r; assert(bus); @@ -1586,7 +1578,6 @@ int bus_set_transient_exec_command( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *buf = NULL; _cleanup_fclose_ FILE *f = NULL; - ExecCommand *c; size_t size = 0; if (n == 0) @@ -2021,7 +2012,6 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; FilesystemParseFlags invert_flag = allow_list ? 0 : FILESYSTEM_PARSE_INVERT; - char **s; if (strv_isempty(l)) { c->restrict_filesystems_allow_list = false; @@ -2067,7 +2057,6 @@ int bus_exec_context_set_transient_property( if (streq(name, "SupplementaryGroups")) { _cleanup_strv_free_ char **l = NULL; - char **p; r = sd_bus_message_read_strv(message, &l); if (r < 0) @@ -2432,7 +2421,6 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; SeccompParseFlags invert_flag = allow_list ? 0 : SECCOMP_PARSE_INVERT; - char **s; if (strv_isempty(l)) { c->syscall_allow_list = false; @@ -2517,7 +2505,6 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; SeccompParseFlags invert_flag = allow_list ? 0 : SECCOMP_PARSE_INVERT; - char **s; if (strv_isempty(l)) { c->syscall_log_allow_list = false; @@ -2569,9 +2556,7 @@ int bus_exec_context_set_transient_property( if (strv_isempty(l)) c->syscall_archs = set_free(c->syscall_archs); - else { - char **s; - + else STRV_FOREACH(s, l) { uint32_t a; @@ -2584,8 +2569,6 @@ int bus_exec_context_set_transient_property( return r; } - } - joined = strv_join(l, " "); if (!joined) return -ENOMEM; @@ -2617,7 +2600,6 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; - char **s; if (strv_isempty(l)) { c->address_families_allow_list = allow_list; @@ -3146,7 +3128,6 @@ int bus_exec_context_set_transient_property( _cleanup_fclose_ FILE *f = NULL; _cleanup_strv_free_ char **l = NULL; size_t size = 0; - char **i; r = sd_bus_message_enter_container(message, 'a', "(sb)"); if (r < 0) @@ -3261,10 +3242,10 @@ int bus_exec_context_set_transient_property( return 1; } else if (STR_IN_SET(name, "ReadWriteDirectories", "ReadOnlyDirectories", "InaccessibleDirectories", - "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths", "ExecPaths", "NoExecPaths")) { + "ReadWritePaths", "ReadOnlyPaths", "InaccessiblePaths", "ExecPaths", "NoExecPaths", + "ExtensionDirectories")) { _cleanup_strv_free_ char **l = NULL; char ***dirs; - char **p; r = sd_bus_message_read_strv(message, &l); if (r < 0) @@ -3291,6 +3272,8 @@ int bus_exec_context_set_transient_property( dirs = &c->exec_paths; else if (streq(name, "NoExecPaths")) dirs = &c->no_exec_paths; + else if (streq(name, "ExtensionDirectories")) + dirs = &c->extension_directories; else /* "InaccessiblePaths" */ dirs = &c->inaccessible_paths; @@ -3316,16 +3299,15 @@ int bus_exec_context_set_transient_property( } else if (streq(name, "ExecSearchPath")) { _cleanup_strv_free_ char **l = NULL; - char **p; r = sd_bus_message_read_strv(message, &l); if (r < 0) return r; - STRV_FOREACH(p, l) { + STRV_FOREACH(p, l) if (!path_is_absolute(*p) || !path_is_normalized(*p) || strchr(*p, ':')) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid %s", name); - } + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (strv_isempty(l)) { c->exec_search_path = strv_free(c->exec_search_path); @@ -3346,7 +3328,6 @@ int bus_exec_context_set_transient_property( } else if (STR_IN_SET(name, "RuntimeDirectory", "StateDirectory", "CacheDirectory", "LogsDirectory", "ConfigurationDirectory")) { _cleanup_strv_free_ char **l = NULL; - char **p; r = sd_bus_message_read_strv(message, &l); if (r < 0) @@ -3375,7 +3356,6 @@ int bus_exec_context_set_transient_property( unit_write_settingf(u, flags, name, "%s=", name); } else { _cleanup_free_ char *joined = NULL; - char **source; STRV_FOREACH(source, l) { r = exec_directory_add(&d->items, &d->n_items, *source, NULL); diff --git a/src/core/dbus-job.c b/src/core/dbus-job.c index 3334b977b..de474e6d4 100644 --- a/src/core/dbus-job.c +++ b/src/core/dbus-job.c @@ -121,16 +121,14 @@ const sd_bus_vtable bus_job_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_METHOD("Cancel", NULL, NULL, bus_job_method_cancel, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD_WITH_NAMES("GetAfter", - NULL,, - "a(usssoo)", - SD_BUS_PARAM(jobs), + SD_BUS_METHOD_WITH_ARGS("GetAfter", + SD_BUS_NO_ARGS, + SD_BUS_RESULT("a(usssoo)", jobs), bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD_WITH_NAMES("GetBefore", - NULL,, - "a(usssoo)", - SD_BUS_PARAM(jobs), + SD_BUS_METHOD_WITH_ARGS("GetBefore", + SD_BUS_NO_ARGS, + SD_BUS_RESULT("a(usssoo)", jobs), bus_job_method_get_waiting_jobs, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 9b64a8074..572c59dda 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -265,6 +265,42 @@ static int property_get_runtime_watchdog( return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_RUNTIME)); } +static int property_get_pretimeout_watchdog( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(m); + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "t", manager_get_watchdog(m, WATCHDOG_PRETIMEOUT)); +} + +static int property_get_pretimeout_watchdog_governor( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + + assert(m); + assert(bus); + assert(reply); + + return sd_bus_message_append(reply, "s", m->watchdog_pretimeout_governor); +} + static int property_get_reboot_watchdog( sd_bus *bus, const char *path, @@ -314,7 +350,8 @@ static int property_set_watchdog(Manager *m, WatchdogType type, sd_bus_message * if (r < 0) return r; - return manager_override_watchdog(m, type, timeout); + manager_override_watchdog(m, type, timeout); + return 0; } static int property_set_runtime_watchdog( @@ -329,6 +366,42 @@ static int property_set_runtime_watchdog( return property_set_watchdog(userdata, WATCHDOG_RUNTIME, value); } +static int property_set_pretimeout_watchdog( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + return property_set_watchdog(userdata, WATCHDOG_PRETIMEOUT, value); +} + +static int property_set_pretimeout_watchdog_governor( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *value, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + char *governor; + int r; + + assert(m); + + r = sd_bus_message_read(value, "s", &governor); + if (r < 0) + return r; + if (!string_is_safe(governor)) + return -EINVAL; + + return manager_override_watchdog_pretimeout_governor(m, governor); +} + static int property_set_reboot_watchdog( sd_bus *bus, const char *path, @@ -817,7 +890,6 @@ static int method_list_units_by_names(sd_bus_message *message, void *userdata, s _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; Manager *m = userdata; int r; - char **unit; _cleanup_strv_free_ char **units = NULL; assert(message); @@ -2216,7 +2288,7 @@ fail: static int method_enable_unit_files_generic( sd_bus_message *message, Manager *m, - int (*call)(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, size_t *n_changes), + int (*call)(LookupScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, size_t *n_changes), bool carries_install_info, sd_bus_error *error) { @@ -2280,7 +2352,7 @@ static int method_link_unit_files(sd_bus_message *message, void *userdata, sd_bu return method_enable_unit_files_generic(message, userdata, unit_file_link, false, error); } -static int unit_file_preset_without_mode(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes) { +static int unit_file_preset_without_mode(LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes) { return unit_file_preset(scope, flags, root_dir, files, UNIT_FILE_PRESET_FULL, changes, n_changes); } @@ -2340,7 +2412,7 @@ static int method_preset_unit_files_with_mode(sd_bus_message *message, void *use static int method_disable_unit_files_generic( sd_bus_message *message, Manager *m, - int (*call)(UnitFileScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, size_t *n_changes), + int (*call)(LookupScope scope, UnitFileFlags flags, const char *root_dir, char *files[], UnitFileChange **changes, size_t *n_changes), sd_bus_error *error) { _cleanup_strv_free_ char **l = NULL; @@ -2566,7 +2638,7 @@ static int method_get_unit_file_links(sd_bus_message *message, void *userdata, s flags = UNIT_FILE_DRY_RUN | (runtime ? UNIT_FILE_RUNTIME : 0); - r = unit_file_disable(UNIT_FILE_SYSTEM, flags, NULL, p, &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, flags, NULL, p, &changes, &n_changes); if (r < 0) return log_error_errno(r, "Failed to get file links for %s: %m", name); @@ -2637,6 +2709,16 @@ static int method_set_show_status(sd_bus_message *message, void *userdata, sd_bu assert(m); assert(message); + r = mac_selinux_access_check(message, "reload", error); + if (r < 0) + return r; + + r = bus_verify_set_environment_async(m, message, error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + r = sd_bus_message_read(message, "s", &t); if (r < 0) return r; @@ -2695,6 +2777,8 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PROPERTY("DefaultStandardOutput", "s", bus_property_get_exec_output, offsetof(Manager, default_std_output), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("DefaultStandardError", "s", bus_property_get_exec_output, offsetof(Manager, default_std_error), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogUSec", "t", property_get_runtime_watchdog, property_set_runtime_watchdog, 0, 0), + SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogPreUSec", "t", property_get_pretimeout_watchdog, property_set_pretimeout_watchdog, 0, 0), + SD_BUS_WRITABLE_PROPERTY("RuntimeWatchdogPreGovernor", "s", property_get_pretimeout_watchdog_governor, property_set_pretimeout_watchdog_governor, 0, 0), SD_BUS_WRITABLE_PROPERTY("RebootWatchdogUSec", "t", property_get_reboot_watchdog, property_set_reboot_watchdog, 0, 0), /* The following item is an obsolete alias */ SD_BUS_WRITABLE_PROPERTY("ShutdownWatchdogUSec", "t", property_get_reboot_watchdog, property_set_reboot_watchdog, 0, SD_BUS_VTABLE_HIDDEN), @@ -3025,7 +3109,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_PARAM(mode), NULL,, method_set_show_status, - SD_BUS_VTABLE_CAPABILITY(CAP_SYS_ADMIN)), + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_NAMES("ListUnits", NULL,, "a(ssssssouso)", diff --git a/src/core/dbus-path.c b/src/core/dbus-path.c index f143ad5d4..09b353ba3 100644 --- a/src/core/dbus-path.c +++ b/src/core/dbus-path.c @@ -22,7 +22,6 @@ static int property_get_paths( sd_bus_error *error) { Path *p = userdata; - PathSpec *k; int r; assert(bus); diff --git a/src/core/dbus-socket.c b/src/core/dbus-socket.c index a3b1e0442..d1e0509e5 100644 --- a/src/core/dbus-socket.c +++ b/src/core/dbus-socket.c @@ -32,7 +32,6 @@ static int property_get_listen( sd_bus_error *error) { Socket *s = SOCKET(userdata); - SocketPort *p; int r; assert(bus); @@ -326,16 +325,14 @@ static int bus_socket_set_transient_property( if (streq(name, "Symlinks")) { _cleanup_strv_free_ char **l = NULL; - char **p; r = sd_bus_message_read_strv(message, &l); if (r < 0) return r; - STRV_FOREACH(p, l) { + STRV_FOREACH(p, l) if (!path_is_absolute(*p)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Symlink path is not absolute: %s", *p); - } if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (strv_isempty(l)) { diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index 9d823279d..8110fb1fb 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -20,7 +20,6 @@ static int property_get_monotonic_timers( sd_bus_error *error) { Timer *t = userdata; - TimerValue *v; int r; assert(bus); @@ -69,7 +68,6 @@ static int property_get_calendar_timers( sd_bus_error *error) { Timer *t = userdata; - TimerValue *v; int r; assert(bus); diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index a7aac798c..9c4e0fee5 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -273,7 +273,7 @@ static int property_get_conditions( sd_bus_error *error) { const char *(*to_string)(ConditionType type) = NULL; - Condition **list = userdata, *c; + Condition **list = userdata; int r; assert(bus); @@ -736,7 +736,7 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error r = unit_clean(u, mask); if (r == -EOPNOTSUPP) - return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not supporting cleaning.", u->id); + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not support cleaning.", u->id); if (r == -EUNATCH) return sd_bus_error_set(error, BUS_ERROR_NOTHING_TO_CLEAN, "No matching resources found."); if (r == -EBUSY) @@ -1584,6 +1584,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_VTABLE_START(0), SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0), SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0), + SD_BUS_PROPERTY("ControlGroupId", "t", NULL, offsetof(Unit, cgroup_id), 0), SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), SD_BUS_PROPERTY("MemoryAvailable", "t", property_get_available_memory, 0, 0), SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), @@ -2241,16 +2242,14 @@ static int bus_unit_set_transient_property( if (streq(name, "Documentation")) { _cleanup_strv_free_ char **l = NULL; - char **p; r = sd_bus_message_read_strv(message, &l); if (r < 0) return r; - STRV_FOREACH(p, l) { + STRV_FOREACH(p, l) if (!documentation_url_is_valid(*p)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid URL in %s: %s", name, *p); - } if (!UNIT_WRITE_FLAGS_NOOP(flags)) { if (strv_isempty(l)) { @@ -2306,7 +2305,6 @@ static int bus_unit_set_transient_property( } else if (streq(name, "RequiresMountsFor")) { _cleanup_strv_free_ char **l = NULL; - char **p; r = sd_bus_message_read_strv(message, &l); if (r < 0) diff --git a/src/core/dbus.c b/src/core/dbus.c index 2c5bda58f..073675cee 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -925,14 +925,18 @@ int bus_init_private(Manager *m) { r = sockaddr_un_set_path(&sa.un, "/run/systemd/private"); } else { - const char *e, *joined; + _cleanup_free_ char *joined = NULL; + const char *e; e = secure_getenv("XDG_RUNTIME_DIR"); if (!e) return log_error_errno(SYNTHETIC_ERRNO(EHOSTDOWN), "XDG_RUNTIME_DIR is not set, refusing."); - joined = strjoina(e, "/systemd/private"); + joined = path_join(e, "/systemd/private"); + if (!joined) + return log_oom(); + r = sockaddr_un_set_path(&sa.un, joined); } if (r < 0) diff --git a/src/core/device.c b/src/core/device.c index 43f49573b..21d1b50c4 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -307,13 +307,9 @@ static void device_dump(Unit *u, FILE *f, const char *prefix) { prefix, strna(d->sysfs), prefix, strna(s)); - if (!strv_isempty(d->wants_property)) { - char **i; - - STRV_FOREACH(i, d->wants_property) - fprintf(f, "%sudev SYSTEMD_WANTS: %s\n", - prefix, *i); - } + STRV_FOREACH(i, d->wants_property) + fprintf(f, "%sudev SYSTEMD_WANTS: %s\n", + prefix, *i); } _pure_ static UnitActiveState device_active_state(Unit *u) { @@ -419,9 +415,7 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) { k = NULL; } - if (d->state != DEVICE_DEAD) { - char **i; - + if (d->state != DEVICE_DEAD) /* So here's a special hack, to compensate for the fact that the udev database's reload cycles are not * synchronized with our own reload cycles: when we detect that the SYSTEMD_WANTS property of a device * changes while the device unit is already up, let's manually trigger any new units listed in it not @@ -431,7 +425,6 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) { * * We do this only if the device has been up already when we parse this, as otherwise the usual * dependency logic that is run from the dead → plugged transition will trigger these deps. */ - STRV_FOREACH(i, added) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; @@ -442,7 +435,6 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) { if (r < 0) log_unit_warning_errno(u, r, "Failed to enqueue SYSTEMD_WANTS= job, ignoring: %s", bus_error_message(&error, r)); } - } return strv_free_and_replace(d->wants_property, added); } @@ -710,7 +702,7 @@ static void device_update_found_one(Device *d, DeviceFound found, DeviceFound ma } static void device_update_found_by_sysfs(Manager *m, const char *sysfs, DeviceFound found, DeviceFound mask) { - Device *d, *l, *n; + Device *l; assert(m); assert(sysfs); @@ -719,7 +711,7 @@ static void device_update_found_by_sysfs(Manager *m, const char *sysfs, DeviceFo return; l = hashmap_get(m->devices_by_sysfs, sysfs); - LIST_FOREACH_SAFE(same_sysfs, d, n, l) + LIST_FOREACH(same_sysfs, d, l) device_update_found_one(d, found, mask); } @@ -765,7 +757,7 @@ static bool device_is_ready(sd_device *dev) { static Unit *device_following(Unit *u) { Device *d = DEVICE(u); - Device *other, *first = NULL; + Device *first = NULL; assert(d); @@ -788,7 +780,7 @@ static Unit *device_following(Unit *u) { } static int device_following_set(Unit *u, Set **_set) { - Device *d = DEVICE(u), *other; + Device *d = DEVICE(u); _cleanup_set_free_ Set *set = NULL; int r; @@ -898,14 +890,14 @@ fail: } static void device_propagate_reload_by_sysfs(Manager *m, const char *sysfs) { - Device *d, *l, *n; + Device *l; int r; assert(m); assert(sysfs); l = hashmap_get(m->devices_by_sysfs, sysfs); - LIST_FOREACH_SAFE(same_sysfs, d, n, l) { + LIST_FOREACH(same_sysfs, d, l) { if (d->state == DEVICE_DEAD) continue; diff --git a/src/core/efi-random.c b/src/core/efi-random.c index a0b89d137..e8d8ccd11 100644 --- a/src/core/efi-random.c +++ b/src/core/efi-random.c @@ -20,24 +20,23 @@ * is suitably validated. */ static void lock_down_efi_variables(void) { - const char *p; int r; /* Paranoia: let's restrict access modes of these a bit, so that unprivileged users can't use them to * identify the system or gain too much insight into what we might have credited to the entropy * pool. */ - FOREACH_STRING(p, + FOREACH_STRING(path, EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed)), EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken))) { - r = chattr_path(p, 0, FS_IMMUTABLE_FL, NULL); + r = chattr_path(path, 0, FS_IMMUTABLE_FL, NULL); if (r == -ENOENT) continue; if (r < 0) - log_warning_errno(r, "Failed to drop FS_IMMUTABLE_FL from %s, ignoring: %m", p); + log_warning_errno(r, "Failed to drop FS_IMMUTABLE_FL from %s, ignoring: %m", path); - if (chmod(p, 0600) < 0) - log_warning_errno(errno, "Failed to reduce access mode of %s, ignoring: %m", p); + if (chmod(path, 0600) < 0) + log_warning_errno(errno, "Failed to reduce access mode of %s, ignoring: %m", path); } } diff --git a/src/core/execute.c b/src/core/execute.c index 0b20d386d..3cd63846b 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -80,6 +80,7 @@ #include "path-util.h" #include "process-util.h" #include "random-util.h" +#include "recurse-dir.h" #include "rlimit-util.h" #include "rm-rf.h" #if HAVE_SECCOMP @@ -994,7 +995,6 @@ static int get_fixed_group(const ExecContext *c, const char **group, gid_t *gid) static int get_supplementary_groups(const ExecContext *c, const char *user, const char *group, gid_t gid, gid_t **supplementary_gids, int *ngids) { - char **i; int r, k = 0; int ngroups_max; bool keep_groups = false; @@ -1186,7 +1186,6 @@ static int setup_pam( pam_handle_t *handle = NULL; sigset_t old_ss; int pam_code = PAM_SUCCESS, r; - char **nv; bool close_session = false; pid_t pam_pid = 0, parent_pid; int flags = 0; @@ -2004,7 +2003,6 @@ static int build_environment( static int build_pass_environment(const ExecContext *c, char ***ret) { _cleanup_strv_free_ char **pass_env = NULL; size_t n_env = 0; - char **i; STRV_FOREACH(i, c->pass_environment) { _cleanup_free_ char *x = NULL; @@ -2058,6 +2056,9 @@ bool exec_needs_mount_namespace( if (context->n_extension_images > 0) return true; + if (!strv_isempty(context->extension_directories)) + return true; + if (!IN_SET(context->mount_flags, 0, MS_SHARED)) return true; @@ -2277,7 +2278,6 @@ static bool exec_directory_is_private(const ExecContext *context, ExecDirectoryT static int create_many_symlinks(const char *root, const char *source, char **symlinks) { _cleanup_free_ char *src_abs = NULL; - char **dst; int r; assert(source); @@ -2595,6 +2595,168 @@ static int write_credential( return 0; } +static int load_credential( + const ExecContext *context, + const ExecParameters *params, + ExecLoadCredential *lc, + const char *unit, + int read_dfd, + int write_dfd, + uid_t uid, + bool ownership_ok, + uint64_t *left) { + + assert(context); + assert(lc); + assert(unit); + assert(write_dfd >= 0); + assert(left); + + ReadFullFileFlags flags = READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER; + _cleanup_(erase_and_freep) char *data = NULL; + _cleanup_free_ char *j = NULL, *bindname = NULL; + bool missing_ok = true; + const char *source; + size_t size, add; + int r; + + if (path_is_absolute(lc->path) || read_dfd >= 0) { + /* If this is an absolute path, read the data directly from it, and support AF_UNIX sockets */ + source = lc->path; + flags |= READ_FULL_FILE_CONNECT_SOCKET; + + /* Pass some minimal info about the unit and the credential name we are looking to acquire + * via the source socket address in case we read off an AF_UNIX socket. */ + if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, lc->id) < 0) + return -ENOMEM; + + missing_ok = false; + + } else if (params->received_credentials) { + /* If this is a relative path, take it relative to the credentials we received + * ourselves. We don't support the AF_UNIX stuff in this mode, since we are operating + * on a credential store, i.e. this is guaranteed to be regular files. */ + j = path_join(params->received_credentials, lc->path); + if (!j) + return -ENOMEM; + + source = j; + } else + source = NULL; + + if (source) + r = read_full_file_full( + read_dfd, source, + UINT64_MAX, + lc->encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX, + flags | (lc->encrypted ? READ_FULL_FILE_UNBASE64 : 0), + bindname, + &data, &size); + else + r = -ENOENT; + + if (r == -ENOENT && (missing_ok || hashmap_contains(context->set_credentials, lc->id))) { + /* Make a missing inherited credential non-fatal, let's just continue. After all apps + * will get clear errors if we don't pass such a missing credential on as they + * themselves will get ENOENT when trying to read them, which should not be much + * worse than when we handle the error here and make it fatal. + * + * Also, if the source file doesn't exist, but a fallback is set via SetCredentials= + * we are fine, too. */ + log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", lc->path); + return 0; + } + if (r < 0) + return log_debug_errno(r, "Failed to read credential '%s': %m", lc->path); + + if (lc->encrypted) { + _cleanup_free_ void *plaintext = NULL; + size_t plaintext_size = 0; + + r = decrypt_credential_and_warn(lc->id, now(CLOCK_REALTIME), NULL, data, size, &plaintext, &plaintext_size); + if (r < 0) + return r; + + free_and_replace(data, plaintext); + size = plaintext_size; + } + + add = strlen(lc->id) + size; + if (add > *left) + return -E2BIG; + + r = write_credential(write_dfd, lc->id, data, size, uid, ownership_ok); + if (r < 0) + return r; + + *left -= add; + return 0; +} + +struct load_cred_args { + Set *seen_creds; + + const ExecContext *context; + const ExecParameters *params; + ExecLoadCredential *parent_local_credential; + const char *unit; + int dfd; + uid_t uid; + bool ownership_ok; + uint64_t *left; +}; + +static int load_cred_recurse_dir_cb( + RecurseDirEvent event, + const char *path, + int dir_fd, + int inode_fd, + const struct dirent *de, + const struct statx *sx, + void *userdata) { + + _cleanup_free_ char *credname = NULL, *sub_id = NULL; + struct load_cred_args *args = userdata; + int r; + + if (event != RECURSE_DIR_ENTRY) + return RECURSE_DIR_CONTINUE; + + if (!IN_SET(de->d_type, DT_REG, DT_SOCK)) + return RECURSE_DIR_CONTINUE; + + credname = strreplace(path, "/", "_"); + if (!credname) + return -ENOMEM; + + sub_id = strjoin(args->parent_local_credential->id, "_", credname); + if (!sub_id) + return -ENOMEM; + + if (!credential_name_valid(sub_id)) + return -EINVAL; + + if (set_contains(args->seen_creds, sub_id)) { + log_debug("Skipping credential with duplicated ID %s at %s", sub_id, path); + return RECURSE_DIR_CONTINUE; + } + + r = set_put_strdup(&args->seen_creds, sub_id); + if (r < 0) + return r; + + r = load_credential(args->context, args->params, + &(ExecLoadCredential) { + .id = sub_id, + .path = (char *) de->d_name, + .encrypted = args->parent_local_credential->encrypted, + }, args->unit, dir_fd, args->dfd, args->uid, args->ownership_ok, args->left); + if (r < 0) + return r; + + return RECURSE_DIR_CONTINUE; +} + static int acquire_credentials( const ExecContext *context, const ExecParameters *params, @@ -2605,6 +2767,7 @@ static int acquire_credentials( uint64_t left = CREDENTIALS_TOTAL_SIZE_MAX; _cleanup_close_ int dfd = -1; + _cleanup_set_free_ Set *seen_creds = NULL; ExecLoadCredential *lc; ExecSetCredential *sc; int r; @@ -2616,84 +2779,53 @@ static int acquire_credentials( if (dfd < 0) return -errno; + seen_creds = set_new(&string_hash_ops_free); + if (!seen_creds) + return -ENOMEM; + /* First, load credentials off disk (or acquire via AF_UNIX socket) */ HASHMAP_FOREACH(lc, context->load_credentials) { - ReadFullFileFlags flags = READ_FULL_FILE_SECURE|READ_FULL_FILE_FAIL_WHEN_LARGER; - _cleanup_(erase_and_freep) char *data = NULL; - _cleanup_free_ char *j = NULL, *bindname = NULL; - bool missing_ok = true; - const char *source; - size_t size, add; + _cleanup_close_ int sub_fd = -1; - if (path_is_absolute(lc->path)) { - /* If this is an absolute path, read the data directly from it, and support AF_UNIX sockets */ - source = lc->path; - flags |= READ_FULL_FILE_CONNECT_SOCKET; - - /* Pass some minimal info about the unit and the credential name we are looking to acquire - * via the source socket address in case we read off an AF_UNIX socket. */ - if (asprintf(&bindname, "@%" PRIx64"/unit/%s/%s", random_u64(), unit, lc->id) < 0) - return -ENOMEM; - - missing_ok = false; - - } else if (params->received_credentials) { - /* If this is a relative path, take it relative to the credentials we received - * ourselves. We don't support the AF_UNIX stuff in this mode, since we are operating - * on a credential store, i.e. this is guaranteed to be regular files. */ - j = path_join(params->received_credentials, lc->path); - if (!j) - return -ENOMEM; - - source = j; - } else - source = NULL; - - if (source) - r = read_full_file_full( - AT_FDCWD, source, - UINT64_MAX, - lc->encrypted ? CREDENTIAL_ENCRYPTED_SIZE_MAX : CREDENTIAL_SIZE_MAX, - flags | (lc->encrypted ? READ_FULL_FILE_UNBASE64 : 0), - bindname, - &data, &size); - else - r = -ENOENT; - if (r == -ENOENT && (missing_ok || hashmap_contains(context->set_credentials, lc->id))) { - /* Make a missing inherited credential non-fatal, let's just continue. After all apps - * will get clear errors if we don't pass such a missing credential on as they - * themselves will get ENOENT when trying to read them, which should not be much - * worse than when we handle the error here and make it fatal. - * - * Also, if the source file doesn't exist, but a fallback is set via SetCredentials= - * we are fine, too. */ - log_debug_errno(r, "Couldn't read inherited credential '%s', skipping: %m", lc->path); + /* Skip over credentials with unspecified paths. These are received by the + * service manager via the $CREDENTIALS_DIRECTORY environment variable. */ + if (!is_path(lc->path) && streq(lc->id, lc->path)) continue; - } - if (r < 0) - return log_debug_errno(r, "Failed to read credential '%s': %m", lc->path); - if (lc->encrypted) { - _cleanup_free_ void *plaintext = NULL; - size_t plaintext_size = 0; + sub_fd = open(lc->path, O_DIRECTORY|O_CLOEXEC|O_RDONLY); + if (sub_fd < 0 && errno != ENOTDIR) + return -errno; - r = decrypt_credential_and_warn(lc->id, now(CLOCK_REALTIME), NULL, data, size, &plaintext, &plaintext_size); + if (sub_fd < 0) { + r = set_put_strdup(&seen_creds, lc->id); + if (r < 0) + return r; + r = load_credential(context, params, lc, unit, -1, dfd, uid, ownership_ok, &left); if (r < 0) return r; - free_and_replace(data, plaintext); - size = plaintext_size; + } else { + r = recurse_dir( + sub_fd, + /* path= */ "", + /* statx_mask= */ 0, + /* n_depth_max= */ UINT_MAX, + RECURSE_DIR_IGNORE_DOT|RECURSE_DIR_ENSURE_TYPE, + load_cred_recurse_dir_cb, + &(struct load_cred_args) { + .seen_creds = seen_creds, + .context = context, + .params = params, + .parent_local_credential = lc, + .unit = unit, + .dfd = dfd, + .uid = uid, + .ownership_ok = ownership_ok, + .left = &left, + }); + if (r < 0) + return r; } - - add = strlen(lc->id) + size; - if (add > left) - return -E2BIG; - - r = write_credential(dfd, lc->id, data, size, uid, ownership_ok); - if (r < 0) - return r; - - left -= add; } /* First we use the literally specified credentials. Note that they might be overridden again below, @@ -2911,7 +3043,6 @@ static int setup_credentials( uid_t uid) { _cleanup_free_ char *p = NULL, *q = NULL; - const char *i; int r; assert(context); @@ -3216,7 +3347,6 @@ static int compile_symlinks( for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) { for (size_t i = 0; i < context->directories[dt].n_items; i++) { _cleanup_free_ char *private_path = NULL, *path = NULL; - char **symlink; STRV_FOREACH(symlink, context->directories[dt].items[i].symlinks) { _cleanup_free_ char *src_abs = NULL, *dst_abs = NULL; @@ -3279,6 +3409,9 @@ static bool insist_on_sandboxing( if (context->dynamic_user) return true; + if (context->n_extension_images > 0 || !strv_isempty(context->extension_directories)) + return true; + /* If there are any bind mounts set that don't map back onto themselves, fs namespacing becomes * essential. */ for (size_t i = 0; i < n_bind_mounts; i++) @@ -3302,7 +3435,8 @@ static int apply_mount_namespace( _cleanup_strv_free_ char **empty_directories = NULL, **symlinks = NULL; const char *tmp_dir = NULL, *var_tmp_dir = NULL; const char *root_dir = NULL, *root_image = NULL; - _cleanup_free_ char *creds_path = NULL, *incoming_dir = NULL, *propagate_dir = NULL; + _cleanup_free_ char *creds_path = NULL, *incoming_dir = NULL, *propagate_dir = NULL, + *extension_dir = NULL; NamespaceInfo ns_info; bool needs_sandboxing; BindMount *bind_mounts = NULL; @@ -3401,7 +3535,17 @@ static int apply_mount_namespace( r = -ENOMEM; goto finalize; } - } + + extension_dir = strdup("/run/systemd/unit-extensions"); + if (!extension_dir) { + r = -ENOMEM; + goto finalize; + } + } else + if (asprintf(&extension_dir, "/run/user/" UID_FMT "/systemd/unit-extensions", geteuid()) < 0) { + r = -ENOMEM; + goto finalize; + } r = setup_namespace(root_dir, root_image, context->root_image_options, &ns_info, context->read_write_paths, @@ -3427,8 +3571,10 @@ static int apply_mount_namespace( context->root_verity, context->extension_images, context->n_extension_images, + context->extension_directories, propagate_dir, incoming_dir, + extension_dir, root_dir || root_image ? params->notify_socket : NULL, error_path); @@ -3915,6 +4061,10 @@ static int exec_child( assert(params); assert(exit_status); + /* Explicitly test for CVE-2021-4034 inspired invocations */ + assert(command->path); + assert(!strv_isempty(command->argv)); + rename_process_from_path(command->path); /* We reset exactly these signals, since they are the only ones we set to SIG_IGN in the main @@ -4109,6 +4259,12 @@ static int exec_child( } r = cg_attach_everywhere(params->cgroup_supported, p, 0, NULL, NULL); + if (r == -EUCLEAN) { + *exit_status = EXIT_CGROUP; + return log_unit_error_errno(unit, r, "Failed to attach process to cgroup %s " + "because the cgroup or one of its parents or " + "siblings is in the threaded mode: %m", p); + } if (r < 0) { *exit_status = EXIT_CGROUP; return log_unit_error_errno(unit, r, "Failed to attach to cgroup %s: %m", p); @@ -4312,12 +4468,9 @@ static int exec_child( return log_oom(); } - /* The PATH variable is set to the default path in params->environment. - * However, this is overridden if user specified fields have PATH set. - * The intention is to also override PATH if the user does - * not specify PATH and the user has specified ExecSearchPath - */ - + /* The $PATH variable is set to the default path in params->environment. However, this is overridden + * if user-specified fields have $PATH set. The intention is to also override $PATH if the unit does + * not specify PATH but the unit has ExecSearchPath. */ if (!strv_isempty(context->exec_search_path)) { _cleanup_free_ char *joined = NULL; @@ -4354,22 +4507,26 @@ static int exec_child( return log_unit_error_errno(unit, r, "Failed to set up kernel keyring: %m"); } - /* We need sandboxing if the caller asked us to apply it and the command isn't explicitly excepted from it */ + /* We need sandboxing if the caller asked us to apply it and the command isn't explicitly excepted + * from it. */ needs_sandboxing = (params->flags & EXEC_APPLY_SANDBOXING) && !(command->flags & EXEC_COMMAND_FULLY_PRIVILEGED); - /* We need the ambient capability hack, if the caller asked us to apply it and the command is marked for it, and the kernel doesn't actually support ambient caps */ + /* We need the ambient capability hack, if the caller asked us to apply it and the command is marked + * for it, and the kernel doesn't actually support ambient caps. */ needs_ambient_hack = (params->flags & EXEC_APPLY_SANDBOXING) && (command->flags & EXEC_COMMAND_AMBIENT_MAGIC) && !ambient_capabilities_supported(); - /* We need setresuid() if the caller asked us to apply sandboxing and the command isn't explicitly excepted from either whole sandboxing or just setresuid() itself, and the ambient hack is not desired */ + /* We need setresuid() if the caller asked us to apply sandboxing and the command isn't explicitly + * excepted from either whole sandboxing or just setresuid() itself, and the ambient hack is not + * desired. */ if (needs_ambient_hack) needs_setuid = false; else needs_setuid = (params->flags & EXEC_APPLY_SANDBOXING) && !(command->flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID)); if (needs_sandboxing) { - /* MAC enablement checks need to be done before a new mount ns is created, as they rely on /sys being - * present. The actual MAC context application will happen later, as late as possible, to avoid - * impacting our own code paths. */ + /* MAC enablement checks need to be done before a new mount ns is created, as they rely on + * /sys being present. The actual MAC context application will happen later, as late as + * possible, to avoid impacting our own code paths. */ #if HAVE_SELINUX use_selinux = mac_selinux_use(); @@ -5105,6 +5262,7 @@ void exec_context_done(ExecContext *c) { c->root_hash_sig_path = mfree(c->root_hash_sig_path); c->root_verity = mfree(c->root_verity); c->extension_images = mount_image_free_many(c->extension_images, &c->n_extension_images); + c->extension_directories = strv_free(c->extension_directories); c->tty_path = mfree(c->tty_path); c->syslog_identifier = mfree(c->syslog_identifier); c->user = mfree(c->user); @@ -5185,7 +5343,6 @@ int exec_context_destroy_runtime_directory(const ExecContext *c, const char *run * service next. */ (void) rm_rf(p, REMOVE_ROOT); - char **symlink; STRV_FOREACH(symlink, c->directories[EXEC_DIRECTORY_RUNTIME].items[i].symlinks) { _cleanup_free_ char *symlink_abs = NULL; @@ -5259,12 +5416,9 @@ void exec_command_reset_status_array(ExecCommand *c, size_t n) { } void exec_command_reset_status_list_array(ExecCommand **c, size_t n) { - for (size_t i = 0; i < n; i++) { - ExecCommand *z; - + for (size_t i = 0; i < n; i++) LIST_FOREACH(command, z, c[i]) exec_status_reset(&z->exec_status); - } } typedef struct InvalidEnvInfo { @@ -5357,20 +5511,17 @@ static int exec_context_named_iofds( return targets == 0 ? 0 : -ENOENT; } -static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***l) { - char **i, **r = NULL; +static int exec_context_load_environment(const Unit *unit, const ExecContext *c, char ***ret) { + _cleanup_strv_free_ char **v = NULL; + int r; assert(c); - assert(l); + assert(ret); STRV_FOREACH(i, c->environment_files) { - char *fn; - int k; - bool ignore = false; - char **p; _cleanup_globfree_ glob_t pglob = {}; - - fn = *i; + bool ignore = false; + char *fn = *i; if (fn[0] == '-') { ignore = true; @@ -5380,33 +5531,30 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c, if (!path_is_absolute(fn)) { if (ignore) continue; - - strv_free(r); return -EINVAL; } /* Filename supports globbing, take all matching files */ - k = safe_glob(fn, 0, &pglob); - if (k < 0) { + r = safe_glob(fn, 0, &pglob); + if (r < 0) { if (ignore) continue; - - strv_free(r); - return k; + return r; } /* When we don't match anything, -ENOENT should be returned */ assert(pglob.gl_pathc > 0); for (unsigned n = 0; n < pglob.gl_pathc; n++) { - k = load_env_file(NULL, pglob.gl_pathv[n], &p); - if (k < 0) { + _cleanup_strv_free_ char **p = NULL; + + r = load_env_file(NULL, pglob.gl_pathv[n], &p); + if (r < 0) { if (ignore) continue; - - strv_free(r); - return k; + return r; } + /* Log invalid environment variables with filename */ if (p) { InvalidEnvInfo info = { @@ -5417,23 +5565,19 @@ static int exec_context_load_environment(const Unit *unit, const ExecContext *c, p = strv_env_clean_with_callback(p, invalid_env, &info); } - if (!r) - r = p; + if (!v) + v = TAKE_PTR(p); else { - char **m; - - m = strv_env_merge(r, p); - strv_free(r); - strv_free(p); + char **m = strv_env_merge(v, p); if (!m) return -ENOMEM; - r = m; + strv_free_and_replace(v, m); } } } - *l = r; + *ret = TAKE_PTR(v); return 0; } @@ -5475,8 +5619,6 @@ bool exec_context_may_touch_console(const ExecContext *ec) { } static void strv_fprintf(FILE *f, char **l) { - char **g; - assert(f); STRV_FOREACH(g, l) @@ -5496,7 +5638,6 @@ static void strv_dump(FILE* f, const char *prefix, const char *name, char **strv } void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { - char **e, **d; int r; assert(c); @@ -5558,8 +5699,6 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { fprintf(f, "%sRootImage: %s\n", prefix, c->root_image); if (c->root_image_options) { - MountOptions *o; - fprintf(f, "%sRootImageOptions:", prefix); LIST_FOREACH(mount_options, o, c->root_image_options) if (!isempty(o->options)) @@ -5928,9 +6067,11 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { } #if HAVE_LIBBPF - if (exec_context_restrict_filesystems_set(c)) - SET_FOREACH(e, c->restrict_filesystems) - fprintf(f, "%sRestrictFileSystems: %s\n", prefix, *e); + if (exec_context_restrict_filesystems_set(c)) { + char *fs; + SET_FOREACH(fs, c->restrict_filesystems) + fprintf(f, "%sRestrictFileSystems: %s\n", prefix, fs); + } #endif if (c->network_namespace_path) @@ -5956,8 +6097,6 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { } for (size_t i = 0; i < c->n_mount_images; i++) { - MountOptions *o; - fprintf(f, "%sMountImages: %s%s:%s", prefix, c->mount_images[i].ignore_enoent ? "-": "", c->mount_images[i].source, @@ -5970,8 +6109,6 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { } for (size_t i = 0; i < c->n_extension_images; i++) { - MountOptions *o; - fprintf(f, "%sExtensionImages: %s%s", prefix, c->extension_images[i].ignore_enoent ? "-": "", c->extension_images[i].source); @@ -5981,6 +6118,8 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { strempty(o->options)); fprintf(f, "\n"); } + + strv_dump(f, prefix, "ExtensionDirectories", c->extension_directories); } bool exec_context_maintains_privileges(const ExecContext *c) { @@ -6121,7 +6260,6 @@ int exec_context_get_clean_directories( return r; } - char **symlink; STRV_FOREACH(symlink, c->directories[t].items[i].symlinks) { j = path_join(prefix[t], *symlink); if (!j) @@ -6236,8 +6374,8 @@ void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) { prefix = strempty(prefix); - LIST_FOREACH(command, c, c) - exec_command_dump(c, f, prefix); + LIST_FOREACH(command, i, c) + exec_command_dump(i, f, prefix); } void exec_command_append_list(ExecCommand **l, ExecCommand *e) { diff --git a/src/core/execute.h b/src/core/execute.h index a898cbcc6..43070ce11 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -273,6 +273,7 @@ struct ExecContext { size_t n_mount_images; MountImage *extension_images; size_t n_extension_images; + char **extension_directories; uint64_t capability_bounding_set; uint64_t capability_ambient_set; @@ -385,6 +386,7 @@ typedef enum ExecFlags { EXEC_PASS_FDS = 1 << 10, EXEC_SETENV_RESULT = 1 << 11, EXEC_SET_WATCHDOG = 1 << 12, + EXEC_SETENV_MONITOR_RESULT = 1 << 13, /* Pass exit status to OnFailure= and OnSuccess= dependencies. */ } ExecFlags; /* Parameters for a specific invocation of a command. This structure is put together right before a command is diff --git a/src/core/fuzz-unit-file.c b/src/core/fuzz-unit-file.c index aef29f4cf..c12e874e2 100644 --- a/src/core/fuzz-unit-file.c +++ b/src/core/fuzz-unit-file.c @@ -2,7 +2,6 @@ #include "conf-parser.h" #include "fd-util.h" -#include "fileio.h" #include "fuzz.h" #include "install.h" #include "load-fragment.h" @@ -22,10 +21,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { const char *name; long offset; - if (size == 0) - return 0; - - f = fmemopen_unlocked((char*) data, size, "re"); + f = data_to_file(data, size); assert_se(f); if (read_line(f, LINE_MAX, &p) < 0) @@ -66,7 +62,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (!getenv("SYSTEMD_LOG_LEVEL")) log_set_max_level(LOG_CRIT); - assert_se(manager_new(UNIT_FILE_SYSTEM, MANAGER_TEST_RUN_MINIMAL, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_SYSTEM, MANAGER_TEST_RUN_MINIMAL, &m) >= 0); name = strjoina("a.", unit_type_to_string(t)); assert_se(unit_new_for_name(m, unit_vtable[t]->object_size, name, &u) >= 0); diff --git a/src/core/load-dropin.c b/src/core/load-dropin.c index 080a63bc7..53d2a3daa 100644 --- a/src/core/load-dropin.c +++ b/src/core/load-dropin.c @@ -13,7 +13,6 @@ static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suffix) { _cleanup_strv_free_ char **paths = NULL; - char **p; int r; r = unit_file_find_dropin_paths(NULL, @@ -85,7 +84,6 @@ static int process_deps(Unit *u, UnitDependency dependency, const char *dir_suff int unit_load_dropin(Unit *u) { _cleanup_strv_free_ char **l = NULL; - char **f; int r; assert(u); diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in index deea540e1..cc04393c1 100644 --- a/src/core/load-fragment-gperf.gperf.in +++ b/src/core/load-fragment-gperf.gperf.in @@ -9,6 +9,7 @@ {{type}}.RootHash, config_parse_exec_root_hash, 0, offsetof({{type}}, exec_context) {{type}}.RootHashSignature, config_parse_exec_root_hash_sig, 0, offsetof({{type}}, exec_context) {{type}}.RootVerity, config_parse_unit_path_printf, true, offsetof({{type}}, exec_context.root_verity) +{{type}}.ExtensionDirectories, config_parse_namespace_path_strv, 0, offsetof({{type}}, exec_context.extension_directories) {{type}}.ExtensionImages, config_parse_extension_images, 0, offsetof({{type}}, exec_context) {{type}}.MountImages, config_parse_mount_images, 0, offsetof({{type}}, exec_context) {{type}}.User, config_parse_user_group_compat, 0, offsetof({{type}}, exec_context.user) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 92a52819e..e0dc47ecd 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1634,7 +1634,6 @@ int config_parse_root_image_options( _cleanup_(mount_options_free_allp) MountOptions *options = NULL; _cleanup_strv_free_ char **l = NULL; - char **first = NULL, **second = NULL; ExecContext *c = data; const Unit *u = userdata; int r; @@ -4328,7 +4327,7 @@ int config_parse_io_limit( void *userdata) { _cleanup_free_ char *path = NULL, *resolved = NULL; - CGroupIODeviceLimit *l = NULL, *t; + CGroupIODeviceLimit *l = NULL; CGroupContext *c = data; CGroupIOLimitType type; const char *p = rvalue; @@ -4343,8 +4342,8 @@ int config_parse_io_limit( assert(type >= 0); if (isempty(rvalue)) { - LIST_FOREACH(device_limits, l, c->io_device_limits) - l->limits[type] = cgroup_io_limit_defaults[type]; + LIST_FOREACH(device_limits, t, c->io_device_limits) + t->limits[type] = cgroup_io_limit_defaults[type]; return 0; } @@ -4378,12 +4377,11 @@ int config_parse_io_limit( } } - LIST_FOREACH(device_limits, t, c->io_device_limits) { + LIST_FOREACH(device_limits, t, c->io_device_limits) if (path_equal(resolved, t->path)) { l = t; break; } - } if (!l) { CGroupIOLimitType ttype; @@ -4486,7 +4484,7 @@ int config_parse_blockio_bandwidth( void *userdata) { _cleanup_free_ char *path = NULL, *resolved = NULL; - CGroupBlockIODeviceBandwidth *b = NULL, *t; + CGroupBlockIODeviceBandwidth *b = NULL; CGroupContext *c = data; const char *p = rvalue; uint64_t bytes; @@ -4500,9 +4498,9 @@ int config_parse_blockio_bandwidth( read = streq("BlockIOReadBandwidth", lvalue); if (isempty(rvalue)) { - LIST_FOREACH(device_bandwidths, b, c->blockio_device_bandwidths) { - b->rbps = CGROUP_LIMIT_MAX; - b->wbps = CGROUP_LIMIT_MAX; + LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) { + t->rbps = CGROUP_LIMIT_MAX; + t->wbps = CGROUP_LIMIT_MAX; } return 0; } @@ -4533,14 +4531,13 @@ int config_parse_blockio_bandwidth( return 0; } - LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) { + LIST_FOREACH(device_bandwidths, t, c->blockio_device_bandwidths) if (path_equal(resolved, t->path)) { b = t; break; } - } - if (!t) { + if (!b) { b = new0(CGroupBlockIODeviceBandwidth, 1); if (!b) return log_oom(); diff --git a/src/core/main.c b/src/core/main.c index 57aedb9b9..f39c7b0e3 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #if HAVE_SECCOMP #include @@ -33,6 +32,7 @@ #include "clock-util.h" #include "conf-parser.h" #include "cpu-set-util.h" +#include "crash-handler.h" #include "dbus-manager.h" #include "dbus.h" #include "def.h" @@ -57,6 +57,7 @@ #include "log.h" #include "loopback-setup.h" #include "machine-id-setup.h" +#include "main.h" #include "manager.h" #include "manager-dump.h" #include "manager-serialize.h" @@ -71,7 +72,6 @@ #include "proc-cmdline.h" #include "process-util.h" #include "random-util.h" -#include "raw-clone.h" #include "rlimit-util.h" #if HAVE_SECCOMP #include "seccomp-util.h" @@ -116,10 +116,10 @@ static const char *arg_bus_introspect = NULL; * defaults are assigned in reset_arguments() below. */ static char *arg_default_unit; static bool arg_system; -static bool arg_dump_core; -static int arg_crash_chvt; -static bool arg_crash_shell; -static bool arg_crash_reboot; +bool arg_dump_core; +int arg_crash_chvt; +bool arg_crash_shell; +bool arg_crash_reboot; static char *arg_confirm_spawn; static ShowStatus arg_show_status; static StatusUnitFormat arg_status_unit_format; @@ -138,7 +138,9 @@ static unsigned arg_default_start_limit_burst; static usec_t arg_runtime_watchdog; static usec_t arg_reboot_watchdog; static usec_t arg_kexec_watchdog; +static usec_t arg_pretimeout_watchdog; static char *arg_early_core_pattern; +static char *arg_watchdog_pretimeout_governor; static char *arg_watchdog_device; static char **arg_default_environment; static char **arg_manager_environment; @@ -203,142 +205,6 @@ static int manager_find_user_config_paths(char ***ret_files, char ***ret_dirs) { return 0; } -_noreturn_ static void freeze_or_exit_or_reboot(void) { - - /* If we are running in a container, let's prefer exiting, after all we can propagate an exit code to - * the container manager, and thus inform it that something went wrong. */ - if (detect_container() > 0) { - log_emergency("Exiting PID 1..."); - _exit(EXIT_EXCEPTION); - } - - if (arg_crash_reboot) { - log_notice("Rebooting in 10s..."); - (void) sleep(10); - - log_notice("Rebooting now..."); - (void) reboot(RB_AUTOBOOT); - log_emergency_errno(errno, "Failed to reboot: %m"); - } - - log_emergency("Freezing execution."); - sync(); - freeze(); -} - -_noreturn_ static void crash(int sig) { - struct sigaction sa; - pid_t pid; - - if (getpid_cached() != 1) - /* Pass this on immediately, if this is not PID 1 */ - (void) raise(sig); - else if (!arg_dump_core) - log_emergency("Caught <%s>, not dumping core.", signal_to_string(sig)); - else { - sa = (struct sigaction) { - .sa_handler = nop_signal_handler, - .sa_flags = SA_NOCLDSTOP|SA_RESTART, - }; - - /* We want to wait for the core process, hence let's enable SIGCHLD */ - (void) sigaction(SIGCHLD, &sa, NULL); - - pid = raw_clone(SIGCHLD); - if (pid < 0) - log_emergency_errno(errno, "Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig)); - else if (pid == 0) { - /* Enable default signal handler for core dump */ - - sa = (struct sigaction) { - .sa_handler = SIG_DFL, - }; - (void) sigaction(sig, &sa, NULL); - - /* Don't limit the coredump size */ - (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)); - - /* Just to be sure... */ - (void) chdir("/"); - - /* Raise the signal again */ - pid = raw_getpid(); - (void) kill(pid, sig); /* raise() would kill the parent */ - - assert_not_reached(); - _exit(EXIT_EXCEPTION); - } else { - siginfo_t status; - int r; - - /* Order things nicely. */ - r = wait_for_terminate(pid, &status); - if (r < 0) - log_emergency_errno(r, "Caught <%s>, waitpid() failed: %m", signal_to_string(sig)); - else if (status.si_code != CLD_DUMPED) { - const char *s = status.si_code == CLD_EXITED - ? exit_status_to_string(status.si_status, EXIT_STATUS_LIBC) - : signal_to_string(status.si_status); - - log_emergency("Caught <%s>, core dump failed (child "PID_FMT", code=%s, status=%i/%s).", - signal_to_string(sig), - pid, - sigchld_code_to_string(status.si_code), - status.si_status, strna(s)); - } else - log_emergency("Caught <%s>, dumped core as pid "PID_FMT".", - signal_to_string(sig), pid); - } - } - - if (arg_crash_chvt >= 0) - (void) chvt(arg_crash_chvt); - - sa = (struct sigaction) { - .sa_handler = SIG_IGN, - .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART, - }; - - /* Let the kernel reap children for us */ - (void) sigaction(SIGCHLD, &sa, NULL); - - if (arg_crash_shell) { - log_notice("Executing crash shell in 10s..."); - (void) sleep(10); - - pid = raw_clone(SIGCHLD); - if (pid < 0) - log_emergency_errno(errno, "Failed to fork off crash shell: %m"); - else if (pid == 0) { - (void) setsid(); - (void) make_console_stdio(); - (void) rlimit_nofile_safe(); - (void) execle("/bin/sh", "/bin/sh", NULL, environ); - - log_emergency_errno(errno, "execle() failed: %m"); - _exit(EXIT_EXCEPTION); - } else { - log_info("Spawned crash shell as PID "PID_FMT".", pid); - (void) wait_for_terminate(pid, NULL); - } - } - - freeze_or_exit_or_reboot(); -} - -static void install_crash_handler(void) { - static const struct sigaction sa = { - .sa_handler = crash, - .sa_flags = SA_NODEFER, /* So that we can raise the signal again from the signal handler */ - }; - int r; - - /* We ignore the return value here, since, we don't mind if we cannot set up a crash handler */ - r = sigaction_many(&sa, SIGNALS_CRASH_HANDLER); - if (r < 0) - log_debug_errno(r, "I had trouble setting up the crash handler, ignoring: %m"); -} - static int console_setup(void) { _cleanup_close_ int tty_fd = -1; int r; @@ -557,6 +423,37 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat arg_kexec_watchdog = arg_reboot_watchdog = arg_runtime_watchdog; + } else if (proc_cmdline_key_streq(key, "systemd.watchdog_pre_sec")) { + + if (proc_cmdline_value_missing(key, value)) + return 0; + + if (streq(value, "default")) + arg_pretimeout_watchdog = USEC_INFINITY; + else if (streq(value, "off")) + arg_pretimeout_watchdog = 0; + else { + r = parse_sec(value, &arg_pretimeout_watchdog); + if (r < 0) { + log_warning_errno(r, "Failed to parse systemd.watchdog_pre_sec= argument '%s', ignoring: %m", value); + return 0; + } + } + + } else if (proc_cmdline_key_streq(key, "systemd.watchdog_pretimeout_governor")) { + + if (proc_cmdline_value_missing(key, value) || isempty(value)) { + arg_watchdog_pretimeout_governor = mfree(arg_watchdog_pretimeout_governor); + return 0; + } + + if (!string_is_safe(value)) { + log_warning("Watchdog pretimeout governor '%s' is not valid, ignoring.", value); + return 0; + } + + return free_and_strdup_warn(&arg_watchdog_pretimeout_governor, value); + } else if (proc_cmdline_key_streq(key, "systemd.clock_usec")) { if (proc_cmdline_value_missing(key, value)) @@ -692,71 +589,73 @@ static int config_parse_oom_score_adjust( static int parse_config_file(void) { const ConfigTableItem items[] = { - { "Manager", "LogLevel", config_parse_level2, 0, NULL }, - { "Manager", "LogTarget", config_parse_target, 0, NULL }, - { "Manager", "LogColor", config_parse_color, 0, NULL }, - { "Manager", "LogLocation", config_parse_location, 0, NULL }, - { "Manager", "LogTime", config_parse_time, 0, NULL }, - { "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core }, - { "Manager", "CrashChVT", /* legacy */ config_parse_crash_chvt, 0, &arg_crash_chvt }, - { "Manager", "CrashChangeVT", config_parse_crash_chvt, 0, &arg_crash_chvt }, - { "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell }, - { "Manager", "CrashReboot", config_parse_bool, 0, &arg_crash_reboot }, - { "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status }, - { "Manager", "StatusUnitFormat", config_parse_status_unit_format, 0, &arg_status_unit_format }, - { "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, &arg_cpu_affinity }, - { "Manager", "NUMAPolicy", config_parse_numa_policy, 0, &arg_numa_policy.type }, - { "Manager", "NUMAMask", config_parse_numa_mask, 0, &arg_numa_policy }, - { "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL }, - { "Manager", "RuntimeWatchdogSec", config_parse_watchdog_sec, 0, &arg_runtime_watchdog }, - { "Manager", "RebootWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog }, - { "Manager", "ShutdownWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog }, /* obsolete alias */ - { "Manager", "KExecWatchdogSec", config_parse_watchdog_sec, 0, &arg_kexec_watchdog }, - { "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device }, - { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set }, - { "Manager", "NoNewPrivileges", config_parse_bool, 0, &arg_no_new_privs }, + { "Manager", "LogLevel", config_parse_level2, 0, NULL }, + { "Manager", "LogTarget", config_parse_target, 0, NULL }, + { "Manager", "LogColor", config_parse_color, 0, NULL }, + { "Manager", "LogLocation", config_parse_location, 0, NULL }, + { "Manager", "LogTime", config_parse_time, 0, NULL }, + { "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core }, + { "Manager", "CrashChVT", /* legacy */ config_parse_crash_chvt, 0, &arg_crash_chvt }, + { "Manager", "CrashChangeVT", config_parse_crash_chvt, 0, &arg_crash_chvt }, + { "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell }, + { "Manager", "CrashReboot", config_parse_bool, 0, &arg_crash_reboot }, + { "Manager", "ShowStatus", config_parse_show_status, 0, &arg_show_status }, + { "Manager", "StatusUnitFormat", config_parse_status_unit_format, 0, &arg_status_unit_format }, + { "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, &arg_cpu_affinity }, + { "Manager", "NUMAPolicy", config_parse_numa_policy, 0, &arg_numa_policy.type }, + { "Manager", "NUMAMask", config_parse_numa_mask, 0, &arg_numa_policy }, + { "Manager", "JoinControllers", config_parse_warn_compat, DISABLED_CONFIGURATION, NULL }, + { "Manager", "RuntimeWatchdogSec", config_parse_watchdog_sec, 0, &arg_runtime_watchdog }, + { "Manager", "RuntimeWatchdogPreSec", config_parse_watchdog_sec, 0, &arg_pretimeout_watchdog }, + { "Manager", "RebootWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog }, + { "Manager", "ShutdownWatchdogSec", config_parse_watchdog_sec, 0, &arg_reboot_watchdog }, /* obsolete alias */ + { "Manager", "KExecWatchdogSec", config_parse_watchdog_sec, 0, &arg_kexec_watchdog }, + { "Manager", "WatchdogDevice", config_parse_path, 0, &arg_watchdog_device }, + { "Manager", "RuntimeWatchdogPreGovernor", config_parse_string, CONFIG_PARSE_STRING_SAFE, &arg_watchdog_pretimeout_governor }, + { "Manager", "CapabilityBoundingSet", config_parse_capability_set, 0, &arg_capability_bounding_set }, + { "Manager", "NoNewPrivileges", config_parse_bool, 0, &arg_no_new_privs }, #if HAVE_SECCOMP - { "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs }, + { "Manager", "SystemCallArchitectures", config_parse_syscall_archs, 0, &arg_syscall_archs }, #endif - { "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec }, - { "Manager", "DefaultTimerAccuracySec", config_parse_sec, 0, &arg_default_timer_accuracy_usec }, - { "Manager", "DefaultStandardOutput", config_parse_output_restricted, 0, &arg_default_std_output }, - { "Manager", "DefaultStandardError", config_parse_output_restricted, 0, &arg_default_std_error }, - { "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec }, - { "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec }, - { "Manager", "DefaultTimeoutAbortSec", config_parse_default_timeout_abort, 0, NULL }, - { "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec }, - { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */ - { "Manager", "DefaultStartLimitIntervalSec", config_parse_sec, 0, &arg_default_start_limit_interval }, - { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst }, - { "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment }, - { "Manager", "ManagerEnvironment", config_parse_environ, 0, &arg_manager_environment }, - { "Manager", "DefaultLimitCPU", config_parse_rlimit, RLIMIT_CPU, arg_default_rlimit }, - { "Manager", "DefaultLimitFSIZE", config_parse_rlimit, RLIMIT_FSIZE, arg_default_rlimit }, - { "Manager", "DefaultLimitDATA", config_parse_rlimit, RLIMIT_DATA, arg_default_rlimit }, - { "Manager", "DefaultLimitSTACK", config_parse_rlimit, RLIMIT_STACK, arg_default_rlimit }, - { "Manager", "DefaultLimitCORE", config_parse_rlimit, RLIMIT_CORE, arg_default_rlimit }, - { "Manager", "DefaultLimitRSS", config_parse_rlimit, RLIMIT_RSS, arg_default_rlimit }, - { "Manager", "DefaultLimitNOFILE", config_parse_rlimit, RLIMIT_NOFILE, arg_default_rlimit }, - { "Manager", "DefaultLimitAS", config_parse_rlimit, RLIMIT_AS, arg_default_rlimit }, - { "Manager", "DefaultLimitNPROC", config_parse_rlimit, RLIMIT_NPROC, arg_default_rlimit }, - { "Manager", "DefaultLimitMEMLOCK", config_parse_rlimit, RLIMIT_MEMLOCK, arg_default_rlimit }, - { "Manager", "DefaultLimitLOCKS", config_parse_rlimit, RLIMIT_LOCKS, arg_default_rlimit }, - { "Manager", "DefaultLimitSIGPENDING", config_parse_rlimit, RLIMIT_SIGPENDING, arg_default_rlimit }, - { "Manager", "DefaultLimitMSGQUEUE", config_parse_rlimit, RLIMIT_MSGQUEUE, arg_default_rlimit }, - { "Manager", "DefaultLimitNICE", config_parse_rlimit, RLIMIT_NICE, arg_default_rlimit }, - { "Manager", "DefaultLimitRTPRIO", config_parse_rlimit, RLIMIT_RTPRIO, arg_default_rlimit }, - { "Manager", "DefaultLimitRTTIME", config_parse_rlimit, RLIMIT_RTTIME, arg_default_rlimit }, - { "Manager", "DefaultCPUAccounting", config_parse_tristate, 0, &arg_default_cpu_accounting }, - { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_default_io_accounting }, - { "Manager", "DefaultIPAccounting", config_parse_bool, 0, &arg_default_ip_accounting }, - { "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting }, - { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting }, - { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting }, - { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_default_tasks_max }, - { "Manager", "CtrlAltDelBurstAction", config_parse_emergency_action, 0, &arg_cad_burst_action }, - { "Manager", "DefaultOOMPolicy", config_parse_oom_policy, 0, &arg_default_oom_policy }, - { "Manager", "DefaultOOMScoreAdjust", config_parse_oom_score_adjust, 0, NULL }, + { "Manager", "TimerSlackNSec", config_parse_nsec, 0, &arg_timer_slack_nsec }, + { "Manager", "DefaultTimerAccuracySec", config_parse_sec, 0, &arg_default_timer_accuracy_usec }, + { "Manager", "DefaultStandardOutput", config_parse_output_restricted, 0, &arg_default_std_output }, + { "Manager", "DefaultStandardError", config_parse_output_restricted, 0, &arg_default_std_error }, + { "Manager", "DefaultTimeoutStartSec", config_parse_sec, 0, &arg_default_timeout_start_usec }, + { "Manager", "DefaultTimeoutStopSec", config_parse_sec, 0, &arg_default_timeout_stop_usec }, + { "Manager", "DefaultTimeoutAbortSec", config_parse_default_timeout_abort, 0, NULL }, + { "Manager", "DefaultRestartSec", config_parse_sec, 0, &arg_default_restart_usec }, + { "Manager", "DefaultStartLimitInterval", config_parse_sec, 0, &arg_default_start_limit_interval }, /* obsolete alias */ + { "Manager", "DefaultStartLimitIntervalSec", config_parse_sec, 0, &arg_default_start_limit_interval }, + { "Manager", "DefaultStartLimitBurst", config_parse_unsigned, 0, &arg_default_start_limit_burst }, + { "Manager", "DefaultEnvironment", config_parse_environ, 0, &arg_default_environment }, + { "Manager", "ManagerEnvironment", config_parse_environ, 0, &arg_manager_environment }, + { "Manager", "DefaultLimitCPU", config_parse_rlimit, RLIMIT_CPU, arg_default_rlimit }, + { "Manager", "DefaultLimitFSIZE", config_parse_rlimit, RLIMIT_FSIZE, arg_default_rlimit }, + { "Manager", "DefaultLimitDATA", config_parse_rlimit, RLIMIT_DATA, arg_default_rlimit }, + { "Manager", "DefaultLimitSTACK", config_parse_rlimit, RLIMIT_STACK, arg_default_rlimit }, + { "Manager", "DefaultLimitCORE", config_parse_rlimit, RLIMIT_CORE, arg_default_rlimit }, + { "Manager", "DefaultLimitRSS", config_parse_rlimit, RLIMIT_RSS, arg_default_rlimit }, + { "Manager", "DefaultLimitNOFILE", config_parse_rlimit, RLIMIT_NOFILE, arg_default_rlimit }, + { "Manager", "DefaultLimitAS", config_parse_rlimit, RLIMIT_AS, arg_default_rlimit }, + { "Manager", "DefaultLimitNPROC", config_parse_rlimit, RLIMIT_NPROC, arg_default_rlimit }, + { "Manager", "DefaultLimitMEMLOCK", config_parse_rlimit, RLIMIT_MEMLOCK, arg_default_rlimit }, + { "Manager", "DefaultLimitLOCKS", config_parse_rlimit, RLIMIT_LOCKS, arg_default_rlimit }, + { "Manager", "DefaultLimitSIGPENDING", config_parse_rlimit, RLIMIT_SIGPENDING, arg_default_rlimit }, + { "Manager", "DefaultLimitMSGQUEUE", config_parse_rlimit, RLIMIT_MSGQUEUE, arg_default_rlimit }, + { "Manager", "DefaultLimitNICE", config_parse_rlimit, RLIMIT_NICE, arg_default_rlimit }, + { "Manager", "DefaultLimitRTPRIO", config_parse_rlimit, RLIMIT_RTPRIO, arg_default_rlimit }, + { "Manager", "DefaultLimitRTTIME", config_parse_rlimit, RLIMIT_RTTIME, arg_default_rlimit }, + { "Manager", "DefaultCPUAccounting", config_parse_tristate, 0, &arg_default_cpu_accounting }, + { "Manager", "DefaultIOAccounting", config_parse_bool, 0, &arg_default_io_accounting }, + { "Manager", "DefaultIPAccounting", config_parse_bool, 0, &arg_default_ip_accounting }, + { "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting }, + { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting }, + { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting }, + { "Manager", "DefaultTasksMax", config_parse_tasks_max, 0, &arg_default_tasks_max }, + { "Manager", "CtrlAltDelBurstAction", config_parse_emergency_action, 0, &arg_cad_burst_action }, + { "Manager", "DefaultOOMPolicy", config_parse_oom_policy, 0, &arg_default_oom_policy }, + { "Manager", "DefaultOOMScoreAdjust", config_parse_oom_score_adjust, 0, NULL }, {} }; @@ -837,6 +736,7 @@ static void set_manager_defaults(Manager *m) { } static void set_manager_settings(Manager *m) { + int r; assert(m); @@ -851,6 +751,10 @@ static void set_manager_settings(Manager *m) { manager_set_watchdog(m, WATCHDOG_RUNTIME, arg_runtime_watchdog); manager_set_watchdog(m, WATCHDOG_REBOOT, arg_reboot_watchdog); manager_set_watchdog(m, WATCHDOG_KEXEC, arg_kexec_watchdog); + manager_set_watchdog(m, WATCHDOG_PRETIMEOUT, arg_pretimeout_watchdog); + r = manager_set_watchdog_pretimeout_governor(m, arg_watchdog_pretimeout_governor); + if (r < 0) + log_warning_errno(r, "Failed to set watchdog pretimeout governor to '%s', ignoring: %m", arg_watchdog_pretimeout_governor); manager_set_show_status(m, arg_show_status, "commandline"); m->status_unit_format = arg_status_unit_format; @@ -1314,7 +1218,7 @@ static void bump_file_max_and_nr_open(void) { #endif } -static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { +static int bump_rlimit_nofile(const struct rlimit *saved_rlimit) { struct rlimit new_rlimit; int r, nr; @@ -1343,7 +1247,7 @@ static int bump_rlimit_nofile(struct rlimit *saved_rlimit) { return 0; } -static int bump_rlimit_memlock(struct rlimit *saved_rlimit) { +static int bump_rlimit_memlock(const struct rlimit *saved_rlimit) { struct rlimit new_rlimit; uint64_t mm; int r; @@ -1595,7 +1499,10 @@ static int become_shutdown( watchdog_timer = arg_kexec_watchdog; /* If we reboot or kexec let's set the shutdown watchdog and tell the - * shutdown binary to repeatedly ping it */ + * shutdown binary to repeatedly ping it. + * Disable the pretimeout watchdog, as we do not support it from the shutdown binary. */ + (void) watchdog_setup_pretimeout(0); + (void) watchdog_setup_pretimeout_governor(NULL); r = watchdog_setup(watchdog_timer); watchdog_close(r < 0); @@ -1664,8 +1571,6 @@ static void initialize_clock(void) { } static void apply_clock_update(void) { - struct timespec ts; - /* This is called later than initialize_clock(), i.e. after we parsed configuration files/kernel * command line and such. */ @@ -1675,7 +1580,7 @@ static void apply_clock_update(void) { if (getpid_cached() != 1) return; - if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, arg_clock_usec)) < 0) + if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(arg_clock_usec)) < 0) log_error_errno(errno, "Failed to set system clock to time specified on kernel command line: %m"); else log_info("Set system clock to %s, as specified on the kernel command line.", @@ -1751,11 +1656,11 @@ static void update_cpu_affinity(bool skip_setup) { assert(arg_cpu_affinity.allocated > 0); - mask = cpu_set_to_string(&arg_cpu_affinity); - log_debug("Setting CPU affinity to %s.", strnull(mask)); + mask = cpu_set_to_range_string(&arg_cpu_affinity); + log_debug("Setting CPU affinity to {%s}.", strnull(mask)); if (sched_setaffinity(0, arg_cpu_affinity.allocated, arg_cpu_affinity.set) < 0) - log_warning_errno(errno, "Failed to set CPU affinity: %m"); + log_warning_errno(errno, "Failed to set CPU affinity, ignoring: %m"); } static void update_numa_policy(bool skip_setup) { @@ -1769,14 +1674,14 @@ static void update_numa_policy(bool skip_setup) { if (DEBUG_LOGGING) { policy = mpol_to_string(numa_policy_get_type(&arg_numa_policy)); nodes = cpu_set_to_range_string(&arg_numa_policy.nodes); - log_debug("Setting NUMA policy to %s, with nodes %s.", strnull(policy), strnull(nodes)); + log_debug("Setting NUMA policy to %s, with nodes {%s}.", strnull(policy), strnull(nodes)); } r = apply_numa_policy(&arg_numa_policy); if (r == -EOPNOTSUPP) log_debug_errno(r, "NUMA support not available, ignoring."); else if (r < 0) - log_warning_errno(r, "Failed to set NUMA memory policy: %m"); + log_warning_errno(r, "Failed to set NUMA memory policy, ignoring: %m"); } static void filter_args( @@ -1824,7 +1729,8 @@ static void filter_args( } } -static void do_reexecute( +static int do_reexecute( + ManagerObjective objective, int argc, char* argv[], const struct rlimit *saved_rlimit_nofile, @@ -1838,6 +1744,7 @@ static void do_reexecute( const char **args; int r; + assert(IN_SET(objective, MANAGER_REEXECUTE, MANAGER_SWITCH_ROOT)); assert(argc >= 0); assert(saved_rlimit_nofile); assert(saved_rlimit_memlock); @@ -1901,6 +1808,12 @@ static void do_reexecute( args[0] = SYSTEMD_BINARY_PATH; (void) execv(args[0], (char* const*) args); + + if (objective == MANAGER_REEXECUTE) { + *ret_error_message = "Failed to execute our own binary"; + return log_error_errno(errno, "Failed to execute our own binary %s: %m", args[0]); + } + log_debug_errno(errno, "Failed to execute our own binary %s, trying fallback: %m", args[0]); } @@ -1938,24 +1851,22 @@ static void do_reexecute( ANSI_HIGHLIGHT_RED " !! " ANSI_NORMAL, "Failed to execute /sbin/init"); + *ret_error_message = "Failed to execute fallback shell"; if (r == -ENOENT) { log_warning("No /sbin/init, trying fallback"); args[0] = "/bin/sh"; args[1] = NULL; (void) execve(args[0], (char* const*) args, saved_env); - log_error_errno(errno, "Failed to execute /bin/sh, giving up: %m"); + return log_error_errno(errno, "Failed to execute /bin/sh, giving up: %m"); } else - log_warning_errno(r, "Failed to execute /sbin/init, giving up: %m"); - - *ret_error_message = "Failed to execute fallback shell"; + return log_error_errno(r, "Failed to execute /sbin/init, giving up: %m"); } static int invoke_main_loop( Manager *m, const struct rlimit *saved_rlimit_nofile, const struct rlimit *saved_rlimit_memlock, - bool *ret_reexecute, int *ret_retval, /* Return parameters relevant for shutting down */ const char **ret_shutdown_verb, /* … */ FDSet **ret_fds, /* Return parameters for reexecuting */ @@ -1968,7 +1879,6 @@ static int invoke_main_loop( assert(m); assert(saved_rlimit_nofile); assert(saved_rlimit_memlock); - assert(ret_reexecute); assert(ret_retval); assert(ret_shutdown_verb); assert(ret_fds); @@ -1977,13 +1887,13 @@ static int invoke_main_loop( assert(ret_error_message); for (;;) { - r = manager_loop(m); - if (r < 0) { + int objective = manager_loop(m); + if (objective < 0) { *ret_error_message = "Failed to run main loop"; - return log_emergency_errno(r, "Failed to run main loop: %m"); + return log_emergency_errno(objective, "Failed to run main loop: %m"); } - switch ((ManagerObjective) r) { + switch (objective) { case MANAGER_RELOAD: { LogTarget saved_log_target; @@ -2010,17 +1920,15 @@ static int invoke_main_loop( if (saved_log_target >= 0) manager_override_log_target(m, saved_log_target); - r = manager_reload(m); - if (r < 0) + if (manager_reload(m) < 0) /* Reloading failed before the point of no return. * Let's continue running as if nothing happened. */ m->objective = MANAGER_OK; - break; + continue; } case MANAGER_REEXECUTE: - r = prepare_reexecute(m, &arg_serialization, ret_fds, false); if (r < 0) { *ret_error_message = "Failed to prepare for reexecution"; @@ -2029,12 +1937,11 @@ static int invoke_main_loop( log_notice("Reexecuting."); - *ret_reexecute = true; *ret_retval = EXIT_SUCCESS; *ret_shutdown_verb = NULL; *ret_switch_root_dir = *ret_switch_root_init = NULL; - return 0; + return objective; case MANAGER_SWITCH_ROOT: if (!m->switch_root_init) { @@ -2048,7 +1955,6 @@ static int invoke_main_loop( log_notice("Switching root."); - *ret_reexecute = true; *ret_retval = EXIT_SUCCESS; *ret_shutdown_verb = NULL; @@ -2056,20 +1962,18 @@ static int invoke_main_loop( *ret_switch_root_dir = TAKE_PTR(m->switch_root); *ret_switch_root_init = TAKE_PTR(m->switch_root_init); - return 0; + return objective; case MANAGER_EXIT: - if (MANAGER_IS_USER(m)) { log_debug("Exit."); - *ret_reexecute = false; *ret_retval = m->return_value; *ret_shutdown_verb = NULL; *ret_fds = NULL; *ret_switch_root_dir = *ret_switch_root_init = NULL; - return 0; + return objective; } _fallthrough_; @@ -2077,7 +1981,7 @@ static int invoke_main_loop( case MANAGER_POWEROFF: case MANAGER_HALT: case MANAGER_KEXEC: { - static const char * const table[_MANAGER_OBJECTIVE_MAX] = { + static const char* const table[_MANAGER_OBJECTIVE_MAX] = { [MANAGER_EXIT] = "exit", [MANAGER_REBOOT] = "reboot", [MANAGER_POWEROFF] = "poweroff", @@ -2087,13 +1991,12 @@ static int invoke_main_loop( log_notice("Shutting down."); - *ret_reexecute = false; *ret_retval = m->return_value; assert_se(*ret_shutdown_verb = table[m->objective]); *ret_fds = NULL; *ret_switch_root_dir = *ret_switch_root_init = NULL; - return 0; + return objective; } default: @@ -2259,7 +2162,7 @@ static int initialize_runtime( if (!arg_system) /* Become reaper of our children */ if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) - log_warning_errno(errno, "Failed to make us a subreaper: %m"); + log_warning_errno(errno, "Failed to make us a subreaper, ignoring: %m"); /* Bump up RLIMIT_NOFILE for systemd itself */ (void) bump_rlimit_nofile(saved_rlimit_nofile); @@ -2405,11 +2308,17 @@ static void fallback_rlimit_memlock(const struct rlimit *saved_rlimit_memlock) { return; } + if (arg_system) { + /* Raise the default limit to 8M also on old kernels and in containers (8M is the kernel + * default for this since kernel 5.16) */ + rl->rlim_max = MAX(rl->rlim_max, (rlim_t) DEFAULT_RLIMIT_MEMLOCK); + rl->rlim_cur = MAX(rl->rlim_cur, (rlim_t) DEFAULT_RLIMIT_MEMLOCK); + } + arg_default_rlimit[RLIMIT_MEMLOCK] = rl; } static void setenv_manager_environment(void) { - char **p; int r; STRV_FOREACH(p, arg_manager_environment) { @@ -2450,8 +2359,10 @@ static void reset_arguments(void) { arg_runtime_watchdog = 0; arg_reboot_watchdog = 10 * USEC_PER_MINUTE; arg_kexec_watchdog = 0; + arg_pretimeout_watchdog = 0; arg_early_core_pattern = NULL; arg_watchdog_device = NULL; + arg_watchdog_pretimeout_governor = mfree(arg_watchdog_pretimeout_governor); arg_default_environment = strv_free(arg_default_environment); arg_manager_environment = strv_free(arg_manager_environment); @@ -2709,15 +2620,18 @@ static int save_env(void) { } int main(int argc, char *argv[]) { - - dual_timestamp initrd_timestamp = DUAL_TIMESTAMP_NULL, userspace_timestamp = DUAL_TIMESTAMP_NULL, kernel_timestamp = DUAL_TIMESTAMP_NULL, - security_start_timestamp = DUAL_TIMESTAMP_NULL, security_finish_timestamp = DUAL_TIMESTAMP_NULL; + dual_timestamp + initrd_timestamp = DUAL_TIMESTAMP_NULL, + userspace_timestamp = DUAL_TIMESTAMP_NULL, + kernel_timestamp = DUAL_TIMESTAMP_NULL, + security_start_timestamp = DUAL_TIMESTAMP_NULL, + security_finish_timestamp = DUAL_TIMESTAMP_NULL; struct rlimit saved_rlimit_nofile = RLIMIT_MAKE_CONST(0), saved_rlimit_memlock = RLIMIT_MAKE_CONST(RLIM_INFINITY); /* The original rlimits we passed * in. Note we use different values * for the two that indicate whether * these fields are initialized! */ - bool skip_setup, loaded_policy = false, queue_default_job = false, first_boot = false, reexecute = false; + bool skip_setup, loaded_policy = false, queue_default_job = false, first_boot = false; char *switch_root_dir = NULL, *switch_root_init = NULL; usec_t before_startup, after_startup; static char systemd[] = "systemd"; @@ -2726,6 +2640,8 @@ int main(int argc, char *argv[]) { Manager *m = NULL; FDSet *fds = NULL; + assert_se(argc > 0 && !isempty(argv[0])); + /* SysV compatibility: redirect init → telinit */ redirect_telinit(argc, argv); @@ -2969,7 +2885,7 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - r = manager_new(arg_system ? UNIT_FILE_SYSTEM : UNIT_FILE_USER, + r = manager_new(arg_system ? LOOKUP_SCOPE_SYSTEM : LOOKUP_SCOPE_USER, arg_action == ACTION_TEST ? MANAGER_TEST_FULL : 0, &m); if (r < 0) { @@ -3021,16 +2937,23 @@ int main(int argc, char *argv[]) { goto finish; } - (void) invoke_main_loop(m, - &saved_rlimit_nofile, - &saved_rlimit_memlock, - &reexecute, - &retval, - &shutdown_verb, - &fds, - &switch_root_dir, - &switch_root_init, - &error_message); + r = invoke_main_loop(m, + &saved_rlimit_nofile, + &saved_rlimit_memlock, + &retval, + &shutdown_verb, + &fds, + &switch_root_dir, + &switch_root_init, + &error_message); + assert(r < 0 || IN_SET(r, MANAGER_EXIT, /* MANAGER_OK is not expected here. */ + MANAGER_RELOAD, + MANAGER_REEXECUTE, + MANAGER_REBOOT, + MANAGER_POWEROFF, + MANAGER_HALT, + MANAGER_KEXEC, + MANAGER_SWITCH_ROOT)); finish: pager_close(); @@ -3043,14 +2966,15 @@ finish: mac_selinux_finish(); - if (reexecute) - do_reexecute(argc, argv, - &saved_rlimit_nofile, - &saved_rlimit_memlock, - fds, - switch_root_dir, - switch_root_init, - &error_message); /* This only returns if reexecution failed */ + if (IN_SET(r, MANAGER_REEXECUTE, MANAGER_SWITCH_ROOT)) + r = do_reexecute(r, + argc, argv, + &saved_rlimit_nofile, + &saved_rlimit_memlock, + fds, + switch_root_dir, + switch_root_init, + &error_message); /* This only returns if reexecution failed */ arg_serialization = safe_fclose(arg_serialization); fds = fdset_free(fds); @@ -3076,7 +3000,9 @@ finish: __lsan_do_leak_check(); #endif - if (shutdown_verb) { + /* Try to invoke the shutdown binary unless we already failed. + * If we failed above, we want to freeze after finishing cleanup. */ + if (r >= 0 && shutdown_verb) { r = become_shutdown(shutdown_verb, retval); log_error_errno(r, "Failed to execute shutdown binary, %s: %m", getpid_cached() == 1 ? "freezing" : "quitting"); error_message = "Failed to execute shutdown binary"; diff --git a/src/core/main.h b/src/core/main.h new file mode 100644 index 000000000..b12a1ccfd --- /dev/null +++ b/src/core/main.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +extern bool arg_dump_core; +extern int arg_crash_chvt; +extern bool arg_crash_shell; +extern bool arg_crash_reboot; diff --git a/src/core/manager-serialize.c b/src/core/manager-serialize.c index 60a35f48f..6a5dec436 100644 --- a/src/core/manager-serialize.c +++ b/src/core/manager-serialize.c @@ -118,6 +118,8 @@ int manager_serialize( (void) serialize_usec(f, "runtime-watchdog-overridden", m->watchdog_overridden[WATCHDOG_RUNTIME]); (void) serialize_usec(f, "reboot-watchdog-overridden", m->watchdog_overridden[WATCHDOG_REBOOT]); (void) serialize_usec(f, "kexec-watchdog-overridden", m->watchdog_overridden[WATCHDOG_KEXEC]); + (void) serialize_usec(f, "pretimeout-watchdog-overridden", m->watchdog_overridden[WATCHDOG_PRETIMEOUT]); + (void) serialize_item(f, "pretimeout-watchdog-governor-overridden", m->watchdog_pretimeout_governor_overridden); for (ManagerTimestamp q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) { _cleanup_free_ char *joined = NULL; @@ -455,6 +457,19 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { else manager_override_watchdog(m, WATCHDOG_KEXEC, t); + } else if ((val = startswith(l, "pretimeout-watchdog-overridden="))) { + usec_t t; + + if (deserialize_usec(val, &t) < 0) + log_notice("Failed to parse pretimeout-watchdog-overridden value '%s', ignoring.", val); + else + manager_override_watchdog(m, WATCHDOG_PRETIMEOUT, t); + + } else if ((val = startswith(l, "pretimeout-watchdog-governor-overridden="))) { + r = free_and_strdup(&m->watchdog_pretimeout_governor_overridden, val); + if (r < 0) + return r; + } else if (startswith(l, "env=")) { r = deserialize_environment(l + 4, &m->client_environment); if (r < 0) diff --git a/src/core/manager.c b/src/core/manager.c index 12c49e7fc..449310329 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -39,6 +39,7 @@ #include "dirent-util.h" #include "env-util.h" #include "escape.h" +#include "event-util.h" #include "exec-util.h" #include "execute.h" #include "exit-status.h" @@ -406,13 +407,8 @@ static int manager_setup_time_change(Manager *m) { return 0; m->time_change_event_source = sd_event_source_disable_unref(m->time_change_event_source); - m->time_change_fd = safe_close(m->time_change_fd); - m->time_change_fd = time_change_fd(); - if (m->time_change_fd < 0) - return log_error_errno(m->time_change_fd, "Failed to create timer change timer fd: %m"); - - r = sd_event_add_io(m->event, &m->time_change_event_source, m->time_change_fd, EPOLLIN, manager_dispatch_time_change_fd, m); + r = event_add_time_change(m->event, &m->time_change_event_source, manager_dispatch_time_change_fd, m); if (r < 0) return log_error_errno(r, "Failed to create time change event source: %m"); @@ -421,8 +417,6 @@ static int manager_setup_time_change(Manager *m) { if (r < 0) return log_error_errno(r, "Failed to set priority of time change event sources: %m"); - (void) sd_event_source_set_description(m->time_change_event_source, "manager-time-change"); - log_debug("Set up TFD_TIMER_CANCEL_ON_SET timerfd."); return 0; @@ -781,13 +775,13 @@ static int manager_setup_sigchld_event_source(Manager *m) { return 0; } -int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager **_m) { +int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager **_m) { _cleanup_(manager_freep) Manager *m = NULL; const char *e; int r; assert(_m); - assert(IN_SET(scope, UNIT_FILE_SYSTEM, UNIT_FILE_USER)); + assert(IN_SET(scope, LOOKUP_SCOPE_SYSTEM, LOOKUP_SCOPE_USER)); m = new(Manager, 1); if (!m) @@ -813,13 +807,13 @@ int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager .watchdog_overridden[WATCHDOG_RUNTIME] = USEC_INFINITY, .watchdog_overridden[WATCHDOG_REBOOT] = USEC_INFINITY, .watchdog_overridden[WATCHDOG_KEXEC] = USEC_INFINITY, + .watchdog_overridden[WATCHDOG_PRETIMEOUT] = USEC_INFINITY, .show_status_overridden = _SHOW_STATUS_INVALID, .notify_fd = -1, .cgroups_agent_fd = -1, .signal_fd = -1, - .time_change_fd = -1, .user_lookup_fds = { -1, -1 }, .private_listen_fd = -1, .dev_autofs_fd = -1, @@ -1221,7 +1215,6 @@ static void unit_gc_sweep(Unit *u, unsigned gc_marker) { is_bad = false; } - const UnitRef *ref; LIST_FOREACH(refs_by_target, ref, u->refs_by_target) { unit_gc_sweep(ref->source, gc_marker); @@ -1508,7 +1501,6 @@ Manager* manager_free(Manager *m) { safe_close(m->signal_fd); safe_close(m->notify_fd); safe_close(m->cgroups_agent_fd); - safe_close(m->time_change_fd); safe_close_pair(m->user_lookup_fds); manager_close_ask_password(m); @@ -1541,6 +1533,9 @@ Manager* manager_free(Manager *m) { m->prefix[dt] = mfree(m->prefix[dt]); free(m->received_credentials); + free(m->watchdog_pretimeout_governor); + free(m->watchdog_pretimeout_governor_overridden); + #if BPF_FRAMEWORK lsm_bpf_destroy(m->restrict_fs); #endif @@ -1705,7 +1700,7 @@ static void manager_preset_all(Manager *m) { return; /* If this is the first boot, and we are in the host system, then preset everything */ - r = unit_file_preset_all(UNIT_FILE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0); + r = unit_file_preset_all(LOOKUP_SCOPE_SYSTEM, 0, NULL, UNIT_FILE_PRESET_ENABLE_ONLY, NULL, 0); if (r < 0) log_full_errno(r == -EEXIST ? LOG_NOTICE : LOG_WARNING, r, "Failed to populate /etc with preset unit settings, ignoring: %m"); @@ -1756,11 +1751,11 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds, const char *roo /* If we are running in test mode, we still want to run the generators, * but we should not touch the real generator directories. */ - r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, - MANAGER_IS_TEST_RUN(m) ? LOOKUP_PATHS_TEMPORARY_GENERATED : 0, - root); + r = lookup_paths_init_or_warn(&m->lookup_paths, m->unit_file_scope, + MANAGER_IS_TEST_RUN(m) ? LOOKUP_PATHS_TEMPORARY_GENERATED : 0, + root); if (r < 0) - return log_error_errno(r, "Failed to initialize path lookup table: %m"); + return r; dual_timestamp_get(m->timestamps + manager_timestamp_initrd_mangle(MANAGER_TIMESTAMP_GENERATORS_START)); r = manager_run_environment_generators(m); @@ -2649,9 +2644,7 @@ static int manager_dispatch_sigchld(sd_event_source *source, void *userdata) { * We only do this for the cgroup the PID belonged to. */ (void) unit_check_oom(u1); - /* This only logs for now. In the future when the interface for kills/notifications - * is more stable we can extend service results table similar to how kernel oom kills - * are managed. */ + /* We check if systemd-oomd performed a kill so that we log and notify appropriately */ (void) unit_check_oomd_kill(u1); manager_invoke_sigchld_event(m, u1, &si); @@ -2909,7 +2902,6 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint Unit *u; assert(m); - assert(m->time_change_fd == fd); log_struct(LOG_DEBUG, "MESSAGE_ID=" SD_MESSAGE_TIME_CHANGE_STR, @@ -3232,31 +3224,80 @@ void manager_set_watchdog(Manager *m, WatchdogType t, usec_t timeout) { if (m->watchdog[t] == timeout) return; - if (t == WATCHDOG_RUNTIME) + if (t == WATCHDOG_RUNTIME) { if (!timestamp_is_set(m->watchdog_overridden[WATCHDOG_RUNTIME])) (void) watchdog_setup(timeout); + } else if (t == WATCHDOG_PRETIMEOUT) + if (m->watchdog_overridden[WATCHDOG_PRETIMEOUT] == USEC_INFINITY) + (void) watchdog_setup_pretimeout(timeout); m->watchdog[t] = timeout; } -int manager_override_watchdog(Manager *m, WatchdogType t, usec_t timeout) { +void manager_override_watchdog(Manager *m, WatchdogType t, usec_t timeout) { + + assert(m); + + if (MANAGER_IS_USER(m)) + return; + + if (m->watchdog_overridden[t] == timeout) + return; + + if (t == WATCHDOG_RUNTIME) { + usec_t usec = timestamp_is_set(timeout) ? timeout : m->watchdog[t]; + + (void) watchdog_setup(usec); + } else if (t == WATCHDOG_PRETIMEOUT) + (void) watchdog_setup_pretimeout(timeout); + + m->watchdog_overridden[t] = timeout; +} + +int manager_set_watchdog_pretimeout_governor(Manager *m, const char *governor) { + _cleanup_free_ char *p = NULL; + int r; assert(m); if (MANAGER_IS_USER(m)) return 0; - if (m->watchdog_overridden[t] == timeout) + if (streq_ptr(m->watchdog_pretimeout_governor, governor)) return 0; - if (t == WATCHDOG_RUNTIME) { - usec_t usec = timestamp_is_set(timeout) ? timeout : m->watchdog[t]; + p = strdup(governor); + if (!p) + return -ENOMEM; - (void) watchdog_setup(usec); - } + r = watchdog_setup_pretimeout_governor(governor); + if (r < 0) + return r; - m->watchdog_overridden[t] = timeout; - return 0; + return free_and_replace(m->watchdog_pretimeout_governor, p); +} + +int manager_override_watchdog_pretimeout_governor(Manager *m, const char *governor) { + _cleanup_free_ char *p = NULL; + int r; + + assert(m); + + if (MANAGER_IS_USER(m)) + return 0; + + if (streq_ptr(m->watchdog_pretimeout_governor_overridden, governor)) + return 0; + + p = strdup(governor); + if (!p) + return -ENOMEM; + + r = watchdog_setup_pretimeout_governor(governor); + if (r < 0) + return r; + + return free_and_replace(m->watchdog_pretimeout_governor_overridden, p); } int manager_reload(Manager *m) { @@ -3302,9 +3343,9 @@ int manager_reload(Manager *m) { m->uid_refs = hashmap_free(m->uid_refs); m->gid_refs = hashmap_free(m->gid_refs); - r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); + r = lookup_paths_init_or_warn(&m->lookup_paths, m->unit_file_scope, 0, NULL); if (r < 0) - log_warning_errno(r, "Failed to initialize path lookup table, ignoring: %m"); + return r; (void) manager_run_environment_generators(m); (void) manager_run_generators(m); @@ -3582,7 +3623,6 @@ void manager_check_finished(Manager *m) { } static bool generator_path_any(const char* const* paths) { - char **path; bool found = false; /* Optimize by skipping the whole process by not creating output directories diff --git a/src/core/manager.h b/src/core/manager.h index e445e4d75..dc6718ada 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -60,9 +60,9 @@ typedef enum StatusType { } StatusType; typedef enum OOMPolicy { - OOM_CONTINUE, /* The kernel kills the process it wants to kill, and that's it */ - OOM_STOP, /* The kernel kills the process it wants to kill, and we stop the unit */ - OOM_KILL, /* The kernel kills the process it wants to kill, and all others in the unit, and we stop the unit */ + OOM_CONTINUE, /* The kernel or systemd-oomd kills the process it wants to kill, and that's it */ + OOM_STOP, /* The kernel or systemd-oomd kills the process it wants to kill, and we stop the unit */ + OOM_KILL, /* The kernel or systemd-oomd kills the process it wants to kill, and all others in the unit, and we stop the unit */ _OOM_POLICY_MAX, _OOM_POLICY_INVALID = -EINVAL, } OOMPolicy; @@ -118,6 +118,7 @@ typedef enum WatchdogType { WATCHDOG_RUNTIME, WATCHDOG_REBOOT, WATCHDOG_KEXEC, + WATCHDOG_PRETIMEOUT, _WATCHDOG_TYPE_MAX, } WatchdogType; @@ -225,7 +226,6 @@ struct Manager { sd_event_source *sigchld_event_source; - int time_change_fd; sd_event_source *time_change_event_source; sd_event_source *timezone_change_event_source; @@ -235,7 +235,7 @@ struct Manager { int user_lookup_fds[2]; sd_event_source *user_lookup_event_source; - UnitFileScope unit_file_scope; + LookupScope unit_file_scope; LookupPaths lookup_paths; Hashmap *unit_id_map; Hashmap *unit_name_map; @@ -247,6 +247,8 @@ struct Manager { usec_t watchdog[_WATCHDOG_TYPE_MAX]; usec_t watchdog_overridden[_WATCHDOG_TYPE_MAX]; + char *watchdog_pretimeout_governor; + char *watchdog_pretimeout_governor_overridden; dual_timestamp timestamps[_MANAGER_TIMESTAMP_MAX]; @@ -461,8 +463,8 @@ static inline usec_t manager_default_timeout_abort_usec(Manager *m) { return m->default_timeout_abort_set ? m->default_timeout_abort_usec : m->default_timeout_stop_usec; } -#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM) -#define MANAGER_IS_USER(m) ((m)->unit_file_scope != UNIT_FILE_SYSTEM) +#define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == LOOKUP_SCOPE_SYSTEM) +#define MANAGER_IS_USER(m) ((m)->unit_file_scope != LOOKUP_SCOPE_SYSTEM) #define MANAGER_IS_RELOADING(m) ((m)->n_reloading > 0) @@ -473,7 +475,7 @@ static inline usec_t manager_default_timeout_abort_usec(Manager *m) { #define MANAGER_IS_TEST_RUN(m) ((m)->test_run_flags != 0) -int manager_new(UnitFileScope scope, ManagerTestRunFlags test_run_flags, Manager **m); +int manager_new(LookupScope scope, ManagerTestRunFlags test_run_flags, Manager **m); Manager* manager_free(Manager *m); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); @@ -573,7 +575,9 @@ ManagerTimestamp manager_timestamp_initrd_mangle(ManagerTimestamp s); usec_t manager_get_watchdog(Manager *m, WatchdogType t); void manager_set_watchdog(Manager *m, WatchdogType t, usec_t timeout); -int manager_override_watchdog(Manager *m, WatchdogType t, usec_t timeout); +void manager_override_watchdog(Manager *m, WatchdogType t, usec_t timeout); +int manager_set_watchdog_pretimeout_governor(Manager *m, const char *governor); +int manager_override_watchdog_pretimeout_governor(Manager *m, const char *governor); const char* oom_policy_to_string(OOMPolicy i) _const_; OOMPolicy oom_policy_from_string(const char *s) _pure_; diff --git a/src/core/meson.build b/src/core/meson.build index d229d4677..f5e04b37c 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -167,12 +167,18 @@ load_fragment_gperf_nulstr_c = custom_target( command : [awk, '-f', '@INPUT0@', '@INPUT1@'], capture : true) -libcore = static_library( - 'core', +libcore_name = 'systemd-core-@0@'.format(shared_lib_tag) + +libcore = shared_library( + libcore_name, libcore_sources, load_fragment_gperf_c, load_fragment_gperf_nulstr_c, include_directories : includes, + c_args : ['-fvisibility=default'], + link_args : ['-shared', + '-Wl,--version-script=' + libshared_sym_path], + link_with : libshared, dependencies : [versiondep, threads, libdl, @@ -184,12 +190,19 @@ libcore = static_library( libapparmor, libselinux, libmount, + libblkid, libacl], - build_by_default : false) + install : true, + install_dir : rootlibexecdir) core_includes = [includes, include_directories('.')] -systemd_sources = files('main.c') +systemd_sources = files( + 'main.c', + 'main.h', + 'crash-handler.c', + 'crash-handler.h', +) in_files = [['system.conf', pkgsysconfdir], ['user.conf', pkgsysconfdir], @@ -228,7 +241,7 @@ endif ############################################################ fuzzers += [ - [['src/core/fuzz-unit-file.c'], + [files('fuzz-unit-file.c'), [libcore, libshared], [libmount]], diff --git a/src/core/mount.c b/src/core/mount.c index c650b5abe..329abfdaa 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1957,7 +1957,6 @@ static int drain_libmount(Manager *m) { static int mount_process_proc_self_mountinfo(Manager *m) { _cleanup_set_free_free_ Set *around = NULL, *gone = NULL; const char *what; - Unit *u; int r; assert(m); diff --git a/src/core/namespace.c b/src/core/namespace.c index a731c9386..4ede17788 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -63,6 +63,7 @@ typedef enum MountMode { EXEC, TMPFS, RUN, + EXTENSION_DIRECTORIES, /* Bind-mounted outside the root directory, and used by subsequent mounts */ EXTENSION_IMAGES, /* Mounted outside the root directory, and used by subsequent mounts */ MQUEUEFS, READWRITE_IMPLICIT, /* Should have the lowest priority. */ @@ -313,8 +314,6 @@ static void mount_entry_done(MountEntry *p) { } static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, bool forcibly_require_prefix) { - char **i; - assert(p); /* Adds a list of user-supplied READWRITE/READWRITE_IMPLICIT/READONLY/INACCESSIBLE entries */ @@ -349,8 +348,6 @@ static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, boo } static int append_empty_dir_mounts(MountEntry **p, char **strv) { - char **i; - assert(p); /* Adds tmpfs mounts to provide readable but empty directories. This is primarily used to implement the @@ -408,24 +405,24 @@ static int append_mount_images(MountEntry **p, const MountImage *mount_images, s return 0; } -static int append_extension_images( +static int append_extensions( MountEntry **p, const char *root, const char *extension_dir, char **hierarchies, const MountImage *mount_images, - size_t n) { + size_t n, + char **extension_directories) { _cleanup_strv_free_ char **overlays = NULL; - char **hierarchy; int r; + if (n == 0 && strv_isempty(extension_directories)) + return 0; + assert(p); assert(extension_dir); - if (n == 0) - return 0; - /* Prepare a list of overlays, that will have as each element a string suitable for being * passed as a lowerdir= parameter, so start with the hierarchy on the root. * The overlays vector will have the same number of elements and will correspond to the @@ -482,6 +479,62 @@ static int append_extension_images( }; } + /* Secondly, extend the lowerdir= parameters with each ExtensionDirectory. + * Bind mount them in the same location as the ExtensionImages, so that we + * can check that they are valid trees (extension-release.d). */ + STRV_FOREACH(extension_directory, extension_directories) { + _cleanup_free_ char *mount_point = NULL, *source = NULL; + const char *e = *extension_directory; + bool ignore_enoent = false; + + /* Pick up the counter where the ExtensionImages left it. */ + r = asprintf(&mount_point, "%s/%zu", extension_dir, n++); + if (r < 0) + return -ENOMEM; + + /* Look for any prefixes */ + if (startswith(e, "-")) { + e++; + ignore_enoent = true; + } + /* Ignore this for now */ + if (startswith(e, "+")) + e++; + + source = strdup(e); + if (!source) + return -ENOMEM; + + for (size_t j = 0; hierarchies && hierarchies[j]; ++j) { + _cleanup_free_ char *prefixed_hierarchy = NULL, *escaped = NULL, *lowerdir = NULL; + + prefixed_hierarchy = path_join(mount_point, hierarchies[j]); + if (!prefixed_hierarchy) + return -ENOMEM; + + escaped = shell_escape(prefixed_hierarchy, ",:"); + if (!escaped) + return -ENOMEM; + + /* Note that lowerdir= parameters are in 'reverse' order, so the + * top-most directory in the overlay comes first in the list. */ + lowerdir = strjoin(escaped, ":", overlays[j]); + if (!lowerdir) + return -ENOMEM; + + free_and_replace(overlays[j], lowerdir); + } + + *((*p)++) = (MountEntry) { + .path_malloc = TAKE_PTR(mount_point), + .source_const = TAKE_PTR(source), + .mode = EXTENSION_DIRECTORIES, + .ignore = ignore_enoent, + .has_prefix = true, + .read_only = true, + }; + } + /* Then, for each hierarchy, prepare an overlay with the list of lowerdir= strings * set up earlier. */ for (size_t i = 0; hierarchies && hierarchies[i]; ++i) { @@ -605,9 +658,12 @@ static int append_protect_system(MountEntry **p, ProtectSystem protect_system, b static int mount_path_compare(const MountEntry *a, const MountEntry *b) { int d; - /* EXTENSION_IMAGES will be used by other mounts as a base, so sort them first + /* ExtensionImages/Directories will be used by other mounts as a base, so sort them first * regardless of the prefix - they are set up in the propagate directory anyway */ d = -CMP(a->mode == EXTENSION_IMAGES, b->mode == EXTENSION_IMAGES); + if (d != 0) + return d; + d = -CMP(a->mode == EXTENSION_DIRECTORIES, b->mode == EXTENSION_DIRECTORIES); if (d != 0) return d; @@ -715,22 +771,20 @@ static void drop_nop(MountEntry *m, size_t *n) { /* Only suppress such subtrees for READONLY, READWRITE and READWRITE_IMPLICIT entries */ if (IN_SET(f->mode, READONLY, READWRITE, READWRITE_IMPLICIT)) { - MountEntry *p; - bool found = false; + MountEntry *found = NULL; /* Now let's find the first parent of the entry we are looking at. */ - for (p = t-1; p >= m; p--) { + for (MountEntry *p = PTR_SUB1(t, m); p; p = PTR_SUB1(p, m)) if (path_startswith(mount_entry_path(f), mount_entry_path(p))) { - found = true; + found = p; break; } - } /* We found it, let's see if it's the same mode, if so, we can drop this entry */ - if (found && p->mode == f->mode) { + if (found && found->mode == f->mode) { log_debug("%s (%s) is made redundant by %s (%s)", mount_entry_path(f), mount_mode_to_string(f->mode), - mount_entry_path(p), mount_mode_to_string(p->mode)); + mount_entry_path(found), mount_mode_to_string(found->mode)); mount_entry_done(f); continue; } @@ -757,8 +811,8 @@ static void drop_outside_root(const char *root_directory, MountEntry *m, size_t for (f = m, t = m; f < m + *n; f++) { - /* ExtensionImages bases are opened in /run/systemd/unit-extensions on the host */ - if (f->mode != EXTENSION_IMAGES && !path_startswith(mount_entry_path(f), root_directory)) { + /* ExtensionImages/Directories bases are opened in /run/systemd/unit-extensions on the host */ + if (!IN_SET(f->mode, EXTENSION_IMAGES, EXTENSION_DIRECTORIES) && !path_startswith(mount_entry_path(f), root_directory)) { log_debug("%s is outside of root directory.", mount_entry_path(f)); mount_entry_done(f); continue; @@ -1067,9 +1121,15 @@ static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) { r = path_is_mount_point(entry_path, NULL, 0); if (r < 0) return log_debug_errno(r, "Unable to determine whether /proc is already mounted: %m"); - if (r == 0) - /* /proc is not mounted. Propagate the original error code. */ - return -EPERM; + if (r == 0) { + /* We lack permissions to mount a new instance of /proc, and it is not already + * mounted. But we can access the host's, so as a final fallback bind-mount it to + * the destination, as most likely we are inside a user manager in an unprivileged + * user namespace. */ + r = mount_nofollow_verbose(LOG_DEBUG, "/proc", entry_path, NULL, MS_BIND|MS_REC, NULL); + if (r < 0) + return -EPERM; + } } else if (r < 0) return r; @@ -1296,6 +1356,47 @@ static int apply_one_mount( what = mount_entry_path(m); break; + case EXTENSION_DIRECTORIES: { + _cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL, + *host_os_release_sysext_level = NULL, *extension_name = NULL; + _cleanup_strv_free_ char **extension_release = NULL; + + r = path_extract_filename(mount_entry_source(m), &extension_name); + if (r < 0) + return log_debug_errno(r, "Failed to extract extension name from %s: %m", mount_entry_source(m)); + + r = parse_os_release( + empty_to_root(root_directory), + "ID", &host_os_release_id, + "VERSION_ID", &host_os_release_version_id, + "SYSEXT_LEVEL", &host_os_release_sysext_level, + NULL); + if (r < 0) + return log_debug_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory)); + if (isempty(host_os_release_id)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "'ID' field not found or empty in 'os-release' data of OS tree '%s': %m", empty_to_root(root_directory)); + + r = load_extension_release_pairs(mount_entry_source(m), extension_name, &extension_release); + if (r == -ENOENT && m->ignore) + return 0; + if (r < 0) + return log_debug_errno(r, "Failed to parse directory %s extension-release metadata: %m", extension_name); + + r = extension_release_validate( + extension_name, + host_os_release_id, + host_os_release_version_id, + host_os_release_sysext_level, + /* host_sysext_scope */ NULL, /* Leave empty, we need to accept both system and portable */ + extension_release); + if (r == 0) + return log_debug_errno(SYNTHETIC_ERRNO(ESTALE), "Directory %s extension-release metadata does not match the root's", extension_name); + if (r < 0) + return log_debug_errno(r, "Failed to compare directory %s extension-release metadata with the root's os-release: %m", extension_name); + + _fallthrough_; + } + case BIND_MOUNT: rbind = false; @@ -1525,6 +1626,7 @@ static size_t namespace_calculate_mounts( size_t n_temporary_filesystems, size_t n_mount_images, size_t n_extension_images, + size_t n_extension_directories, size_t n_hierarchies, const char* tmp_dir, const char* var_tmp_dir, @@ -1559,7 +1661,8 @@ static size_t namespace_calculate_mounts( strv_length(empty_directories) + n_bind_mounts + n_mount_images + - (n_extension_images > 0 ? n_hierarchies + n_extension_images : 0) + /* Mount each image plus an overlay per hierarchy */ + (n_extension_images > 0 || n_extension_directories > 0 ? /* Mount each image and directory plus an overlay per hierarchy */ + n_hierarchies + n_extension_images + n_extension_directories: 0) + n_temporary_filesystems + ns_info->private_dev + (ns_info->protect_kernel_tunables ? @@ -1599,7 +1702,6 @@ static void drop_unused_mounts(const char *root_directory, MountEntry *mounts, s } static int create_symlinks_from_tuples(const char *root, char **strv_symlinks) { - char **src, **dst; int r; STRV_FOREACH_PAIR(src, dst, strv_symlinks) { @@ -1662,8 +1764,8 @@ static int apply_mounts( if (m->applied) continue; - /* ExtensionImages are first opened in the propagate directory, not in the root_directory */ - r = follow_symlink(m->mode != EXTENSION_IMAGES ? root : NULL, m); + /* ExtensionImages/Directories are first opened in the propagate directory, not in the root_directory */ + r = follow_symlink(!IN_SET(m->mode, EXTENSION_IMAGES, EXTENSION_DIRECTORIES) ? root : NULL, m); if (r < 0) { if (error_path && mount_entry_path(m)) *error_path = strdup(mount_entry_path(m)); @@ -1886,8 +1988,10 @@ int setup_namespace( const char *verity_data_path, const MountImage *extension_images, size_t n_extension_images, + char **extension_directories, const char *propagate_dir, const char *incoming_dir, + const char *extension_dir, const char *notify_socket, char **error_path) { @@ -1898,7 +2002,7 @@ int setup_namespace( _cleanup_strv_free_ char **hierarchies = NULL; MountEntry *m = NULL, *mounts = NULL; bool require_prefix = false, setup_propagate = false; - const char *root, *extension_dir = "/run/systemd/unit-extensions"; + const char *root; DissectImageFlags dissect_image_flags = DISSECT_IMAGE_GENERIC_ROOT | DISSECT_IMAGE_REQUIRE_ROOT | @@ -1999,7 +2103,7 @@ int setup_namespace( require_prefix = true; } - if (n_extension_images > 0) { + if (n_extension_images > 0 || !strv_isempty(extension_directories)) { r = parse_env_extension_hierarchies(&hierarchies); if (r < 0) return r; @@ -2017,6 +2121,7 @@ int setup_namespace( n_temporary_filesystems, n_mount_images, n_extension_images, + strv_length(extension_directories), strv_length(hierarchies), tmp_dir, var_tmp_dir, creds_path, @@ -2085,7 +2190,7 @@ int setup_namespace( if (r < 0) goto finish; - r = append_extension_images(&m, root, extension_dir, hierarchies, extension_images, n_extension_images); + r = append_extensions(&m, root, extension_dir, hierarchies, extension_images, n_extension_images, extension_directories); if (r < 0) goto finish; @@ -2278,9 +2383,9 @@ int setup_namespace( if (setup_propagate) (void) mkdir_p(propagate_dir, 0600); - if (n_extension_images > 0) - /* ExtensionImages mountpoint directories will be created while parsing the mounts to create, - * so have the parent ready */ + if (n_extension_images > 0 || !strv_isempty(extension_directories)) + /* ExtensionImages/Directories mountpoint directories will be created while parsing the + * mounts to create, so have the parent ready */ (void) mkdir_p(extension_dir, 0600); /* Remount / as SLAVE so that nothing now mounted in the namespace @@ -2340,6 +2445,17 @@ int setup_namespace( /* MS_MOVE does not work on MS_SHARED so the remount MS_SHARED will be done later */ r = mount_move_root(root); + if (r == -EINVAL && root_directory) { + /* If we are using root_directory and we don't have privileges (ie: user manager in a user + * namespace) and the root_directory is already a mount point in the parent namespace, + * MS_MOVE will fail as we don't have permission to change it (with EINVAL rather than + * EPERM). Attempt to bind-mount it over itself (like we do above if it's not already a + * mount point) and try again. */ + r = mount_nofollow_verbose(LOG_DEBUG, root, root, NULL, MS_BIND|MS_REC, NULL); + if (r < 0) + goto finish; + r = mount_move_root(root); + } if (r < 0) { log_debug_errno(r, "Failed to mount root with MS_MOVE: %m"); goto finish; @@ -2438,7 +2554,6 @@ MountImage* mount_image_free_many(MountImage *m, size_t *n) { int mount_image_add(MountImage **m, size_t *n, const MountImage *item) { _cleanup_free_ char *s = NULL, *d = NULL; _cleanup_(mount_options_free_allp) MountOptions *options = NULL; - MountOptions *i; MountImage *c; assert(m); diff --git a/src/core/namespace.h b/src/core/namespace.h index 62f05d758..3ef41d2c6 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -142,8 +142,10 @@ int setup_namespace( const char *root_verity, const MountImage *extension_images, size_t n_extension_images, + char **extension_directories, const char *propagate_dir, const char *incoming_dir, + const char *extension_dir, const char *notify_socket, char **error_path); diff --git a/src/core/path.c b/src/core/path.c index 0b736f00b..081439122 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -173,7 +173,6 @@ void path_spec_unwatch(PathSpec *s) { int path_spec_fd_event(PathSpec *s, uint32_t revents) { union inotify_event_buffer buffer; - struct inotify_event *e; ssize_t l; assert(s); @@ -191,7 +190,7 @@ int path_spec_fd_event(PathSpec *s, uint32_t revents) { } if (IN_SET(s->type, PATH_CHANGED, PATH_MODIFIED)) - FOREACH_INOTIFY_EVENT(e, buffer, l) + FOREACH_INOTIFY_EVENT_WARN(e, buffer, l) if (s->primary_wd == e->wd) return 1; @@ -292,7 +291,6 @@ static void path_done(Unit *u) { } static int path_add_mount_dependencies(Path *p) { - PathSpec *s; int r; assert(p); @@ -401,7 +399,6 @@ static int path_load(Unit *u) { static void path_dump(Unit *u, FILE *f, const char *prefix) { Path *p = PATH(u); Unit *trigger; - PathSpec *s; assert(p); assert(f); @@ -429,8 +426,6 @@ static void path_dump(Unit *u, FILE *f, const char *prefix) { } static void path_unwatch(Path *p) { - PathSpec *s; - assert(p); LIST_FOREACH(spec, s, p->specs) @@ -439,7 +434,6 @@ static void path_unwatch(Path *p) { static int path_watch(Path *p) { int r; - PathSpec *s; assert(p); @@ -539,8 +533,6 @@ fail: } static bool path_check_good(Path *p, bool initial, bool from_trigger_notify) { - PathSpec *s; - assert(p); LIST_FOREACH(spec, s, p->specs) @@ -591,8 +583,6 @@ fail: } static void path_mkdir(Path *p) { - PathSpec *s; - assert(p); if (!p->make_directory) @@ -637,7 +627,6 @@ static int path_stop(Unit *u) { static int path_serialize(Unit *u, FILE *f, FDSet *fds) { Path *p = PATH(u); - PathSpec *s; assert(u); assert(f); @@ -700,7 +689,6 @@ static int path_deserialize_item(Unit *u, const char *key, const char *value, FD _cleanup_free_ char *unescaped = NULL; ssize_t l; PathType type; - PathSpec *s; type = path_type_from_string(type_str); if (type < 0) { @@ -742,7 +730,7 @@ _pure_ static const char *path_sub_state_to_string(Unit *u) { } static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { - PathSpec *s = userdata; + PathSpec *s = userdata, *found = NULL; Path *p; int changed; @@ -755,18 +743,18 @@ static int path_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v if (!IN_SET(p->state, PATH_WAITING, PATH_RUNNING)) return 0; - /* log_debug("inotify wakeup on %s.", UNIT(p)->id); */ - - LIST_FOREACH(spec, s, p->specs) - if (path_spec_owns_inotify_fd(s, fd)) + LIST_FOREACH(spec, i, p->specs) + if (path_spec_owns_inotify_fd(i, fd)) { + found = i; break; + } - if (!s) { + if (!found) { log_error("Got event on unknown fd."); goto fail; } - changed = path_spec_fd_event(s, revents); + changed = path_spec_fd_event(found, revents); if (changed < 0) goto fail; diff --git a/src/core/restrict-ifaces.c b/src/core/restrict-ifaces.c index 0132c3c87..efa5c8d85 100644 --- a/src/core/restrict-ifaces.c +++ b/src/core/restrict-ifaces.c @@ -34,11 +34,11 @@ static int prepare_restrict_ifaces_bpf( obj = restrict_ifaces_bpf__open(); if (!obj) - return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOMEM), "Failed to open BPF object"); + return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, errno, "Failed to open BPF object: %m"); r = sym_bpf_map__resize(obj->maps.sd_restrictif, MAX(set_size(restrict_network_interfaces), 1u)); if (r != 0) - return log_unit_error_errno(u, r, + return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, r, "Failed to resize BPF map '%s': %m", sym_bpf_map__name(obj->maps.sd_restrictif)); @@ -46,7 +46,7 @@ static int prepare_restrict_ifaces_bpf( r = restrict_ifaces_bpf__load(obj); if (r != 0) - return log_unit_error_errno(u, r, "Failed to load BPF object: %m"); + return log_unit_full_errno(u, u ? LOG_ERR : LOG_DEBUG, r, "Failed to load BPF object: %m"); map_fd = sym_bpf_map__fd(obj->maps.sd_restrictif); @@ -61,7 +61,9 @@ static int prepare_restrict_ifaces_bpf( } if (sym_bpf_map_update_elem(map_fd, &ifindex, &dummy, BPF_ANY)) - return log_unit_error_errno(u, errno, "Failed to update BPF map '%s' fd: %m", sym_bpf_map__name(obj->maps.sd_restrictif)); + return log_unit_full_errno(u, u ? LOG_ERR : LOG_WARNING, errno, + "Failed to update BPF map '%s' fd: %m", + sym_bpf_map__name(obj->maps.sd_restrictif)); } *ret_object = TAKE_PTR(obj); diff --git a/src/core/service.c b/src/core/service.c index 87f0d34c8..2d7a08685 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -43,6 +43,8 @@ #include "utf8.h" #include "util.h" +#define service_spawn(...) service_spawn_internal(__func__, __VA_ARGS__) + static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { [SERVICE_DEAD] = UNIT_INACTIVE, [SERVICE_CONDITION] = UNIT_ACTIVATING, @@ -208,7 +210,7 @@ static void service_start_watchdog(Service *s) { assert(s); watchdog_usec = service_get_watchdog_usec(s); - if (IN_SET(watchdog_usec, 0, USEC_INFINITY)) { + if (!timestamp_is_set(watchdog_usec)) { service_stop_watchdog(s); return; } @@ -279,7 +281,7 @@ static void service_extend_timeout(Service *s, usec_t extend_timeout_usec) { assert(s); - if (IN_SET(extend_timeout_usec, 0, USEC_INFINITY)) + if (!timestamp_is_set(extend_timeout_usec)) return; extended = usec_add(now(CLOCK_MONOTONIC), extend_timeout_usec); @@ -432,8 +434,8 @@ static int service_add_fd_store(Service *s, int fd, const char *name, bool do_po * Use this errno rather than E[NM]FILE to distinguish from * the case where systemd itself hits the file limit. */ - LIST_FOREACH(fd_store, fs, s->fd_store) { - r = same_fd(fs->fd, fd); + LIST_FOREACH(fd_store, i, s->fd_store) { + r = same_fd(i->fd, fd); if (r < 0) return r; if (r > 0) { @@ -502,12 +504,10 @@ static int service_add_fd_store_set(Service *s, FDSet *fds, const char *name, bo } static void service_remove_fd_store(Service *s, const char *name) { - ServiceFDStore *fs, *n; - assert(s); assert(name); - LIST_FOREACH_SAFE(fd_store, fs, n, s->fd_store) { + LIST_FOREACH(fd_store, fs, s->fd_store) { if (!streq(fs->fdname, name)) continue; @@ -565,9 +565,7 @@ static int service_verify(Service *s) { assert(s); assert(UNIT(s)->load_state == UNIT_LOADED); - for (ServiceExecCommand c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) { - ExecCommand *command; - + for (ServiceExecCommand c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) LIST_FOREACH(command, command, s->exec_command[c]) { if (!path_is_absolute(command->path) && !filename_is_valid(command->path)) return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), @@ -579,7 +577,6 @@ static int service_verify(Service *s) { "Service has an empty argv in %s=. Refusing.", service_exec_command_to_string(c)); } - } if (!s->exec_command[SERVICE_EXEC_START] && !s->exec_command[SERVICE_EXEC_STOP] && UNIT(s)->success_action == EMERGENCY_ACTION_NONE) @@ -626,6 +623,9 @@ static int service_verify(Service *s) { if (s->runtime_max_usec == USEC_INFINITY && s->runtime_rand_extra_usec != 0) log_unit_warning(UNIT(s), "Service has RuntimeRandomizedExtraSec= setting, but no RuntimeMaxSec=. Ignoring."); + if (s->exit_type == SERVICE_EXIT_CGROUP && cg_unified() < CGROUP_UNIFIED_SYSTEMD) + log_unit_warning(UNIT(s), "Service has ExitType=cgroup set, but we are running with legacy cgroups v1, which might not work correctly. Continuing."); + return 0; } @@ -1329,7 +1329,6 @@ static int service_collect_fds( } if (s->n_fd_store > 0) { - ServiceFDStore *fs; size_t n_fds; char **nl; int *t; @@ -1444,7 +1443,40 @@ static bool service_exec_needs_notify_socket(Service *s, ExecFlags flags) { return s->notify_access != NOTIFY_NONE; } -static int service_spawn( +static Service *service_get_triggering_service(Service *s) { + Unit *candidate = NULL, *other; + + assert(s); + + /* Return the service which triggered service 's', this means dependency + * types which include the UNIT_ATOM_ON_{FAILURE,SUCCESS}_OF atoms. + * + * N.B. if there are multiple services which could trigger 's' via OnFailure= + * or OnSuccess= then we return NULL. This is since we don't know from which + * one to propagate the exit status. */ + + UNIT_FOREACH_DEPENDENCY(other, UNIT(s), UNIT_ATOM_ON_FAILURE_OF) { + if (candidate) + goto have_other; + candidate = other; + } + + UNIT_FOREACH_DEPENDENCY(other, UNIT(s), UNIT_ATOM_ON_SUCCESS_OF) { + if (candidate) + goto have_other; + candidate = other; + } + + return SERVICE(candidate); + + have_other: + log_unit_warning(UNIT(s), "multiple trigger source candidates for exit status propagation (%s, %s), skipping.", + candidate->id, other->id); + return NULL; +} + +static int service_spawn_internal( + const char *caller, Service *s, ExecCommand *c, usec_t timeout, @@ -1464,10 +1496,13 @@ static int service_spawn( pid_t pid; int r; + assert(caller); assert(s); assert(c); assert(ret_pid); + log_unit_debug(UNIT(s), "Will spawn child (%s): %s", caller, c->path); + r = unit_prepare_exec(UNIT(s)); /* This realizes the cgroup, among other things */ if (r < 0) return r; @@ -1508,7 +1543,7 @@ static int service_spawn( if (r < 0) return r; - our_env = new0(char*, 10); + our_env = new0(char*, 12); if (!our_env) return -ENOMEM; @@ -1565,22 +1600,45 @@ static int service_spawn( } } + Service *env_source = NULL; + const char *monitor_prefix; if (flags & EXEC_SETENV_RESULT) { - if (asprintf(our_env + n_env++, "SERVICE_RESULT=%s", service_result_to_string(s->result)) < 0) + env_source = s; + monitor_prefix = ""; + } else if (flags & EXEC_SETENV_MONITOR_RESULT) { + env_source = service_get_triggering_service(s); + monitor_prefix = "MONITOR_"; + } + + if (env_source) { + if (asprintf(our_env + n_env++, "%sSERVICE_RESULT=%s", monitor_prefix, service_result_to_string(env_source->result)) < 0) return -ENOMEM; - if (s->main_exec_status.pid > 0 && - dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) { - if (asprintf(our_env + n_env++, "EXIT_CODE=%s", sigchld_code_to_string(s->main_exec_status.code)) < 0) + if (env_source->main_exec_status.pid > 0 && + dual_timestamp_is_set(&env_source->main_exec_status.exit_timestamp)) { + if (asprintf(our_env + n_env++, "%sEXIT_CODE=%s", monitor_prefix, sigchld_code_to_string(env_source->main_exec_status.code)) < 0) return -ENOMEM; - if (s->main_exec_status.code == CLD_EXITED) - r = asprintf(our_env + n_env++, "EXIT_STATUS=%i", s->main_exec_status.status); + if (env_source->main_exec_status.code == CLD_EXITED) + r = asprintf(our_env + n_env++, "%sEXIT_STATUS=%i", monitor_prefix, env_source->main_exec_status.status); else - r = asprintf(our_env + n_env++, "EXIT_STATUS=%s", signal_to_string(s->main_exec_status.status)); + r = asprintf(our_env + n_env++, "%sEXIT_STATUS=%s", monitor_prefix, signal_to_string(env_source->main_exec_status.status)); + if (r < 0) return -ENOMEM; } + + if (env_source != s) { + if (!sd_id128_is_null(UNIT(env_source)->invocation_id)) { + r = asprintf(our_env + n_env++, "%sINVOCATION_ID=" SD_ID128_FORMAT_STR, + monitor_prefix, SD_ID128_FORMAT_VAL(UNIT(env_source)->invocation_id)); + if (r < 0) + return -ENOMEM; + } + + if (asprintf(our_env + n_env++, "%sUNIT=%s", monitor_prefix, UNIT(env_source)->id) < 0) + return -ENOMEM; + } } r = unit_set_exec_params(UNIT(s), &exec_params); @@ -1706,7 +1764,7 @@ static bool service_shall_restart(Service *s, const char **reason) { return false; case SERVICE_RESTART_ALWAYS: - return true; + return s->result != SERVICE_SKIP_CONDITION; case SERVICE_RESTART_ON_SUCCESS: return s->result == SERVICE_SUCCESS; @@ -2168,7 +2226,7 @@ static void service_enter_start(Service *s) { r = service_spawn(s, c, timeout, - EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG|EXEC_WRITE_CREDENTIALS, + EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG|EXEC_WRITE_CREDENTIALS|EXEC_SETENV_MONITOR_RESULT, &pid); if (r < 0) goto fail; @@ -2226,7 +2284,7 @@ static void service_enter_start_pre(Service *s) { r = service_spawn(s, s->control_command, s->timeout_start_usec, - EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN, + EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN|EXEC_SETENV_MONITOR_RESULT, &s->control_pid); if (r < 0) goto fail; @@ -2395,6 +2453,7 @@ static void service_run_next_control(Service *s) { EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL| (IN_SET(s->control_command_id, SERVICE_EXEC_CONDITION, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)| (IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0)| + (IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_START) ? EXEC_SETENV_MONITOR_RESULT : 0)| (IN_SET(s->control_command_id, SERVICE_EXEC_START_POST, SERVICE_EXEC_RELOAD, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_CONTROL_CGROUP : 0), &s->control_pid); if (r < 0) @@ -2431,7 +2490,7 @@ static void service_run_next_main(Service *s) { r = service_spawn(s, s->main_command, s->timeout_start_usec, - EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG, + EXEC_PASS_FDS|EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG|EXEC_SETENV_MONITOR_RESULT, &pid); if (r < 0) goto fail; @@ -2591,7 +2650,6 @@ static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command ServiceExecCommand id; size_t length = 0; unsigned idx; - char **arg; assert(s); assert(f); @@ -2647,7 +2705,6 @@ static int service_serialize_exec_command(Unit *u, FILE *f, ExecCommand *command static int service_serialize(Unit *u, FILE *f, FDSet *fds) { Service *s = SERVICE(u); - ServiceFDStore *fs; int r; assert(u); @@ -3347,10 +3404,13 @@ static void service_notify_cgroup_empty_event(Unit *u) { } } -static void service_notify_cgroup_oom_event(Unit *u) { +static void service_notify_cgroup_oom_event(Unit *u, bool managed_oom) { Service *s = SERVICE(u); - log_unit_debug(u, "Process of control group was killed by the OOM killer."); + if (managed_oom) + log_unit_debug(u, "Process(es) of control group were killed by systemd-oomd."); + else + log_unit_debug(u, "Process of control group was killed by the OOM killer."); if (s->oom_policy == OOM_CONTINUE) return; @@ -4027,7 +4087,6 @@ static void service_notify_message( Service *s = SERVICE(u); bool notify_dbus = false; const char *e; - char * const *i; int r; assert(u); @@ -4186,7 +4245,7 @@ static void service_notify_message( /* Process FD store messages. Either FDSTOREREMOVE=1 for removal, or FDSTORE=1 for addition. In both cases, * process FDNAME= for picking the file descriptor name to use. Note that FDNAME= is required when removing * fds, but optional when pushing in new fds, for compatibility reasons. */ - if (strv_find(tags, "FDSTOREREMOVE=1")) { + if (strv_contains(tags, "FDSTOREREMOVE=1")) { const char *name; name = strv_find_startswith(tags, "FDNAME="); @@ -4195,7 +4254,7 @@ static void service_notify_message( else service_remove_fd_store(s, name); - } else if (strv_find(tags, "FDSTORE=1")) { + } else if (strv_contains(tags, "FDSTORE=1")) { const char *name; name = strv_find_startswith(tags, "FDNAME="); @@ -4582,7 +4641,7 @@ static const char* const service_type_table[_SERVICE_TYPE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType); static const char* const service_exit_type_table[_SERVICE_EXIT_TYPE_MAX] = { - [SERVICE_EXIT_MAIN] = "main", + [SERVICE_EXIT_MAIN] = "main", [SERVICE_EXIT_CGROUP] = "cgroup", }; diff --git a/src/core/service.h b/src/core/service.h index 4116e40d8..91e02e6d7 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -75,7 +75,7 @@ typedef enum ServiceResult { SERVICE_FAILURE_CORE_DUMP, SERVICE_FAILURE_WATCHDOG, SERVICE_FAILURE_START_LIMIT_HIT, - SERVICE_FAILURE_OOM_KILL, + SERVICE_FAILURE_OOM_KILL, /* OOM Kill by the Kernel or systemd-oomd */ SERVICE_SKIP_CONDITION, _SERVICE_RESULT_MAX, _SERVICE_RESULT_INVALID = -EINVAL, diff --git a/src/core/socket.c b/src/core/socket.c index 8a5c7fdd0..802c6afde 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -208,8 +208,6 @@ static int socket_arm_timer(Socket *s, usec_t usec) { } static bool have_non_accept_socket(Socket *s) { - SocketPort *p; - assert(s); if (!s->accept) @@ -228,7 +226,6 @@ static bool have_non_accept_socket(Socket *s) { } static int socket_add_mount_dependencies(Socket *s) { - SocketPort *p; int r; assert(s); @@ -368,7 +365,6 @@ static int socket_add_extras(Socket *s) { static const char *socket_find_symlink_target(Socket *s) { const char *found = NULL; - SocketPort *p; LIST_FOREACH(port, p, s->ports) { const char *f = NULL; @@ -565,7 +561,6 @@ _const_ static const char* listen_lookup(int family, int type) { static void socket_dump(Unit *u, FILE *f, const char *prefix) { Socket *s = SOCKET(u); - SocketPort *p; const char *prefix2, *str; assert(s); @@ -781,8 +776,6 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { fprintf(f, "%sSocketProtocol: %s\n", prefix, str); if (!strv_isempty(s->symlinks)) { - char **q; - fprintf(f, "%sSymlinks:", prefix); STRV_FOREACH(q, s->symlinks) fprintf(f, " %s", *q); @@ -923,9 +916,6 @@ static int instance_from_socket(int fd, unsigned nr, char **instance) { } static void socket_close_fds(Socket *s) { - SocketPort *p; - char **i; - assert(s); LIST_FOREACH(port, p, s->ports) { @@ -1271,7 +1261,6 @@ static int mq_address_create( static int socket_symlink(Socket *s) { const char *p; - char **i; int r; assert(s); @@ -1624,7 +1613,6 @@ static int socket_open_fds(Socket *orig_s) { _cleanup_(socket_close_fdsp) Socket *s = orig_s; _cleanup_(mac_selinux_freep) char *label = NULL; bool know_label = false; - SocketPort *p; int r; assert(s); @@ -1734,7 +1722,6 @@ static int socket_open_fds(Socket *orig_s) { } static void socket_unwatch_fds(Socket *s) { - SocketPort *p; int r; assert(s); @@ -1753,7 +1740,6 @@ static void socket_unwatch_fds(Socket *s) { } static int socket_watch_fds(Socket *s) { - SocketPort *p; int r; assert(s); @@ -1791,7 +1777,6 @@ enum { static int socket_check_open(Socket *s) { bool have_open = false, have_closed = false; - SocketPort *p; assert(s); @@ -1995,7 +1980,6 @@ static int socket_chown(Socket *s, pid_t *_pid) { if (r == 0) { uid_t uid = UID_INVALID; gid_t gid = GID_INVALID; - SocketPort *p; /* Child */ @@ -2294,7 +2278,7 @@ fail: } static void flush_ports(Socket *s) { - SocketPort *p; + assert(s); /* Flush all incoming traffic, regardless if actual bytes or new connections, so that this socket isn't busy * anymore */ @@ -2563,7 +2547,6 @@ static int socket_stop(Unit *u) { static int socket_serialize(Unit *u, FILE *f, FDSet *fds) { Socket *s = SOCKET(u); - SocketPort *p; int r; assert(u); @@ -2674,7 +2657,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } } else if (streq(key, "fifo")) { int fd, skip = 0; - SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse fifo value: %s", value); @@ -2695,7 +2677,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } else if (streq(key, "special")) { int fd, skip = 0; - SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse special value: %s", value); @@ -2716,7 +2697,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } else if (streq(key, "mqueue")) { int fd, skip = 0; - SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse mqueue value: %s", value); @@ -2737,7 +2717,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } else if (streq(key, "socket")) { int fd, type, skip = 0; - SocketPort *p; if (sscanf(value, "%i %i %n", &fd, &type, &skip) < 2 || fd < 0 || type < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse socket value: %s", value); @@ -2758,7 +2737,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } else if (streq(key, "netlink")) { int fd, skip = 0; - SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse socket value: %s", value); @@ -2778,7 +2756,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, } else if (streq(key, "ffs")) { int fd, skip = 0; - SocketPort *p; if (sscanf(value, "%i %n", &fd, &skip) < 1 || fd < 0 || !fdset_contains(fds, fd)) log_unit_debug(u, "Failed to parse ffs value: %s", value); @@ -2805,7 +2782,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value, static void socket_distribute_fds(Unit *u, FDSet *fds) { Socket *s = SOCKET(u); - SocketPort *p; assert(u); @@ -3227,7 +3203,6 @@ static int socket_dispatch_timer(sd_event_source *source, usec_t usec, void *use int socket_collect_fds(Socket *s, int **fds) { size_t k = 0, n = 0; - SocketPort *p; int *rfds; assert(s); diff --git a/src/core/swap.c b/src/core/swap.c index 9c0d4fb22..0de73970a 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -537,7 +537,6 @@ static void swap_process_new(Manager *m, const char *device, int prio, bool set_ static void swap_set_state(Swap *s, SwapState state) { SwapState old_state; - Swap *other; assert(s); @@ -745,8 +744,6 @@ static void swap_enter_dead_or_active(Swap *s, SwapResult f) { assert(s); if (s->from_proc_swaps) { - Swap *other; - swap_enter_active(s, f); LIST_FOREACH_OTHERS(same_devnode, other, s) @@ -902,7 +899,7 @@ static void swap_cycle_clear(Swap *s) { } static int swap_start(Unit *u) { - Swap *s = SWAP(u), *other; + Swap *s = SWAP(u); int r; assert(s); @@ -1207,7 +1204,6 @@ static int swap_load_proc_swaps(Manager *m, bool set_flags) { } static int swap_process_proc_swaps(Manager *m) { - Unit *u; int r; assert(m); @@ -1300,7 +1296,7 @@ static int swap_dispatch_io(sd_event_source *source, int fd, uint32_t revents, v static Unit *swap_following(Unit *u) { Swap *s = SWAP(u); - Swap *other, *first = NULL; + Swap *first = NULL; assert(s); @@ -1336,7 +1332,7 @@ static Unit *swap_following(Unit *u) { } static int swap_following_set(Unit *u, Set **_set) { - Swap *s = SWAP(u), *other; + Swap *s = SWAP(u); _cleanup_set_free_ Set *set = NULL; int r; diff --git a/src/core/system.conf.in b/src/core/system.conf.in index 96fb64d2c..e132b086a 100644 --- a/src/core/system.conf.in +++ b/src/core/system.conf.in @@ -30,6 +30,8 @@ #NUMAPolicy=default #NUMAMask= #RuntimeWatchdogSec=off +#RuntimeWatchdogPreSec=off +#RuntimeWatchdogPreGovernor= #RebootWatchdogSec=10min #KExecWatchdogSec=off #WatchdogDevice= @@ -64,7 +66,7 @@ #DefaultLimitNOFILE=1024:{{HIGH_RLIMIT_NOFILE}} #DefaultLimitAS= #DefaultLimitNPROC= -#DefaultLimitMEMLOCK= +#DefaultLimitMEMLOCK=8M #DefaultLimitLOCKS= #DefaultLimitSIGPENDING= #DefaultLimitMSGQUEUE= diff --git a/src/core/timer.c b/src/core/timer.c index a13b86474..ab107860a 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -83,7 +83,6 @@ static int timer_verify(Timer *t) { static int timer_add_default_dependencies(Timer *t) { int r; - TimerValue *v; assert(t); @@ -100,8 +99,6 @@ static int timer_add_default_dependencies(Timer *t) { return r; LIST_FOREACH(value, v, t->values) { - const char *target; - if (v->base != TIMER_CALENDAR) continue; @@ -236,7 +233,6 @@ static int timer_load(Unit *u) { static void timer_dump(Unit *u, FILE *f, const char *prefix) { Timer *t = TIMER(u); Unit *trigger; - TimerValue *v; trigger = UNIT_TRIGGER(u); @@ -375,7 +371,6 @@ static void timer_enter_waiting(Timer *t, bool time_change) { bool found_monotonic = false, found_realtime = false; bool leave_around = false; triple_timestamp ts; - TimerValue *v; Unit *trigger; int r; @@ -617,7 +612,6 @@ fail: static int timer_start(Unit *u) { Timer *t = TIMER(u); - TimerValue *v; int r; assert(t); @@ -756,7 +750,6 @@ static int timer_dispatch(sd_event_source *s, uint64_t usec, void *userdata) { static void timer_trigger_notify(Unit *u, Unit *other) { Timer *t = TIMER(u); - TimerValue *v; assert(u); assert(other); diff --git a/src/core/timer.h b/src/core/timer.h index a51fbf56f..551e28334 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -64,7 +64,7 @@ struct Timer { char *stamp_path; }; -#define TIMER_MONOTONIC_CLOCK(t) ((t)->wake_system && clock_boottime_supported() ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC) +#define TIMER_MONOTONIC_CLOCK(t) ((t)->wake_system ? CLOCK_BOOTTIME_ALARM : CLOCK_MONOTONIC) void timer_free_values(Timer *t); diff --git a/src/core/transaction.c b/src/core/transaction.c index ebe5f1910..7bb8dfb0f 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -46,7 +46,7 @@ void transaction_abort(Transaction *tr) { } static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) { - JobDependency *l; + assert(j); /* A recursive sweep through the graph that marks all units * that matter to the anchor job, i.e. are directly or @@ -71,7 +71,7 @@ static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generat } static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) { - JobDependency *l, *last; + JobDependency *last; assert(j); assert(other); @@ -124,8 +124,6 @@ static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other } _pure_ static bool job_is_conflicted_by(Job *j) { - JobDependency *l; - assert(j); /* Returns true if this job is pulled in by a least one @@ -138,10 +136,8 @@ _pure_ static bool job_is_conflicted_by(Job *j) { return false; } -static int delete_one_unmergeable_job(Transaction *tr, Job *j) { - Job *k; - - assert(j); +static int delete_one_unmergeable_job(Transaction *tr, Job *job) { + assert(job); /* Tries to delete one item in the linked list * j->transaction_next->transaction_next->... that conflicts @@ -150,7 +146,7 @@ static int delete_one_unmergeable_job(Transaction *tr, Job *j) { /* We rely here on the fact that if a merged with b does not * merge with c, either a or b merge with c neither */ - LIST_FOREACH(transaction, j, j) + LIST_FOREACH(transaction, j, job) LIST_FOREACH(transaction, k, j->transaction_next) { Job *d; @@ -226,7 +222,6 @@ static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) { * task conflict. If so, try to drop one of them. */ HASHMAP_FOREACH(j, tr->jobs) { JobType t; - Job *k; t = j->type; LIST_FOREACH(transaction, k, j->transaction_next) { @@ -257,12 +252,12 @@ static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) { /* Second step, merge the jobs. */ HASHMAP_FOREACH(j, tr->jobs) { JobType t = j->type; - Job *k; /* Merge all transaction jobs for j->unit */ LIST_FOREACH(transaction, k, j->transaction_next) assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0); + Job *k; while ((k = j->transaction_next)) { if (tr->anchor_job == k) { transaction_merge_and_delete_job(tr, k, j, t); @@ -293,7 +288,6 @@ static void transaction_drop_redundant(Transaction *tr) { HASHMAP_FOREACH(j, tr->jobs) { bool keep = false; - Job *k; LIST_FOREACH(transaction, k, j) if (tr->anchor_job == k || @@ -314,14 +308,15 @@ static void transaction_drop_redundant(Transaction *tr) { } while (again); } -_pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) { +_pure_ static bool unit_matters_to_anchor(Unit *u, Job *job) { assert(u); - assert(!j->transaction_prev); + assert(job); + assert(!job->transaction_prev); /* Checks whether at least one of the jobs for this unit * matters to the anchor. */ - LIST_FOREACH(transaction, j, j) + LIST_FOREACH(transaction, j, job) if (j->matters_to_anchor) return true; @@ -329,7 +324,7 @@ _pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) { } static char* merge_unit_ids(const char* unit_log_field, char **pairs) { - char **unit_id, **job_type, *ans = NULL; + char *ans = NULL; size_t size = 0, next; STRV_FOREACH_PAIR(unit_id, job_type, pairs) { @@ -366,7 +361,6 @@ static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsi if (j->generation == generation) { Job *k, *delete = NULL; _cleanup_free_ char **array = NULL, *unit_ids = NULL; - char **unit_id, **job_type; /* If the marker is NULL we have been here already and decided the job was loop-free from * here. Hence shortcut things and return right-away. */ @@ -558,7 +552,7 @@ static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_erro } static void transaction_minimize_impact(Transaction *tr) { - Job *j; + Job *head; assert(tr); @@ -566,8 +560,8 @@ static void transaction_minimize_impact(Transaction *tr) { * or that stop a running service. */ rescan: - HASHMAP_FOREACH(j, tr->jobs) { - LIST_FOREACH(transaction, j, j) { + HASHMAP_FOREACH(head, tr->jobs) { + LIST_FOREACH(transaction, j, head) { bool stops_running_service, changes_existing_job; /* If it matters, we shouldn't drop it */ @@ -804,13 +798,13 @@ static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, b f = hashmap_get(tr->jobs, unit); - LIST_FOREACH(transaction, j, f) { - assert(j->unit == unit); + LIST_FOREACH(transaction, i, f) { + assert(i->unit == unit); - if (j->type == type) { + if (i->type == type) { if (is_new) *is_new = false; - return j; + return i; } } diff --git a/src/core/unit-dependency-atom.c b/src/core/unit-dependency-atom.c index 761835828..81333523e 100644 --- a/src/core/unit-dependency-atom.c +++ b/src/core/unit-dependency-atom.c @@ -83,10 +83,12 @@ static const UnitDependencyAtom atom_map[_UNIT_DEPENDENCY_MAX] = { UNIT_ATOM_PROPAGATE_STOP, /* These are simple dependency types: they consist of a single atom only */ + [UNIT_ON_FAILURE] = UNIT_ATOM_ON_FAILURE, + [UNIT_ON_SUCCESS] = UNIT_ATOM_ON_SUCCESS, + [UNIT_ON_FAILURE_OF] = UNIT_ATOM_ON_FAILURE_OF, + [UNIT_ON_SUCCESS_OF] = UNIT_ATOM_ON_SUCCESS_OF, [UNIT_BEFORE] = UNIT_ATOM_BEFORE, [UNIT_AFTER] = UNIT_ATOM_AFTER, - [UNIT_ON_SUCCESS] = UNIT_ATOM_ON_SUCCESS, - [UNIT_ON_FAILURE] = UNIT_ATOM_ON_FAILURE, [UNIT_TRIGGERS] = UNIT_ATOM_TRIGGERS, [UNIT_TRIGGERED_BY] = UNIT_ATOM_TRIGGERED_BY, [UNIT_PROPAGATES_RELOAD_TO] = UNIT_ATOM_PROPAGATES_RELOAD_TO, @@ -100,8 +102,6 @@ static const UnitDependencyAtom atom_map[_UNIT_DEPENDENCY_MAX] = { * things discoverable/debuggable as they are the inverse dependencies to some of the above. As they * have no effect of their own, they all map to no atoms at all, i.e. the value 0. */ [UNIT_RELOAD_PROPAGATED_FROM] = 0, - [UNIT_ON_SUCCESS_OF] = 0, - [UNIT_ON_FAILURE_OF] = 0, [UNIT_STOP_PROPAGATED_FROM] = 0, }; @@ -198,18 +198,24 @@ UnitDependency unit_dependency_from_unique_atom(UnitDependencyAtom atom) { /* And now, the simple ones */ + case UNIT_ATOM_ON_FAILURE: + return UNIT_ON_FAILURE; + + case UNIT_ATOM_ON_SUCCESS: + return UNIT_ON_SUCCESS; + + case UNIT_ATOM_ON_SUCCESS_OF: + return UNIT_ON_SUCCESS_OF; + + case UNIT_ATOM_ON_FAILURE_OF: + return UNIT_ON_FAILURE_OF; + case UNIT_ATOM_BEFORE: return UNIT_BEFORE; case UNIT_ATOM_AFTER: return UNIT_AFTER; - case UNIT_ATOM_ON_SUCCESS: - return UNIT_ON_SUCCESS; - - case UNIT_ATOM_ON_FAILURE: - return UNIT_ON_FAILURE; - case UNIT_ATOM_TRIGGERS: return UNIT_TRIGGERS; diff --git a/src/core/unit-dependency-atom.h b/src/core/unit-dependency-atom.h index c5f8f5d40..02532e57d 100644 --- a/src/core/unit-dependency-atom.h +++ b/src/core/unit-dependency-atom.h @@ -67,19 +67,21 @@ typedef enum UnitDependencyAtom { UNIT_ATOM_DEFAULT_TARGET_DEPENDENCIES = UINT64_C(1) << 21, /* The remaining atoms map 1:1 to the equally named high-level deps */ - UNIT_ATOM_BEFORE = UINT64_C(1) << 22, - UNIT_ATOM_AFTER = UINT64_C(1) << 23, - UNIT_ATOM_ON_SUCCESS = UINT64_C(1) << 24, - UNIT_ATOM_ON_FAILURE = UINT64_C(1) << 25, - UNIT_ATOM_TRIGGERS = UINT64_C(1) << 26, - UNIT_ATOM_TRIGGERED_BY = UINT64_C(1) << 27, - UNIT_ATOM_PROPAGATES_RELOAD_TO = UINT64_C(1) << 28, - UNIT_ATOM_JOINS_NAMESPACE_OF = UINT64_C(1) << 29, - UNIT_ATOM_REFERENCES = UINT64_C(1) << 30, - UNIT_ATOM_REFERENCED_BY = UINT64_C(1) << 31, - UNIT_ATOM_IN_SLICE = UINT64_C(1) << 32, - UNIT_ATOM_SLICE_OF = UINT64_C(1) << 33, - _UNIT_DEPENDENCY_ATOM_MAX = (UINT64_C(1) << 34) - 1, + UNIT_ATOM_ON_FAILURE = UINT64_C(1) << 22, + UNIT_ATOM_ON_SUCCESS = UINT64_C(1) << 23, + UNIT_ATOM_ON_FAILURE_OF = UINT64_C(1) << 24, + UNIT_ATOM_ON_SUCCESS_OF = UINT64_C(1) << 25, + UNIT_ATOM_BEFORE = UINT64_C(1) << 26, + UNIT_ATOM_AFTER = UINT64_C(1) << 27, + UNIT_ATOM_TRIGGERS = UINT64_C(1) << 28, + UNIT_ATOM_TRIGGERED_BY = UINT64_C(1) << 29, + UNIT_ATOM_PROPAGATES_RELOAD_TO = UINT64_C(1) << 30, + UNIT_ATOM_JOINS_NAMESPACE_OF = UINT64_C(1) << 31, + UNIT_ATOM_REFERENCES = UINT64_C(1) << 32, + UNIT_ATOM_REFERENCED_BY = UINT64_C(1) << 33, + UNIT_ATOM_IN_SLICE = UINT64_C(1) << 34, + UNIT_ATOM_SLICE_OF = UINT64_C(1) << 35, + _UNIT_DEPENDENCY_ATOM_MAX = (UINT64_C(1) << 36) - 1, _UNIT_DEPENDENCY_ATOM_INVALID = -EINVAL, } UnitDependencyAtom; diff --git a/src/core/unit-printf.c b/src/core/unit-printf.c index 46c383b84..0cdfcd6ef 100644 --- a/src/core/unit-printf.c +++ b/src/core/unit-printf.c @@ -13,28 +13,22 @@ #include "user-util.h" static int specifier_prefix_and_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const Unit *u = userdata; - - assert(u); + const Unit *u = ASSERT_PTR(userdata); return unit_name_to_prefix_and_instance(u->id, ret); } static int specifier_prefix(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const Unit *u = userdata; - - assert(u); + const Unit *u = ASSERT_PTR(userdata); return unit_name_to_prefix(u->id, ret); } static int specifier_prefix_unescaped(char specifier, const void *data, const char *root, const void *userdata, char **ret) { _cleanup_free_ char *p = NULL; - const Unit *u = userdata; + const Unit *u = ASSERT_PTR(userdata); int r; - assert(u); - r = unit_name_to_prefix(u->id, &p); if (r < 0) return r; @@ -43,21 +37,17 @@ static int specifier_prefix_unescaped(char specifier, const void *data, const ch } static int specifier_instance_unescaped(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const Unit *u = userdata; - - assert(u); + const Unit *u = ASSERT_PTR(userdata); return unit_name_unescape(strempty(u->instance), ret); } static int specifier_last_component(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const Unit *u = userdata; + const Unit *u = ASSERT_PTR(userdata); _cleanup_free_ char *prefix = NULL; char *dash; int r; - assert(u); - r = unit_name_to_prefix(u->id, &prefix); if (r < 0) return r; @@ -82,9 +72,7 @@ static int specifier_last_component_unescaped(char specifier, const void *data, } static int specifier_filename(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const Unit *u = userdata; - - assert(u); + const Unit *u = ASSERT_PTR(userdata); if (u->instance) return unit_name_path_unescape(u->instance, ret); @@ -97,11 +85,9 @@ static void bad_specifier(const Unit *u, char specifier) { } static int specifier_cgroup(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const Unit *u = userdata; + const Unit *u = ASSERT_PTR(userdata); char *n; - assert(u); - bad_specifier(u, specifier); if (u->cgroup_path) @@ -116,11 +102,9 @@ static int specifier_cgroup(char specifier, const void *data, const char *root, } static int specifier_cgroup_root(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const Unit *u = userdata; + const Unit *u = ASSERT_PTR(userdata); char *n; - assert(u); - bad_specifier(u, specifier); n = strdup(u->manager->cgroup_root); @@ -132,11 +116,9 @@ static int specifier_cgroup_root(char specifier, const void *data, const char *r } static int specifier_cgroup_slice(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const Unit *u = userdata, *slice; + const Unit *u = ASSERT_PTR(userdata), *slice; char *n; - assert(u); - bad_specifier(u, specifier); slice = UNIT_GET_SLICE(u); @@ -155,10 +137,8 @@ static int specifier_cgroup_slice(char specifier, const void *data, const char * } static int specifier_special_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const Unit *u = userdata; - char *n = NULL; - - assert(u); + const Unit *u = ASSERT_PTR(userdata); + char *n; n = strdup(u->manager->prefix[PTR_TO_UINT(data)]); if (!n) @@ -168,8 +148,21 @@ static int specifier_special_directory(char specifier, const void *data, const c return 0; } -int unit_name_printf(const Unit *u, const char* format, char **ret) { +static int specifier_credentials_dir(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + const Unit *u = ASSERT_PTR(userdata); + char *d; + assert(ret); + + d = strjoin(u->manager->prefix[EXEC_DIRECTORY_RUNTIME], "/credentials/", u->id); + if (!d) + return -ENOMEM; + + *ret = d; + return 0; +} + +int unit_name_printf(const Unit *u, const char* format, char **ret) { /* * This will use the passed string as format string and replace the following specifiers (which should all be * safe for inclusion in unit names): @@ -190,7 +183,7 @@ int unit_name_printf(const Unit *u, const char* format, char **ret) { COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(u->manager->unit_file_scope), {} }; @@ -212,6 +205,7 @@ int unit_full_printf_full(const Unit *u, const char *format, size_t max_length, * %R: the root of this systemd's instance tree (deprecated) * * %C: the cache directory root (e.g. /var/cache or $XDG_CACHE_HOME) + * %d: the credentials directory ($CREDENTIALS_DIRECTORY) * %E: the configuration directory root (e.g. /etc or $XDG_CONFIG_HOME) * %L: the log directory root (e.g. /var/log or $XDG_CONFIG_HOME/log) * %S: the state directory root (e.g. /var/lib or $XDG_CONFIG_HOME) @@ -240,12 +234,15 @@ int unit_full_printf_full(const Unit *u, const char *format, size_t max_length, { 'P', specifier_prefix_unescaped, NULL }, { 'f', specifier_filename, NULL }, + { 'y', specifier_real_path, u->fragment_path }, + { 'Y', specifier_real_directory, u->fragment_path }, { 'c', specifier_cgroup, NULL }, { 'r', specifier_cgroup_slice, NULL }, { 'R', specifier_cgroup_root, NULL }, { 'C', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_CACHE) }, + { 'd', specifier_credentials_dir, NULL }, { 'E', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_CONFIGURATION) }, { 'L', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_LOGS) }, { 'S', specifier_special_directory, UINT_TO_PTR(EXEC_DIRECTORY_STATE) }, @@ -256,7 +253,7 @@ int unit_full_printf_full(const Unit *u, const char *format, size_t max_length, COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(u->manager->unit_file_scope), COMMON_TMP_SPECIFIERS, {} diff --git a/src/core/unit-serialize.c b/src/core/unit-serialize.c index 7d2e6bc13..65ed110ce 100644 --- a/src/core/unit-serialize.c +++ b/src/core/unit-serialize.c @@ -623,7 +623,7 @@ static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependency } void unit_dump(Unit *u, FILE *f, const char *prefix) { - char *t, **j; + char *t; const char *prefix2; Unit *following; _cleanup_set_free_ Set *following_set = NULL; diff --git a/src/core/unit.c b/src/core/unit.c index af6cf097f..5ab7601ed 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -375,6 +375,20 @@ int unit_set_description(Unit *u, const char *description) { return 0; } +static bool unit_success_failure_handler_has_jobs(Unit *unit) { + Unit *other; + + UNIT_FOREACH_DEPENDENCY(other, unit, UNIT_ATOM_ON_SUCCESS) + if (other->job || other->nop_job) + return true; + + UNIT_FOREACH_DEPENDENCY(other, unit, UNIT_ATOM_ON_FAILURE) + if (other->job || other->nop_job) + return true; + + return false; +} + bool unit_may_gc(Unit *u) { UnitActiveState state; int r; @@ -389,10 +403,7 @@ bool unit_may_gc(Unit *u) { * in unit_gc_sweep(), but using markers to properly collect dependency loops. */ - if (u->job) - return false; - - if (u->nop_job) + if (u->job || u->nop_job) return false; state = unit_active_state(u); @@ -427,6 +438,10 @@ bool unit_may_gc(Unit *u) { assert_not_reached(); } + /* Check if any OnFailure= or on Success= jobs may be pending */ + if (unit_success_failure_handler_has_jobs(u)) + return false; + if (u->cgroup_path) { /* If the unit has a cgroup, then check whether there's anything in it. If so, we should stay * around. Units with active processes should never be collected. */ @@ -568,8 +583,6 @@ static void unit_clear_dependencies(Unit *u) { } static void unit_remove_transient(Unit *u) { - char **i; - assert(u); if (!u->transient) @@ -2210,7 +2223,7 @@ void unit_start_on_failure( UnitDependencyAtom atom, JobMode job_mode) { - bool logged = false; + int n_jobs = -1; Unit *other; int r; @@ -2223,9 +2236,9 @@ void unit_start_on_failure( UNIT_FOREACH_DEPENDENCY(other, u, atom) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - if (!logged) { + if (n_jobs < 0) { log_unit_info(u, "Triggering %s dependencies.", dependency_name); - logged = true; + n_jobs = 0; } r = manager_add_job(u->manager, JOB_START, other, job_mode, NULL, &error, NULL); @@ -2233,10 +2246,12 @@ void unit_start_on_failure( log_unit_warning_errno( u, r, "Failed to enqueue %s job, ignoring: %s", dependency_name, bus_error_message(&error, r)); + n_jobs ++; } - if (logged) - log_unit_debug(u, "Triggering %s dependencies done.", dependency_name); + if (n_jobs >= 0) + log_unit_debug(u, "Triggering %s dependencies done (%u %s).", + dependency_name, n_jobs, n_jobs == 1 ? "job" : "jobs"); } void unit_trigger_notify(Unit *u) { @@ -3009,37 +3024,37 @@ int unit_add_dependency( UnitDependencyMask mask) { static const UnitDependency inverse_table[_UNIT_DEPENDENCY_MAX] = { - [UNIT_REQUIRES] = UNIT_REQUIRED_BY, - [UNIT_REQUISITE] = UNIT_REQUISITE_OF, - [UNIT_WANTS] = UNIT_WANTED_BY, - [UNIT_BINDS_TO] = UNIT_BOUND_BY, - [UNIT_PART_OF] = UNIT_CONSISTS_OF, - [UNIT_UPHOLDS] = UNIT_UPHELD_BY, - [UNIT_REQUIRED_BY] = UNIT_REQUIRES, - [UNIT_REQUISITE_OF] = UNIT_REQUISITE, - [UNIT_WANTED_BY] = UNIT_WANTS, - [UNIT_BOUND_BY] = UNIT_BINDS_TO, - [UNIT_CONSISTS_OF] = UNIT_PART_OF, - [UNIT_UPHELD_BY] = UNIT_UPHOLDS, - [UNIT_CONFLICTS] = UNIT_CONFLICTED_BY, - [UNIT_CONFLICTED_BY] = UNIT_CONFLICTS, - [UNIT_BEFORE] = UNIT_AFTER, - [UNIT_AFTER] = UNIT_BEFORE, - [UNIT_ON_SUCCESS] = UNIT_ON_SUCCESS_OF, - [UNIT_ON_SUCCESS_OF] = UNIT_ON_SUCCESS, - [UNIT_ON_FAILURE] = UNIT_ON_FAILURE_OF, - [UNIT_ON_FAILURE_OF] = UNIT_ON_FAILURE, - [UNIT_TRIGGERS] = UNIT_TRIGGERED_BY, - [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS, - [UNIT_PROPAGATES_RELOAD_TO] = UNIT_RELOAD_PROPAGATED_FROM, + [UNIT_REQUIRES] = UNIT_REQUIRED_BY, + [UNIT_REQUISITE] = UNIT_REQUISITE_OF, + [UNIT_WANTS] = UNIT_WANTED_BY, + [UNIT_BINDS_TO] = UNIT_BOUND_BY, + [UNIT_PART_OF] = UNIT_CONSISTS_OF, + [UNIT_UPHOLDS] = UNIT_UPHELD_BY, + [UNIT_REQUIRED_BY] = UNIT_REQUIRES, + [UNIT_REQUISITE_OF] = UNIT_REQUISITE, + [UNIT_WANTED_BY] = UNIT_WANTS, + [UNIT_BOUND_BY] = UNIT_BINDS_TO, + [UNIT_CONSISTS_OF] = UNIT_PART_OF, + [UNIT_UPHELD_BY] = UNIT_UPHOLDS, + [UNIT_CONFLICTS] = UNIT_CONFLICTED_BY, + [UNIT_CONFLICTED_BY] = UNIT_CONFLICTS, + [UNIT_BEFORE] = UNIT_AFTER, + [UNIT_AFTER] = UNIT_BEFORE, + [UNIT_ON_SUCCESS] = UNIT_ON_SUCCESS_OF, + [UNIT_ON_SUCCESS_OF] = UNIT_ON_SUCCESS, + [UNIT_ON_FAILURE] = UNIT_ON_FAILURE_OF, + [UNIT_ON_FAILURE_OF] = UNIT_ON_FAILURE, + [UNIT_TRIGGERS] = UNIT_TRIGGERED_BY, + [UNIT_TRIGGERED_BY] = UNIT_TRIGGERS, + [UNIT_PROPAGATES_RELOAD_TO] = UNIT_RELOAD_PROPAGATED_FROM, [UNIT_RELOAD_PROPAGATED_FROM] = UNIT_PROPAGATES_RELOAD_TO, - [UNIT_PROPAGATES_STOP_TO] = UNIT_STOP_PROPAGATED_FROM, - [UNIT_STOP_PROPAGATED_FROM] = UNIT_PROPAGATES_STOP_TO, - [UNIT_JOINS_NAMESPACE_OF] = UNIT_JOINS_NAMESPACE_OF, /* symmetric! 👓 */ - [UNIT_REFERENCES] = UNIT_REFERENCED_BY, - [UNIT_REFERENCED_BY] = UNIT_REFERENCES, - [UNIT_IN_SLICE] = UNIT_SLICE_OF, - [UNIT_SLICE_OF] = UNIT_IN_SLICE, + [UNIT_PROPAGATES_STOP_TO] = UNIT_STOP_PROPAGATED_FROM, + [UNIT_STOP_PROPAGATED_FROM] = UNIT_PROPAGATES_STOP_TO, + [UNIT_JOINS_NAMESPACE_OF] = UNIT_JOINS_NAMESPACE_OF, /* symmetric! 👓 */ + [UNIT_REFERENCES] = UNIT_REFERENCED_BY, + [UNIT_REFERENCED_BY] = UNIT_REFERENCES, + [UNIT_IN_SLICE] = UNIT_SLICE_OF, + [UNIT_SLICE_OF] = UNIT_IN_SLICE, }; Unit *original_u = u, *original_other = other; UnitDependencyAtom a; @@ -3603,7 +3618,6 @@ int unit_add_blockdev_dependency(Unit *u, const char *what, UnitDependencyMask m int unit_coldplug(Unit *u) { int r = 0, q; - char **i; assert(u); @@ -3676,7 +3690,6 @@ static bool fragment_mtime_newer(const char *path, usec_t mtime, bool path_maske bool unit_need_daemon_reload(Unit *u) { _cleanup_strv_free_ char **t = NULL; - char **path; assert(u); @@ -3788,6 +3801,13 @@ int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) { return UNIT_VTABLE(u)->kill(u, w, signo, error); } +void unit_notify_cgroup_oom(Unit *u, bool managed_oom) { + assert(u); + + if (UNIT_VTABLE(u)->notify_cgroup_oom) + UNIT_VTABLE(u)->notify_cgroup_oom(u, managed_oom); +} + static Set *unit_pid_set(pid_t main_pid, pid_t control_pid) { _cleanup_set_free_ Set *pid_set = NULL; int r; @@ -4113,7 +4133,6 @@ int unit_patch_contexts(Unit *u) { if ((ec->root_image || !LIST_IS_EMPTY(ec->mount_images)) && (cc->device_policy != CGROUP_DEVICE_POLICY_AUTO || cc->device_allow)) { - const char *p; /* When RootImage= or MountImages= is specified, the following devices are touched. */ FOREACH_STRING(p, "/dev/loop-control", "/dev/mapper/control") { @@ -4261,7 +4280,6 @@ char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf) { char* unit_concat_strv(char **l, UnitWriteFlags flags) { _cleanup_free_ char *result = NULL; size_t n = 0; - char **i; /* Takes a list of strings, escapes them, and concatenates them. This may be used to format command lines in a * way suitable for ExecStart= stanzas */ @@ -5062,7 +5080,6 @@ int unit_fork_and_watch_rm_rf(Unit *u, char **paths, pid_t *ret_pid) { return r; if (r == 0) { int ret = EXIT_SUCCESS; - char **i; STRV_FOREACH(i, paths) { r = rm_rf(*i, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_MISSING_OK); @@ -5852,7 +5869,7 @@ int unit_thaw_vtable_common(Unit *u) { } Condition *unit_find_failed_condition(Unit *u) { - Condition *c, *failed_trigger = NULL; + Condition *failed_trigger = NULL; bool has_succeeded_trigger = false; if (u->condition_result) diff --git a/src/core/unit.h b/src/core/unit.h index 94f218095..733eeecd7 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -285,7 +285,7 @@ typedef struct Unit { nsec_t cpu_usage_base; nsec_t cpu_usage_last; /* the most recently read value */ - /* The current counter of processes sent SIGKILL by systemd-oomd */ + /* The current counter of OOM kills initiated by systemd-oomd */ uint64_t managed_oom_kill_last; /* The current counter of the oom_kill field in the memory.events cgroup attribute */ @@ -596,7 +596,7 @@ typedef struct UnitVTable { void (*notify_cgroup_empty)(Unit *u); /* Called whenever an OOM kill event on this unit was seen */ - void (*notify_cgroup_oom)(Unit *u); + void (*notify_cgroup_oom)(Unit *u, bool managed_oom); /* Called whenever a process of this unit sends us a message */ void (*notify_message)(Unit *u, const struct ucred *ucred, char * const *tags, FDSet *fds); @@ -811,6 +811,8 @@ int unit_reload(Unit *u); int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error); int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t control_pid, sd_bus_error *error); +void unit_notify_cgroup_oom(Unit *u, bool managed_oom); + typedef enum UnitNotifyFlags { UNIT_NOTIFY_RELOAD_FAILURE = 1 << 0, UNIT_NOTIFY_WILL_AUTO_RESTART = 1 << 1, diff --git a/src/coredump/coredump-vacuum.c b/src/coredump/coredump-vacuum.c index dcf9cc03c..c6e201ecf 100644 --- a/src/coredump/coredump-vacuum.c +++ b/src/coredump/coredump-vacuum.c @@ -13,6 +13,7 @@ #include "hashmap.h" #include "macro.h" #include "memory-util.h" +#include "stat-util.h" #include "string-util.h" #include "time-util.h" #include "user-util.h" @@ -167,9 +168,7 @@ int coredump_vacuum(int exclude_fd, uint64_t keep_free, uint64_t max_use) { if (!S_ISREG(st.st_mode)) continue; - if (exclude_fd >= 0 && - exclude_st.st_dev == st.st_dev && - exclude_st.st_ino == st.st_ino) + if (exclude_fd >= 0 && stat_inode_same(&exclude_st, &st)) continue; r = hashmap_ensure_allocated(&h, NULL); diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index 6a6e9765d..6055b91ac 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -48,8 +48,14 @@ #include "uid-alloc-range.h" #include "user-util.h" -/* The maximum size up to which we process coredumps */ -#define PROCESS_SIZE_MAX ((uint64_t) (2LLU*1024LLU*1024LLU*1024LLU)) +/* The maximum size up to which we process coredumps. We use 1G on 32bit systems, and 32G on 64bit systems */ +#if __SIZEOF_POINTER__ == 4 +#define PROCESS_SIZE_MAX ((uint64_t) (1LLU*1024LLU*1024LLU*1024LLU)) +#elif __SIZEOF_POINTER__ == 8 +#define PROCESS_SIZE_MAX ((uint64_t) (32LLU*1024LLU*1024LLU*1024LLU)) +#else +#error "Unexpected pointer size" +#endif /* The maximum size up to which we leave the coredump around on disk */ #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX @@ -701,10 +707,10 @@ static int get_mount_namespace_leader(pid_t pid, pid_t *ret) { * Returns a negative number on errors. */ static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) { - int r = 0; pid_t container_pid; const char *proc_root_path; struct stat root_stat, proc_root_stat; + int r; /* To compare inodes of / and /proc/[pid]/root */ if (stat("/", &root_stat) < 0) @@ -715,7 +721,7 @@ static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) { return -errno; /* The process uses system root. */ - if (proc_root_stat.st_ino == root_stat.st_ino) { + if (stat_inode_same(&proc_root_stat, &root_stat)) { *cmdline = NULL; return 0; } @@ -772,6 +778,7 @@ static int submit_coredump( bool truncated = false; JsonVariant *module_json; int r; + assert(context); assert(iovw); assert(input_fd >= 0); @@ -793,10 +800,9 @@ static int submit_coredump( r = maybe_remove_external_coredump(filename, coredump_node_fd >= 0 ? coredump_compressed_size : coredump_size); if (r < 0) return r; - if (r == 0) { + if (r == 0) (void) iovw_put_string_field(iovw, "COREDUMP_FILENAME=", filename); - - } else if (arg_storage == COREDUMP_STORAGE_EXTERNAL) + else if (arg_storage == COREDUMP_STORAGE_EXTERNAL) log_info("The core will not be stored: size %"PRIu64" is greater than %"PRIu64" (the configured maximum)", coredump_node_fd >= 0 ? coredump_compressed_size : coredump_size, arg_external_size_max); diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index 2904de372..723554428 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -91,7 +91,6 @@ static int add_match(sd_journal *j, const char *match) { } static int add_matches(sd_journal *j, char **matches) { - char **match; int r; r = sd_journal_add_match(j, "MESSAGE_ID=" SD_MESSAGE_COREDUMP_STR, 0); diff --git a/src/coredump/meson.build b/src/coredump/meson.build index 22a8837e2..ff509f9e2 100644 --- a/src/coredump/meson.build +++ b/src/coredump/meson.build @@ -13,8 +13,8 @@ if conf.get('ENABLE_COREDUMP') == 1 and install_sysconfdir_samples endif tests += [ - [['src/coredump/test-coredump-vacuum.c', - 'src/coredump/coredump-vacuum.c', - 'src/coredump/coredump-vacuum.h'], + [files('test-coredump-vacuum.c', + 'coredump-vacuum.c', + 'coredump-vacuum.h'), [], [], [], '', 'manual'], ] diff --git a/src/creds/creds.c b/src/creds/creds.c index 3d2841c07..2fd314ff3 100644 --- a/src/creds/creds.c +++ b/src/creds/creds.c @@ -311,7 +311,6 @@ static int write_blob(FILE *f, const void *data, size_t size) { static int verb_cat(int argc, char **argv, void *userdata) { _cleanup_(closedirp) DIR *d = NULL; int r, ret = 0; - char **cn; r = open_credential_directory(&d); if (r == -ENXIO) diff --git a/src/cryptenroll/cryptenroll-password.c b/src/cryptenroll/cryptenroll-password.c index 1775912d8..9b7c8b540 100644 --- a/src/cryptenroll/cryptenroll-password.c +++ b/src/cryptenroll/cryptenroll-password.c @@ -17,20 +17,13 @@ int enroll_password( _cleanup_free_ char *error = NULL; const char *node; int r, keyslot; - char *e; assert_se(node = crypt_get_device_name(cd)); - e = getenv("NEWPASSWORD"); - if (e) { - - new_password = strdup(e); - if (!new_password) - return log_oom(); - - assert_se(unsetenv_erase("NEWPASSWORD") >= 0); - - } else { + r = getenv_steal_erase("NEWPASSWORD", &new_password); + if (r < 0) + return log_error_errno(r, "Failed to acquire password from environment: %m"); + if (r == 0) { _cleanup_free_ char *disk_path = NULL; unsigned i = 5; const char *id; diff --git a/src/cryptenroll/cryptenroll-tpm2.c b/src/cryptenroll/cryptenroll-tpm2.c index 801014af1..e8c64dd75 100644 --- a/src/cryptenroll/cryptenroll-tpm2.c +++ b/src/cryptenroll/cryptenroll-tpm2.c @@ -1,7 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" +#include "ask-password-api.h" #include "cryptenroll-tpm2.h" +#include "env-util.h" #include "hexdecoct.h" #include "json.h" #include "memory-util.h" @@ -58,11 +60,78 @@ static int search_policy_hash( return -ENOENT; /* Not found */ } +static int get_pin(char **ret_pin_str, TPM2Flags *ret_flags) { + _cleanup_free_ char *pin_str = NULL; + int r; + TPM2Flags flags = 0; + + assert(ret_pin_str); + assert(ret_flags); + + r = getenv_steal_erase("NEWPIN", &pin_str); + if (r < 0) + return log_error_errno(r, "Failed to acquire PIN from environment: %m"); + if (r > 0) + flags |= TPM2_FLAGS_USE_PIN; + else { + for (size_t i = 5;; i--) { + _cleanup_strv_free_erase_ char **pin = NULL, **pin2 = NULL; + + if (i <= 0) + return log_error_errno( + SYNTHETIC_ERRNO(ENOKEY), "Too many attempts, giving up."); + + pin = strv_free_erase(pin); + r = ask_password_auto( + "Please enter TPM2 PIN:", + "drive-harddisk", + NULL, + "tpm2-pin", + "cryptenroll.tpm2-pin", + USEC_INFINITY, + 0, + &pin); + if (r < 0) + return log_error_errno(r, "Failed to ask for user pin: %m"); + assert(strv_length(pin) == 1); + + r = ask_password_auto( + "Please enter TPM2 PIN (repeat):", + "drive-harddisk", + NULL, + "tpm2-pin", + "cryptenroll.tpm2-pin", + USEC_INFINITY, + 0, + &pin2); + if (r < 0) + return log_error_errno(r, "Failed to ask for user pin: %m"); + assert(strv_length(pin) == 1); + + if (strv_equal(pin, pin2)) { + pin_str = strdup(*pin); + if (!pin_str) + return log_oom(); + flags |= TPM2_FLAGS_USE_PIN; + break; + } + + log_error("PINs didn't match, please try again!"); + } + } + + *ret_flags = flags; + *ret_pin_str = TAKE_PTR(pin_str); + + return 0; +} + int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, - uint32_t pcr_mask) { + uint32_t pcr_mask, + bool use_pin) { _cleanup_(erase_and_freep) void *secret = NULL, *secret2 = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; @@ -71,7 +140,9 @@ int enroll_tpm2(struct crypt_device *cd, _cleanup_free_ void *blob = NULL, *hash = NULL; uint16_t pcr_bank, primary_alg; const char *node; + _cleanup_(erase_and_freep) char *pin_str = NULL; int r, keyslot; + TPM2Flags flags = 0; assert(cd); assert(volume_key); @@ -80,7 +151,13 @@ int enroll_tpm2(struct crypt_device *cd, assert_se(node = crypt_get_device_name(cd)); - r = tpm2_seal(device, pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg); + if (use_pin) { + r = get_pin(&pin_str, &flags); + if (r < 0) + return r; + } + + r = tpm2_seal(device, pcr_mask, pin_str, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg); if (r < 0) return r; @@ -97,7 +174,7 @@ int enroll_tpm2(struct crypt_device *cd, /* Quick verification that everything is in order, we are not in a hurry after all. */ log_debug("Unsealing for verification..."); - r = tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &secret2, &secret2_size); + r = tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, pin_str, &secret2, &secret2_size); if (r < 0) return r; @@ -123,7 +200,7 @@ int enroll_tpm2(struct crypt_device *cd, if (keyslot < 0) return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node); - r = tpm2_make_luks2_json(keyslot, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &v); + r = tpm2_make_luks2_json(keyslot, pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, flags, &v); if (r < 0) return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m"); diff --git a/src/cryptenroll/cryptenroll-tpm2.h b/src/cryptenroll/cryptenroll-tpm2.h index d5dd1b000..742f49b8d 100644 --- a/src/cryptenroll/cryptenroll-tpm2.h +++ b/src/cryptenroll/cryptenroll-tpm2.h @@ -7,9 +7,9 @@ #include "log.h" #if HAVE_TPM2 -int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t pcr_mask); +int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t pcr_mask, bool use_pin); #else -static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t pcr_mask) { +static inline int enroll_tpm2(struct crypt_device *cd, const void *volume_key, size_t volume_key_size, const char *device, uint32_t pcr_mask, bool use_pin) { return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 key enrollment not supported."); } diff --git a/src/cryptenroll/cryptenroll.c b/src/cryptenroll/cryptenroll.c index c9bc9a248..2e11ffe29 100644 --- a/src/cryptenroll/cryptenroll.c +++ b/src/cryptenroll/cryptenroll.c @@ -32,6 +32,7 @@ static char *arg_pkcs11_token_uri = NULL; static char *arg_fido2_device = NULL; static char *arg_tpm2_device = NULL; static uint32_t arg_tpm2_pcr_mask = UINT32_MAX; +static bool arg_tpm2_pin = false; static char *arg_node = NULL; static int *arg_wipe_slots = NULL; static size_t arg_n_wipe_slots = 0; @@ -100,6 +101,8 @@ static int help(void) { " Enroll a TPM2 device\n" " --tpm2-pcrs=PCR1+PCR2+PCR3+…\n" " Specify TPM2 PCRs to seal against\n" + " --tpm2-with-pin=BOOL\n" + " Whether to require entering a PIN to unlock the volume\n" " --wipe-slot=SLOT1,SLOT2,…\n" " Wipe specified slots\n" "\nSee the %s for details.\n", @@ -121,6 +124,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_FIDO2_DEVICE, ARG_TPM2_DEVICE, ARG_TPM2_PCRS, + ARG_TPM2_PIN, ARG_WIPE_SLOT, ARG_FIDO2_WITH_PIN, ARG_FIDO2_WITH_UP, @@ -139,6 +143,7 @@ static int parse_argv(int argc, char *argv[]) { { "fido2-with-user-verification", required_argument, NULL, ARG_FIDO2_WITH_UV }, { "tpm2-device", required_argument, NULL, ARG_TPM2_DEVICE }, { "tpm2-pcrs", required_argument, NULL, ARG_TPM2_PCRS }, + { "tpm2-with-pin", required_argument, NULL, ARG_TPM2_PIN }, { "wipe-slot", required_argument, NULL, ARG_WIPE_SLOT }, {} }; @@ -301,6 +306,14 @@ static int parse_argv(int argc, char *argv[]) { break; } + case ARG_TPM2_PIN: { + r = parse_boolean_argument("--tpm2-with-pin=", optarg, &arg_tpm2_pin); + if (r < 0) + return r; + + break; + } + case ARG_WIPE_SLOT: { const char *p = optarg; @@ -409,8 +422,8 @@ static int prepare_luks( size_t *ret_volume_key_size) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; + _cleanup_(erase_and_freep) char *envpw = NULL; _cleanup_(erase_and_freep) void *vk = NULL; - char *e = NULL; size_t vks; int r; @@ -445,23 +458,17 @@ static int prepare_luks( if (!vk) return log_oom(); - e = getenv("PASSWORD"); - if (e) { - _cleanup_(erase_and_freep) char *password = NULL; - - password = strdup(e); - if (!password) - return log_oom(); - - assert_se(unsetenv_erase("PASSWORD") >= 0); - + r = getenv_steal_erase("PASSWORD", &envpw); + if (r < 0) + return log_error_errno(r, "Failed to acquire password from environment: %m"); + if (r > 0) { r = crypt_volume_key_get( cd, CRYPT_ANY_SLOT, vk, &vks, - password, - strlen(password)); + envpw, + strlen(envpw)); if (r < 0) return log_error_errno(r, "Password from environment variable $PASSWORD did not work."); } else { @@ -482,7 +489,6 @@ static int prepare_luks( for (;;) { _cleanup_strv_free_erase_ char **passwords = NULL; - char **p; if (--i == 0) return log_error_errno(SYNTHETIC_ERRNO(ENOKEY), @@ -564,7 +570,7 @@ static int run(int argc, char *argv[]) { break; case ENROLL_TPM2: - slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_pcr_mask); + slot = enroll_tpm2(cd, vk, vks, arg_tpm2_device, arg_tpm2_pcr_mask, arg_tpm2_pin); break; case _ENROLL_TYPE_INVALID: diff --git a/src/cryptsetup/cryptsetup-fido2.c b/src/cryptsetup/cryptsetup-fido2.c index 35d5dbe00..74053b8ce 100644 --- a/src/cryptsetup/cryptsetup-fido2.c +++ b/src/cryptsetup/cryptsetup-fido2.c @@ -30,12 +30,12 @@ int acquire_fido2_key( size_t *ret_decrypted_key_size, AskPasswordFlags ask_password_flags) { + _cleanup_(erase_and_freep) char *envpw = NULL; _cleanup_strv_free_erase_ char **pins = NULL; _cleanup_free_ void *loaded_salt = NULL; bool device_exists = false; const char *salt; size_t salt_size; - char *e; int r; ask_password_flags |= ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED; @@ -66,13 +66,13 @@ int acquire_fido2_key( salt = loaded_salt; } - e = getenv("PIN"); - if (e) { - pins = strv_new(e); + r = getenv_steal_erase("PIN", &envpw); + if (r < 0) + return log_error_errno(r, "Failed to acquire password from environment: %m"); + if (r > 0) { + pins = strv_new(envpw); if (!pins) return log_oom(); - - assert_se(unsetenv_erase("PIN") >= 0); } for (;;) { diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 98c8408da..8f5ad67f4 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -277,7 +277,7 @@ static int print_dependencies(FILE *f, const char* device_path) { static int create_disk( const char *name, const char *device, - const char *password, + const char *key_file, const char *keydev, const char *headerdev, const char *options, @@ -285,7 +285,7 @@ static int create_disk( _cleanup_free_ char *n = NULL, *d = NULL, *u = NULL, *e = NULL, *keydev_mount = NULL, *keyfile_timeout_value = NULL, - *filtered = NULL, *u_escaped = NULL, *name_escaped = NULL, *header_path = NULL, *password_buffer = NULL, + *filtered = NULL, *u_escaped = NULL, *name_escaped = NULL, *header_path = NULL, *key_file_buffer = NULL, *tmp_fstype = NULL, *filtered_header = NULL, *headerdev_mount = NULL; _cleanup_fclose_ FILE *f = NULL; const char *dmname; @@ -350,9 +350,9 @@ static int create_disk( if (r < 0) return log_error_errno(r, "Failed to generate unit name: %m"); - if (keydev && !password) + if (keydev && !key_file) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Key device is specified, but path to the password file is missing."); + "Key device is specified, but path to the key file is missing."); r = generator_open_unit_file(arg_dest, NULL, n, &f); if (r < 0) @@ -388,11 +388,11 @@ static int create_disk( if (r < 0) return log_error_errno(r, "Failed to generate keydev umount unit: %m"); - password_buffer = path_join(keydev_mount, password); - if (!password_buffer) + key_file_buffer = path_join(keydev_mount, key_file); + if (!key_file_buffer) return log_oom(); - password = password_buffer; + key_file = key_file_buffer; fprintf(f, "After=%s\n", unit); if (keyfile_can_timeout > 0) @@ -462,8 +462,8 @@ static int create_disk( "Before=%s\n", netdev ? "remote-cryptsetup.target" : "cryptsetup.target"); - if (password && !keydev) { - r = print_dependencies(f, password); + if (key_file && !keydev) { + r = print_dependencies(f, key_file); if (r < 0) return r; } @@ -495,7 +495,7 @@ static int create_disk( if (r < 0) log_warning_errno(r, "Failed to write device timeout drop-in: %m"); - r = generator_write_cryptsetup_service_section(f, name, u, password, filtered); + r = generator_write_cryptsetup_service_section(f, name, u, key_file, filtered); if (r < 0) return r; diff --git a/src/cryptsetup/cryptsetup-keyfile.c b/src/cryptsetup/cryptsetup-keyfile.c index 924555d26..1867e9012 100644 --- a/src/cryptsetup/cryptsetup-keyfile.c +++ b/src/cryptsetup/cryptsetup-keyfile.c @@ -12,7 +12,6 @@ int find_key_file( void **ret_key, size_t *ret_key_size) { - char **i; int r; assert(key_file); diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c index e2d28a5dd..23df97499 100644 --- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c @@ -6,8 +6,10 @@ #include "cryptsetup-token.h" #include "cryptsetup-token-util.h" #include "hexdecoct.h" +#include "json.h" #include "luks2-tpm2.h" #include "memory-util.h" +#include "strv.h" #include "tpm2-util.h" #include "version.h" @@ -78,7 +80,8 @@ _public_ int cryptsetup_token_open( if (usrptr) params = *(systemd_tpm2_plugin_params *)usrptr; - r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash); + TPM2Flags flags = 0; + r = parse_luks2_tpm2_data(json, params.search_pcr_mask, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash, &flags); if (r < 0) return log_debug_open_error(cd, r); @@ -101,6 +104,7 @@ _public_ int cryptsetup_token_open( blob_size, policy_hash, policy_hash_size, + flags, &decrypted_key, &decrypted_key_size); if (r < 0) @@ -135,6 +139,7 @@ _public_ void cryptsetup_token_dump( const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) { int r; + TPM2Flags flags = 0; uint32_t pcr_mask; uint16_t pcr_bank, primary_alg; size_t decoded_blob_size; @@ -144,7 +149,7 @@ _public_ void cryptsetup_token_dump( assert(json); - r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash); + r = parse_luks2_tpm2_data(json, UINT32_MAX, &pcr_mask, &pcr_bank, &primary_alg, &base64_blob, &hex_policy_hash, &flags); if (r < 0) return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m."); @@ -171,6 +176,7 @@ _public_ void cryptsetup_token_dump( crypt_log(cd, "\ttpm2-primary-alg: %s\n", strna(tpm2_primary_alg_to_string(primary_alg))); crypt_log(cd, "\ttpm2-blob: %s\n", blob_str); crypt_log(cd, "\ttpm2-policy-hash:" CRYPT_DUMP_LINE_SEP "%s\n", policy_hash_str); + crypt_log(cd, "\ttpm2-pin: %s\n", true_false(flags & TPM2_FLAGS_USE_PIN)); } /* @@ -268,5 +274,13 @@ _public_ int cryptsetup_token_validate( if (r < 0) return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'tpm2-policy-hash' field: %m"); + w = json_variant_by_key(v, "tpm2-pin"); + if (w) { + if (!json_variant_is_boolean(w)) { + crypt_log_debug(cd, "TPM2 PIN policy is not a boolean."); + return 1; + } + } + return 0; } diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c index 3d39dfa88..0d6e4bc0f 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c +++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c @@ -1,11 +1,15 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" +#include "ask-password-api.h" +#include "env-util.h" #include "hexdecoct.h" #include "json.h" +#include "log.h" #include "luks2-tpm2.h" #include "parse-util.h" #include "random-util.h" +#include "strv.h" #include "tpm2-util.h" int acquire_luks2_key( @@ -17,10 +21,12 @@ int acquire_luks2_key( size_t key_data_size, const void *policy_hash, size_t policy_hash_size, + TPM2Flags flags, void **ret_decrypted_key, size_t *ret_decrypted_key_size) { _cleanup_free_ char *auto_device = NULL; + _cleanup_(erase_and_freep) char *pin_str = NULL; int r; assert(ret_decrypted_key); @@ -36,12 +42,22 @@ int acquire_luks2_key( device = auto_device; } + r = getenv_steal_erase("PIN", &pin_str); + if (r < 0) + return log_error_errno(r, "Failed to acquire PIN from environment: %m"); + if (!r) { + /* PIN entry is not supported by plugin, let it fallback, possibly to sd-cryptsetup's + * internal handling. */ + if (flags & TPM2_FLAGS_USE_PIN) + return -EOPNOTSUPP; + } + return tpm2_unseal( device, pcr_mask, pcr_bank, primary_alg, key_data, key_data_size, - policy_hash, policy_hash_size, + policy_hash, policy_hash_size, pin_str, ret_decrypted_key, ret_decrypted_key_size); } @@ -53,7 +69,8 @@ int parse_luks2_tpm2_data( uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, char **ret_base64_blob, - char **ret_hex_policy_hash) { + char **ret_hex_policy_hash, + TPM2Flags *ret_flags) { int r; JsonVariant *w, *e; @@ -61,6 +78,7 @@ int parse_luks2_tpm2_data( uint16_t pcr_bank = UINT16_MAX, primary_alg = TPM2_ALG_ECC; _cleanup_free_ char *base64_blob = NULL, *hex_policy_hash = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; + TPM2Flags flags = 0; assert(json); assert(ret_pcr_mask); @@ -138,11 +156,21 @@ int parse_luks2_tpm2_data( if (!hex_policy_hash) return -ENOMEM; + w = json_variant_by_key(v, "tpm2-pin"); + if (w) { + if (!json_variant_is_boolean(w)) + return -EINVAL; + + if (json_variant_boolean(w)) + flags |= TPM2_FLAGS_USE_PIN; + } + *ret_pcr_mask = pcr_mask; *ret_pcr_bank = pcr_bank; *ret_primary_alg = primary_alg; *ret_base64_blob = TAKE_PTR(base64_blob); *ret_hex_policy_hash = TAKE_PTR(hex_policy_hash); + *ret_flags = flags; return 0; } diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h index 0c93ea82c..34c93216e 100644 --- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h +++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.h @@ -2,6 +2,8 @@ #pragma once +#include "tpm2-util.h" + struct crypt_device; int acquire_luks2_key( @@ -13,6 +15,7 @@ int acquire_luks2_key( size_t key_data_size, const void *policy_hash, size_t policy_hash_size, + TPM2Flags flags, void **ret_decrypted_key, size_t *ret_decrypted_key_size); @@ -23,4 +26,5 @@ int parse_luks2_tpm2_data( uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg, char **ret_base64_blob, - char **ret_hex_policy_hash); + char **ret_hex_policy_hash, + TPM2Flags *ret_flags); diff --git a/src/cryptsetup/cryptsetup-tpm2.c b/src/cryptsetup/cryptsetup-tpm2.c index cb139518a..b84d64def 100644 --- a/src/cryptsetup/cryptsetup-tpm2.c +++ b/src/cryptsetup/cryptsetup-tpm2.c @@ -1,7 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" +#include "ask-password-api.h" #include "cryptsetup-tpm2.h" +#include "env-util.h" #include "fileio.h" #include "hexdecoct.h" #include "json.h" @@ -9,6 +11,47 @@ #include "random-util.h" #include "tpm2-util.h" +static int get_pin(usec_t until, AskPasswordFlags ask_password_flags, bool headless, char **ret_pin_str) { + _cleanup_free_ char *pin_str = NULL; + _cleanup_strv_free_erase_ char **pin = NULL; + int r; + + assert(ret_pin_str); + + r = getenv_steal_erase("PIN", &pin_str); + if (r < 0) + return log_error_errno(r, "Failed to acquire PIN from environment: %m"); + if (!r) { + if (headless) + return log_error_errno( + SYNTHETIC_ERRNO(ENOPKG), + "PIN querying disabled via 'headless' option. " + "Use the '$PIN' environment variable."); + + pin = strv_free_erase(pin); + r = ask_password_auto( + "Please enter TPM2 PIN:", + "drive-harddisk", + NULL, + "tpm2-pin", + "cryptsetup.tpm2-pin", + until, + ask_password_flags, + &pin); + if (r < 0) + return log_error_errno(r, "Failed to ask for user pin: %m"); + assert(strv_length(pin) == 1); + + pin_str = strdup(pin[0]); + if (!pin_str) + return log_oom(); + } + + *ret_pin_str = TAKE_PTR(pin_str); + + return r; +} + int acquire_tpm2_key( const char *volume_name, const char *device, @@ -22,6 +65,10 @@ int acquire_tpm2_key( size_t key_data_size, const void *policy_hash, size_t policy_hash_size, + TPM2Flags flags, + usec_t until, + bool headless, + AskPasswordFlags ask_password_flags, void **ret_decrypted_key, size_t *ret_decrypted_key_size) { @@ -64,7 +111,51 @@ int acquire_tpm2_key( blob = loaded_blob; } - return tpm2_unseal(device, pcr_mask, pcr_bank, primary_alg, blob, blob_size, policy_hash, policy_hash_size, ret_decrypted_key, ret_decrypted_key_size); + if (!(flags & TPM2_FLAGS_USE_PIN)) + return tpm2_unseal( + device, + pcr_mask, + pcr_bank, + primary_alg, + blob, + blob_size, + policy_hash, + policy_hash_size, + NULL, + ret_decrypted_key, + ret_decrypted_key_size); + + for (int i = 5;; i--) { + _cleanup_(erase_and_freep) char *pin_str = NULL; + + if (i <= 0) + return -EACCES; + + r = get_pin(until, ask_password_flags, headless, &pin_str); + if (r < 0) + return r; + + r = tpm2_unseal( + device, + pcr_mask, + pcr_bank, + primary_alg, + blob, + blob_size, + policy_hash, + policy_hash_size, + pin_str, + ret_decrypted_key, + ret_decrypted_key_size); + /* We get this error in case there is an authentication policy mismatch. This should + * not happen, but this avoids confusing behavior, just in case. */ + if (IN_SET(r, -EPERM, -ENOLCK)) + return r; + if (r < 0) + continue; + + return r; + } } int find_tpm2_auto_data( @@ -79,11 +170,13 @@ int find_tpm2_auto_data( void **ret_policy_hash, size_t *ret_policy_hash_size, int *ret_keyslot, - int *ret_token) { + int *ret_token, + TPM2Flags *ret_flags) { _cleanup_free_ void *blob = NULL, *policy_hash = NULL; size_t blob_size = 0, policy_hash_size = 0; int r, keyslot = -1, token = -1; + TPM2Flags flags = 0; uint32_t pcr_mask = 0; uint16_t pcr_bank = UINT16_MAX; /* default: pick automatically */ uint16_t primary_alg = TPM2_ALG_ECC; /* ECC was the only supported algorithm in systemd < 250, use that as implied default, for compatibility */ @@ -196,6 +289,16 @@ int find_tpm2_auto_data( return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid base64 data in 'tpm2-policy-hash' field."); + w = json_variant_by_key(v, "tpm2-pin"); + if (w) { + if (!json_variant_is_boolean(w)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "TPM2 PIN policy is not a boolean."); + + if (json_variant_boolean(w)) + flags |= TPM2_FLAGS_USE_PIN; + } + break; } @@ -215,6 +318,7 @@ int find_tpm2_auto_data( *ret_token = token; *ret_pcr_bank = pcr_bank; *ret_primary_alg = primary_alg; + *ret_flags = flags; return 0; } diff --git a/src/cryptsetup/cryptsetup-tpm2.h b/src/cryptsetup/cryptsetup-tpm2.h index bd0462046..ab16d0a18 100644 --- a/src/cryptsetup/cryptsetup-tpm2.h +++ b/src/cryptsetup/cryptsetup-tpm2.h @@ -3,9 +3,11 @@ #include +#include "ask-password-api.h" #include "cryptsetup-util.h" #include "log.h" #include "time-util.h" +#include "tpm2-util.h" #if HAVE_TPM2 @@ -22,6 +24,10 @@ int acquire_tpm2_key( size_t key_data_size, const void *policy_hash, size_t policy_hash_size, + TPM2Flags flags, + usec_t until, + bool headless, + AskPasswordFlags ask_password_flags, void **ret_decrypted_key, size_t *ret_decrypted_key_size); @@ -37,7 +43,8 @@ int find_tpm2_auto_data( void **ret_policy_hash, size_t *ret_policy_hash_size, int *ret_keyslot, - int *ret_token); + int *ret_token, + TPM2Flags *ret_flags); #else @@ -54,6 +61,10 @@ static inline int acquire_tpm2_key( size_t key_data_size, const void *policy_hash, size_t policy_hash_size, + TPM2Flags flags, + usec_t until, + bool headless, + AskPasswordFlags ask_password_flags, void **ret_decrypted_key, size_t *ret_decrypted_key_size) { @@ -73,7 +84,8 @@ static inline int find_tpm2_auto_data( void **ret_policy_hash, size_t *ret_policy_hash_size, int *ret_keyslot, - int *ret_token) { + int *ret_token, + TPM2Flags *ret_flags) { return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "TPM2 support not available."); diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 250a8314f..6c7b74037 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -17,7 +17,7 @@ #include "cryptsetup-tpm2.h" #include "cryptsetup-util.h" #include "device-util.h" -#include "efi-loader.h" +#include "efi-api.h" #include "env-util.h" #include "escape.h" #include "fileio.h" @@ -82,6 +82,7 @@ static char *arg_fido2_rp_id = NULL; static char *arg_tpm2_device = NULL; static bool arg_tpm2_device_auto = false; static uint32_t arg_tpm2_pcr_mask = UINT32_MAX; +static bool arg_tpm2_pin = false; static bool arg_headless = false; static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC; @@ -387,6 +388,16 @@ static int parse_one_option(const char *option) { arg_tpm2_pcr_mask |= mask; } + } else if ((val = startswith(option, "tpm2-pin="))) { + + r = parse_boolean(val); + if (r < 0) { + log_error_errno(r, "Failed to parse %s, ignoring: %m", option); + return 0; + } + + arg_tpm2_pin = r; + } else if ((val = startswith(option, "try-empty-password="))) { r = parse_boolean(val); @@ -562,7 +573,7 @@ static int get_password( _cleanup_free_ char *friendly = NULL, *text = NULL, *disk_path = NULL; _cleanup_strv_free_erase_ char **passwords = NULL; - char **p, *id; + char *id; int r = 0; AskPasswordFlags flags = arg_ask_password_flags | ASK_PASSWORD_PUSH_CACHE; @@ -818,20 +829,19 @@ static bool libcryptsetup_plugins_support(void) { #if HAVE_LIBCRYPTSETUP_PLUGINS static int acquire_pins_from_env_variable(char ***ret_pins) { - char *e; + _cleanup_(erase_and_freep) char *envpin = NULL; _cleanup_strv_free_erase_ char **pins = NULL; + int r; assert(ret_pins); - e = getenv("PIN"); - if (e) { - pins = strv_new(e); + r = getenv_steal_erase("PIN", &envpin); + if (r < 0) + return log_error_errno(r, "Failed to acquire PIN from environment: %m"); + if (r > 0) { + pins = strv_new(envpin); if (!pins) return log_oom(); - - string_erase(e); - if (unsetenv("PIN") < 0) - return log_error_errno(errno, "Failed to unset $PIN: %m"); } *ret_pins = TAKE_PTR(pins); @@ -851,7 +861,6 @@ static int attach_luks2_by_fido2( #if HAVE_LIBCRYPTSETUP_PLUGINS AskPasswordFlags flags = ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED; _cleanup_strv_free_erase_ char **pins = NULL; - char **p; int r; r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, NULL, 0, usrptr, activation_flags); @@ -1176,7 +1185,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11( /* Before using this key as passphrase we base64 encode it. Why? For compatibility * with homed's PKCS#11 hookup: there we want to use the key we acquired through * PKCS#11 for other authentication/decryption mechanisms too, and some of them do - * not not take arbitrary binary blobs, but require NUL-terminated strings — most + * not take arbitrary binary blobs, but require NUL-terminated strings — most * importantly UNIX password hashes. Hence, for compatibility we want to use a string * without embedded NUL here too, and that's easiest to generate from a binary blob * via base64 encoding. */ @@ -1302,9 +1311,15 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( key_file, arg_keyfile_size, arg_keyfile_offset, key_data, key_data_size, NULL, 0, /* we don't know the policy hash */ + arg_tpm2_pin, + until, + arg_headless, + arg_ask_password_flags, &decrypted_key, &decrypted_key_size); if (r >= 0) break; + if (IN_SET(r, -EACCES, -ENOLCK)) + return log_error_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 PIN unlock failed, falling back to traditional unlocking."); if (ERRNO_IS_NOT_SUPPORTED(r)) /* TPM2 support not compiled in? */ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 support not available, falling back to traditional unlocking."); if (r != -EAGAIN) /* EAGAIN means: no tpm2 chip found */ @@ -1336,6 +1351,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( for (;;) { uint32_t pcr_mask; uint16_t pcr_bank, primary_alg; + TPM2Flags tpm2_flags; r = find_tpm2_auto_data( cd, @@ -1347,7 +1363,8 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( &blob, &blob_size, &policy_hash, &policy_hash_size, &keyslot, - &token); + &token, + &tpm2_flags); if (r == -ENXIO) /* No further TPM2 tokens found in the LUKS2 header. */ return log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), @@ -1370,7 +1387,13 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( NULL, 0, 0, /* no key file */ blob, blob_size, policy_hash, policy_hash_size, + tpm2_flags, + until, + arg_headless, + arg_ask_password_flags, &decrypted_key, &decrypted_key_size); + if (IN_SET(r, -EACCES, -ENOLCK)) + return log_error_errno(SYNTHETIC_ERRNO(EAGAIN), "TPM2 PIN unlock failed, falling back to traditional unlocking."); if (r != -EPERM) break; @@ -1391,7 +1414,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2( if (is_efi_boot() && !efi_has_tpm2()) return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN), - "No TPM2 hardware discovered and EFI bios indicates no support for it either, assuming TPM2-less system, falling back to traditional unocking."); + "No TPM2 hardware discovered and EFI firmware does not see it either, falling back to traditional unlocking."); r = make_tpm2_device_monitor(&event, &monitor); if (r < 0) @@ -1523,7 +1546,6 @@ static int attach_luks_or_plain_or_bitlk_by_passphrase( uint32_t flags, bool pass_volume_key) { - char **p; int r; assert(cd); @@ -1639,7 +1661,7 @@ static int help(void) { if (r < 0) return log_oom(); - printf("%s attach VOLUME SOURCEDEVICE [PASSWORD] [OPTIONS]\n" + printf("%s attach VOLUME SOURCEDEVICE [KEY-FILE] [OPTIONS]\n" "%s detach VOLUME\n\n" "Attaches or detaches an encrypted block device.\n" "\nSee the %s for details.\n", @@ -1721,7 +1743,7 @@ static int run(int argc, char *argv[]) { unsigned tries; usec_t until; - /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [PASSWORD] [OPTIONS] */ + /* Arguments: systemd-cryptsetup attach VOLUME SOURCE-DEVICE [KEY-FILE] [OPTIONS] */ if (argc < 4) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least two arguments."); diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c index a724ae510..e2e1df571 100644 --- a/src/debug-generator/debug-generator.c +++ b/src/debug-generator/debug-generator.c @@ -89,7 +89,6 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat } static int generate_mask_symlinks(void) { - char **u; int r = 0; if (strv_isempty(arg_mask)) @@ -112,7 +111,6 @@ static int generate_mask_symlinks(void) { } static int generate_wants_symlinks(void) { - char **u; int r = 0; if (strv_isempty(arg_wants)) diff --git a/src/delta/delta.c b/src/delta/delta.c index 3f1b206e6..aa5a546bc 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -91,7 +91,7 @@ static int notify_override_masked(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight_red(), "[MASKED]", ansi_normal(), - top, special_glyph(SPECIAL_GLYPH_ARROW), bottom); + top, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), bottom); return 1; } @@ -101,7 +101,7 @@ static int notify_override_equivalent(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight_green(), "[EQUIVALENT]", ansi_normal(), - top, special_glyph(SPECIAL_GLYPH_ARROW), bottom); + top, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), bottom); return 1; } @@ -111,7 +111,7 @@ static int notify_override_redirected(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight(), "[REDIRECTED]", ansi_normal(), - top, special_glyph(SPECIAL_GLYPH_ARROW), bottom); + top, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), bottom); return 1; } @@ -121,7 +121,7 @@ static int notify_override_overridden(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight(), "[OVERRIDDEN]", ansi_normal(), - top, special_glyph(SPECIAL_GLYPH_ARROW), bottom); + top, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), bottom); return 1; } @@ -131,7 +131,7 @@ static int notify_override_extended(const char *top, const char *bottom) { printf("%s%s%s %s %s %s\n", ansi_highlight(), "[EXTENDED]", ansi_normal(), - top, special_glyph(SPECIAL_GLYPH_ARROW), bottom); + top, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), bottom); return 1; } @@ -195,7 +195,6 @@ static int enumerate_dir_d( _cleanup_free_ char *unit = NULL; _cleanup_free_ char *path = NULL; _cleanup_strv_free_ char **list = NULL; - char **file; char *c; int r; @@ -236,7 +235,7 @@ static int enumerate_dir_d( return -ENOMEM; d = p + strlen(toppath) + 1; - log_debug("Adding at top: %s %s %s", d, special_glyph(SPECIAL_GLYPH_ARROW), p); + log_debug("Adding at top: %s %s %s", d, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), p); k = ordered_hashmap_put(top, d, p); if (k >= 0) { p = strdup(p); @@ -248,7 +247,7 @@ static int enumerate_dir_d( return k; } - log_debug("Adding at bottom: %s %s %s", d, special_glyph(SPECIAL_GLYPH_ARROW), p); + log_debug("Adding at bottom: %s %s %s", d, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), p); free(ordered_hashmap_remove(bottom, d)); k = ordered_hashmap_put(bottom, d, p); if (k < 0) { @@ -272,7 +271,7 @@ static int enumerate_dir_d( return -ENOMEM; log_debug("Adding to drops: %s %s %s %s %s", - unit, special_glyph(SPECIAL_GLYPH_ARROW), basename(p), special_glyph(SPECIAL_GLYPH_ARROW), p); + unit, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), basename(p), special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), p); k = ordered_hashmap_put(h, basename(p), p); if (k < 0) { free(p); @@ -292,7 +291,6 @@ static int enumerate_dir( _cleanup_closedir_ DIR *d = NULL; _cleanup_strv_free_ char **files = NULL, **dirs = NULL; size_t n_files = 0, n_dirs = 0; - char **t; int r; assert(top); @@ -349,7 +347,7 @@ static int enumerate_dir( if (!p) return -ENOMEM; - log_debug("Adding at top: %s %s %s", basename(p), special_glyph(SPECIAL_GLYPH_ARROW), p); + log_debug("Adding at top: %s %s %s", basename(p), special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), p); r = ordered_hashmap_put(top, basename(p), p); if (r >= 0) { p = strdup(p); @@ -358,7 +356,7 @@ static int enumerate_dir( } else if (r != -EEXIST) return r; - log_debug("Adding at bottom: %s %s %s", basename(p), special_glyph(SPECIAL_GLYPH_ARROW), p); + log_debug("Adding at bottom: %s %s %s", basename(p), special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), p); free(ordered_hashmap_remove(bottom, basename(p))); r = ordered_hashmap_put(bottom, basename(p), p); if (r < 0) diff --git a/src/dissect/dissect.c b/src/dissect/dissect.c index a9632a3f1..1d78dc508 100644 --- a/src/dissect/dissect.c +++ b/src/dissect/dissect.c @@ -354,7 +354,6 @@ static int parse_argv(int argc, char *argv[]) { static int strv_pair_to_json(char **l, JsonVariant **ret) { _cleanup_strv_free_ char **jl = NULL; - char **a, **b; STRV_FOREACH_PAIR(a, b, l) { char *j; @@ -371,8 +370,6 @@ static int strv_pair_to_json(char **l, JsonVariant **ret) { } static void strv_pair_print(char **l, const char *prefix) { - char **p, **q; - assert(prefix); STRV_FOREACH_PAIR(p, q, l) { diff --git a/src/environment-d-generator/environment-d-generator.c b/src/environment-d-generator/environment-d-generator.c index 1171fdc29..39c46c7c2 100644 --- a/src/environment-d-generator/environment-d-generator.c +++ b/src/environment-d-generator/environment-d-generator.c @@ -41,7 +41,6 @@ static int environment_dirs(char ***ret) { static int load_and_print(void) { _cleanup_strv_free_ char **dirs = NULL, **files = NULL, **env = NULL; - char **i; int r; r = environment_dirs(&dirs); diff --git a/src/escape/escape.c b/src/escape/escape.c index 676b7dce5..0ad77f3f9 100644 --- a/src/escape/escape.c +++ b/src/escape/escape.c @@ -155,7 +155,6 @@ static int parse_argv(int argc, char *argv[]) { } static int run(int argc, char *argv[]) { - char **i; int r; log_setup(); diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 9b32383a7..75523d0c0 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -237,7 +237,6 @@ static int write_dependency( _cleanup_strv_free_ char **names = NULL, **units = NULL; _cleanup_free_ char *res = NULL; - char **s; int r; assert(f); @@ -518,8 +517,6 @@ static int add_mount( if (r < 0) return r; } else { - char **s; - STRV_FOREACH(s, wanted_by) { r = generator_add_symlink(dest, *s, "wants", name); if (r < 0) @@ -721,7 +718,7 @@ static int sysroot_is_nfsroot(void) { if (!sep) return -EINVAL; - a = strndupa(arg_root_what + 1, sep - arg_root_what - 1); + a = strndupa_safe(arg_root_what + 1, sep - arg_root_what - 1); r = in_addr_from_string(AF_INET6, a, &u); if (r < 0) @@ -733,7 +730,7 @@ static int sysroot_is_nfsroot(void) { /* IPv4 address */ sep = strchr(arg_root_what, ':'); if (sep) { - a = strndupa(arg_root_what, sep - arg_root_what); + a = strndupa_safe(arg_root_what, sep - arg_root_what); if (in_addr_from_string(AF_INET, a, &u) >= 0) return true; diff --git a/src/fundamental/bootspec-fundamental.c b/src/fundamental/bootspec-fundamental.c index 9c4aee974..89e29f598 100644 --- a/src/fundamental/bootspec-fundamental.c +++ b/src/fundamental/bootspec-fundamental.c @@ -2,7 +2,7 @@ #include "bootspec-fundamental.h" -sd_bool bootspec_pick_name_version( +sd_bool bootspec_pick_name_version_sort_key( const sd_char *os_pretty_name, const sd_char *os_image_id, const sd_char *os_name, @@ -12,12 +12,14 @@ sd_bool bootspec_pick_name_version( const sd_char *os_version_id, const sd_char *os_build_id, const sd_char **ret_name, - const sd_char **ret_version) { + const sd_char **ret_version, + const sd_char **ret_sort_key) { - const sd_char *good_name, *good_version; + const sd_char *good_name, *good_version, *good_sort_key; - /* Find the best human readable title and version string for a boot entry (using the os-release(5) - * fields). Precise is preferred over vague, and human readable over machine readable. Thus: + /* Find the best human readable title, version string and sort key for a boot entry (using the + * os-release(5) fields). Precise is preferred over vague, and human readable over machine + * readable. Thus: * * 1. First priority gets the PRETTY_NAME field, which is the primary string intended for display, * and should already contain both a nice description and a version indication (if that concept @@ -37,11 +39,12 @@ sd_bool bootspec_pick_name_version( * which case the version is shown too. * * Note that name/version determined here are used only for display purposes. Boot entry preference - * sorting (i.e. algorithmic ordering of boot entries) is done based on the order of the entry "id" - * string (i.e. not on os-release(5) data). */ + * sorting (i.e. algorithmic ordering of boot entries) is done based on the order of the sort key (if + * defined) or entry "id" string (i.e. entry file name) otherwise. */ good_name = os_pretty_name ?: (os_image_id ?: (os_name ?: os_id)); good_version = os_image_version ?: (os_version ?: (os_version_id ? : os_build_id)); + good_sort_key = os_image_id ?: os_id; if (!good_name || !good_version) return sd_false; @@ -52,5 +55,8 @@ sd_bool bootspec_pick_name_version( if (ret_version) *ret_version = good_version; + if (ret_sort_key) + *ret_sort_key = good_sort_key; + return sd_true; } diff --git a/src/fundamental/bootspec-fundamental.h b/src/fundamental/bootspec-fundamental.h index 2cb6d7daa..ff88f8bc3 100644 --- a/src/fundamental/bootspec-fundamental.h +++ b/src/fundamental/bootspec-fundamental.h @@ -3,7 +3,7 @@ #include "types-fundamental.h" -sd_bool bootspec_pick_name_version( +sd_bool bootspec_pick_name_version_sort_key( const sd_char *os_pretty_name, const sd_char *os_image_id, const sd_char *os_name, @@ -13,4 +13,5 @@ sd_bool bootspec_pick_name_version( const sd_char *os_version_id, const sd_char *os_build_id, const sd_char **ret_name, - const sd_char **ret_version); + const sd_char **ret_version, + const sd_char **ret_sort_key); diff --git a/src/fundamental/efivars-fundamental.c b/src/fundamental/efivars-fundamental.c index 6e22232ba..1eaa45526 100644 --- a/src/fundamental/efivars-fundamental.c +++ b/src/fundamental/efivars-fundamental.c @@ -4,6 +4,7 @@ static const sd_char * const table[_SECURE_BOOT_MAX] = { [SECURE_BOOT_UNSUPPORTED] = STR_C("unsupported"), + [SECURE_BOOT_DISABLED] = STR_C("disabled"), [SECURE_BOOT_UNKNOWN] = STR_C("unknown"), [SECURE_BOOT_AUDIT] = STR_C("audit"), [SECURE_BOOT_DEPLOYED] = STR_C("deployed"), @@ -31,6 +32,11 @@ SecureBootMode decode_secure_boot_mode( if (!secure && !deployed && !audit && setup) return SECURE_BOOT_SETUP; + /* Some firmware allows disabling secure boot while not being in + * setup mode unless the PK is cleared. */ + if (!secure && !deployed && !audit && !setup) + return SECURE_BOOT_DISABLED; + /* Well, this should not happen. */ return SECURE_BOOT_UNKNOWN; } diff --git a/src/fundamental/efivars-fundamental.h b/src/fundamental/efivars-fundamental.h index a70810a8d..942545546 100644 --- a/src/fundamental/efivars-fundamental.h +++ b/src/fundamental/efivars-fundamental.h @@ -19,6 +19,7 @@ typedef enum SecureBootMode { SECURE_BOOT_UNSUPPORTED, + SECURE_BOOT_DISABLED, SECURE_BOOT_UNKNOWN, SECURE_BOOT_AUDIT, SECURE_BOOT_DEPLOYED, diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index f87839d47..15b93165d 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -53,19 +53,36 @@ #define CONCATENATE(x, y) XCONCATENATE(x, y) #ifdef SD_BOOT + void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_; + #ifdef NDEBUG #define assert(expr) #define assert_not_reached() __builtin_unreachable() #else - void efi_assert(const char *expr, const char *file, unsigned line, const char *function) _noreturn_; #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__) #endif + #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); }) #define memcpy(a, b, c) CopyMem((a), (b), (c)) #define free(a) FreePool(a) #endif +/* This passes the argument through after (if asserts are enabled) checking that it is not null. */ +#define ASSERT_PTR(expr) \ + ({ \ + typeof(expr) _expr_ = (expr); \ + assert(_expr_); \ + _expr_; \ + }) + +#define ASSERT_SE_PTR(expr) \ + ({ \ + typeof(expr) _expr_ = (expr); \ + assert_se(_expr_); \ + _expr_; \ + }) + #if defined(static_assert) #define assert_cc(expr) \ static_assert(expr, #expr) diff --git a/src/fundamental/sha256.h b/src/fundamental/sha256.h index abc416762..e53197f2e 100644 --- a/src/fundamental/sha256.h +++ b/src/fundamental/sha256.h @@ -8,6 +8,8 @@ #include "types-fundamental.h" +#define SHA256_DIGEST_SIZE 32 + struct sha256_ctx { uint32_t H[8]; diff --git a/src/fuzz/fuzz-env-file.c b/src/fuzz/fuzz-env-file.c index e0dac260b..3b3e62560 100644 --- a/src/fuzz/fuzz-env-file.c +++ b/src/fuzz/fuzz-env-file.c @@ -4,7 +4,6 @@ #include "alloc-util.h" #include "env-file.h" -#include "fileio.h" #include "fd-util.h" #include "fuzz.h" #include "strv.h" @@ -13,10 +12,10 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_fclose_ FILE *f = NULL; _cleanup_strv_free_ char **rl = NULL, **rlp = NULL; - if (size == 0 || size > 65535) + if (size > 65535) return 0; - f = fmemopen_unlocked((char*) data, size, "re"); + f = data_to_file(data, size); assert_se(f); /* We don't want to fill the logs with messages about parse errors. diff --git a/src/fuzz/fuzz-hostname-setup.c b/src/fuzz/fuzz-hostname-setup.c index b8d36da54..d7c23eef1 100644 --- a/src/fuzz/fuzz-hostname-setup.c +++ b/src/fuzz/fuzz-hostname-setup.c @@ -2,7 +2,6 @@ #include "alloc-util.h" #include "fd-util.h" -#include "fileio.h" #include "fuzz.h" #include "hostname-setup.h" @@ -10,10 +9,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *ret = NULL; - if (size == 0) - return 0; - - f = fmemopen_unlocked((char*) data, size, "re"); + f = data_to_file(data, size); assert_se(f); /* We don't want to fill the logs with messages about parse errors. diff --git a/src/fuzz/fuzz-json.c b/src/fuzz/fuzz-json.c index f9a0e818c..ad7460c6f 100644 --- a/src/fuzz/fuzz-json.c +++ b/src/fuzz/fuzz-json.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "alloc-util.h" -#include "fileio.h" #include "fd-util.h" #include "fuzz.h" #include "json.h" @@ -12,10 +11,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_fclose_ FILE *f = NULL, *g = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; - if (size == 0) - return 0; - - f = fmemopen_unlocked((char*) data, size, "re"); + f = data_to_file(data, size); assert_se(f); if (json_parse_file(f, NULL, 0, &v, NULL, NULL) < 0) diff --git a/src/fuzz/fuzz.h b/src/fuzz/fuzz.h index 579b0eed7..d7cbb0bb1 100644 --- a/src/fuzz/fuzz.h +++ b/src/fuzz/fuzz.h @@ -4,5 +4,14 @@ #include #include +#include "fileio.h" + /* The entry point into the fuzzer */ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +static inline FILE* data_to_file(const uint8_t *data, size_t size) { + if (size == 0) + return fopen("/dev/null", "re"); + else + return fmemopen_unlocked((char*) data, size, "re"); +} diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build index 1ed1dd825..d987f32b0 100644 --- a/src/fuzz/meson.build +++ b/src/fuzz/meson.build @@ -1,23 +1,23 @@ # SPDX-License-Identifier: LGPL-2.1-or-later fuzzers += [ - [['src/fuzz/fuzz-catalog.c']], + [files('fuzz-catalog.c')], - [['src/fuzz/fuzz-json.c']], + [files('fuzz-json.c')], - [['src/fuzz/fuzz-varlink.c']], + [files('fuzz-varlink.c')], - [['src/fuzz/fuzz-udev-database.c']], + [files('fuzz-udev-database.c')], - [['src/fuzz/fuzz-compress.c']], + [files('fuzz-compress.c')], - [['src/fuzz/fuzz-bus-label.c']], + [files('fuzz-bus-label.c')], - [['src/fuzz/fuzz-env-file.c']], + [files('fuzz-env-file.c')], - [['src/fuzz/fuzz-hostname-setup.c']], + [files('fuzz-hostname-setup.c')], - [['src/fuzz/fuzz-calendarspec.c']], + [files('fuzz-calendarspec.c')], - [['src/fuzz/fuzz-time-util.c']], + [files('fuzz-time-util.c')], ] diff --git a/src/getty-generator/getty-generator.c b/src/getty-generator/getty-generator.c index 59bdfc496..4e8162a31 100644 --- a/src/getty-generator/getty-generator.c +++ b/src/getty-generator/getty-generator.c @@ -215,9 +215,7 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) return r; } - /* Automatically add in a serial getty on the first - * virtualizer console */ - const char *j; + /* Automatically add in a serial getty on the first virtualizer console */ FOREACH_STRING(j, "hvc0", "xvc0", diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index 64ca9bb2f..28982a4c3 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -779,12 +779,16 @@ static int add_mounts(void) { return btrfs_log_dev_root(LOG_ERR, r, "root file system"); if (r < 0) return log_error_errno(r, "Failed to determine block device of root file system: %m"); - if (r == 0) { /* Not backed by block device */ + if (r == 0) { /* Not backed by a single block device. (Could be NFS or so, or could be multi-device RAID or so) */ r = get_block_device_harder("/usr", &devno); if (r == -EUCLEAN) return btrfs_log_dev_root(LOG_ERR, r, "/usr"); if (r < 0) - return log_error_errno(r, "Failed to determine block device of /usr file system: %m"); + return log_error_errno(r, "Failed to determine block device of /usr/ file system: %m"); + if (r == 0) { /* /usr/ not backed by single block device, either. */ + log_debug("Neither root nor /usr/ file system are on a (single) block device."); + return 0; + } } } else if (r < 0) return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m"); diff --git a/src/home/home-util.h b/src/home/home-util.h index 69a88000f..36b301d23 100644 --- a/src/home/home-util.h +++ b/src/home/home-util.h @@ -8,10 +8,6 @@ #include "time-util.h" #include "user-record.h" -/* See https://systemd.io/UIDS-GIDS for details how this range fits into the rest of the world */ -#define HOME_UID_MIN 60001 -#define HOME_UID_MAX 60513 - /* Put some limits on disk sizes: not less than 5M, not more than 5T */ #define USER_DISK_SIZE_MIN (UINT64_C(5)*1024*1024) #define USER_DISK_SIZE_MAX (UINT64_C(5)*1024*1024*1024*1024) diff --git a/src/home/homectl-pkcs11.c b/src/home/homectl-pkcs11.c index 52cee9a17..c146c62e5 100644 --- a/src/home/homectl-pkcs11.c +++ b/src/home/homectl-pkcs11.c @@ -114,7 +114,7 @@ int identity_add_token_pin(JsonVariant **v, const char *pin) { if (r < 0) return log_error_errno(r, "Failed to convert PIN array: %m"); - if (strv_find(pins, pin)) + if (strv_contains(pins, pin)) return 0; r = strv_extend(&pins, pin); diff --git a/src/home/homectl.c b/src/home/homectl.c index 1e3c96f5a..f0d1dac6a 100644 --- a/src/home/homectl.c +++ b/src/home/homectl.c @@ -201,24 +201,25 @@ static int acquire_existing_password( AskPasswordFlags flags) { _cleanup_(strv_free_erasep) char **password = NULL; + _cleanup_(erase_and_freep) char *envpw = NULL; _cleanup_free_ char *question = NULL; - char *e; int r; assert(user_name); assert(hr); - e = getenv("PASSWORD"); - if (e) { + r = getenv_steal_erase("PASSWORD", &envpw); + if (r < 0) + return log_error_errno(r, "Failed to acquire password from environment: %m"); + if (r > 0) { /* People really shouldn't use environment variables for passing passwords. We support this * only for testing purposes, and do not document the behaviour, so that people won't * actually use this outside of testing. */ - r = user_record_set_password(hr, STRV_MAKE(e), true); + r = user_record_set_password(hr, STRV_MAKE(envpw), true); if (r < 0) return log_error_errno(r, "Failed to store password: %m"); - assert_se(unsetenv_erase("PASSWORD") >= 0); return 1; } @@ -261,24 +262,25 @@ static int acquire_recovery_key( AskPasswordFlags flags) { _cleanup_(strv_free_erasep) char **recovery_key = NULL; + _cleanup_(erase_and_freep) char *envpw = NULL; _cleanup_free_ char *question = NULL; - char *e; int r; assert(user_name); assert(hr); - e = getenv("RECOVERY_KEY"); - if (e) { + r = getenv_steal_erase("PASSWORD", &envpw); + if (r < 0) + return log_error_errno(r, "Failed to acquire password from environment: %m"); + if (r > 0) { /* People really shouldn't use environment variables for passing secrets. We support this * only for testing purposes, and do not document the behaviour, so that people won't * actually use this outside of testing. */ - r = user_record_set_password(hr, STRV_MAKE(e), true); /* recovery keys are stored in the record exactly like regular passwords! */ + r = user_record_set_password(hr, STRV_MAKE(envpw), true); /* recovery keys are stored in the record exactly like regular passwords! */ if (r < 0) return log_error_errno(r, "Failed to store recovery key: %m"); - assert_se(unsetenv_erase("RECOVERY_KEY") >= 0); return 1; } @@ -318,20 +320,21 @@ static int acquire_token_pin( AskPasswordFlags flags) { _cleanup_(strv_free_erasep) char **pin = NULL; + _cleanup_(erase_and_freep) char *envpin = NULL; _cleanup_free_ char *question = NULL; - char *e; int r; assert(user_name); assert(hr); - e = getenv("PIN"); - if (e) { - r = user_record_set_token_pin(hr, STRV_MAKE(e), false); + r = getenv_steal_erase("PIN", &envpin); + if (r < 0) + return log_error_errno(r, "Failed to acquire PIN from environment: %m"); + if (r > 0) { + r = user_record_set_token_pin(hr, STRV_MAKE(envpin), false); if (r < 0) return log_error_errno(r, "Failed to store token PIN: %m"); - assert_se(unsetenv_erase("PIN") >= 0); return 1; } @@ -554,7 +557,6 @@ static int acquire_passed_secrets(const char *user_name, UserRecord **ret) { static int activate_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r, ret = 0; - char **i; r = acquire_bus(&bus); if (r < 0) @@ -603,7 +605,6 @@ static int activate_home(int argc, char *argv[], void *userdata) { static int deactivate_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r, ret = 0; - char **i; r = acquire_bus(&bus); if (r < 0) @@ -690,7 +691,7 @@ static int inspect_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(strv_freep) char **mangled_list = NULL; int r, ret = 0; - char **items, **i; + char **items; pager_open(arg_pager_flags); @@ -774,7 +775,7 @@ static int authenticate_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(strv_freep) char **mangled_list = NULL; int r, ret = 0; - char **i, **items; + char **items; items = mangle_user_list(strv_skip(argv, 1), &mangled_list); if (!items) @@ -1084,7 +1085,6 @@ static int add_disposition(JsonVariant **v) { static int acquire_new_home_record(UserRecord **ret) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_(user_record_unrefp) UserRecord *hr = NULL; - char **i; int r; assert(ret); @@ -1150,33 +1150,25 @@ static int acquire_new_password( bool suggest, char **ret) { + _cleanup_(erase_and_freep) char *envpw = NULL; unsigned i = 5; - char *e; int r; assert(user_name); assert(hr); - e = getenv("NEWPASSWORD"); - if (e) { - _cleanup_(erase_and_freep) char *copy = NULL; - + r = getenv_steal_erase("NEWPASSWORD", &envpw); + if (r < 0) + return log_error_errno(r, "Failed to acquire password from environment: %m"); + if (r > 0) { /* As above, this is not for use, just for testing */ - if (ret) { - copy = strdup(e); - if (!copy) - return log_oom(); - } - - r = user_record_set_password(hr, STRV_MAKE(e), /* prepend = */ true); + r = user_record_set_password(hr, STRV_MAKE(envpw), /* prepend = */ true); if (r < 0) return log_error_errno(r, "Failed to store password: %m"); - assert_se(unsetenv_erase("NEWPASSWORD") >= 0); - if (ret) - *ret = TAKE_PTR(copy); + *ret = TAKE_PTR(envpw); return 0; } @@ -1375,7 +1367,6 @@ static int create_home(int argc, char *argv[], void *userdata) { static int remove_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r, ret = 0; - char **i; r = acquire_bus(&bus); if (r < 0) @@ -1413,7 +1404,6 @@ static int acquire_updated_home_record( _cleanup_(json_variant_unrefp) JsonVariant *json = NULL; _cleanup_(user_record_unrefp) UserRecord *hr = NULL; - char **i; int r; assert(ret); @@ -1863,7 +1853,6 @@ static int resize_home(int argc, char *argv[], void *userdata) { static int lock_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r, ret = 0; - char **i; r = acquire_bus(&bus); if (r < 0) @@ -1895,7 +1884,6 @@ static int lock_home(int argc, char *argv[], void *userdata) { static int unlock_home(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r, ret = 0; - char **i; r = acquire_bus(&bus); if (r < 0) @@ -2961,8 +2949,6 @@ static int parse_argv(int argc, char *argv[]) { case ARG_DISK_SIZE: if (isempty(optarg)) { - const char *prop; - FOREACH_STRING(prop, "diskSize", "diskSizeRelative", "rebalanceWeight") { r = drop_from_identity(prop); if (r < 0) @@ -3464,9 +3450,7 @@ static int parse_argv(int argc, char *argv[]) { break; } - case ARG_PKCS11_TOKEN_URI: { - const char *p; - + case ARG_PKCS11_TOKEN_URI: if (streq(optarg, "list")) return pkcs11_list_tokens(); @@ -3500,11 +3484,8 @@ static int parse_argv(int argc, char *argv[]) { strv_uniq(arg_pkcs11_token_uri); break; - } - - case ARG_FIDO2_DEVICE: { - const char *p; + case ARG_FIDO2_DEVICE: if (streq(optarg, "list")) return fido2_list_devices(); @@ -3534,7 +3515,6 @@ static int parse_argv(int argc, char *argv[]) { strv_uniq(arg_fido2_device); break; - } case ARG_FIDO2_WITH_PIN: { bool lock_with_pin; @@ -3569,9 +3549,7 @@ static int parse_argv(int argc, char *argv[]) { break; } - case ARG_RECOVERY_KEY: { - const char *p; - + case ARG_RECOVERY_KEY: r = parse_boolean(optarg); if (r < 0) return log_error_errno(r, "Failed to parse --recovery-key= argument: %s", optarg); @@ -3585,7 +3563,6 @@ static int parse_argv(int argc, char *argv[]) { } break; - } case ARG_AUTO_RESIZE_MODE: if (isempty(optarg)) { diff --git a/src/home/homed-home.c b/src/home/homed-home.c index 470c7f07f..79c568917 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -1185,14 +1185,18 @@ static int home_start_work(Home *h, const char *verb, UserRecord *hr, UserRecord if (r < 0) return r; if (r == 0) { + _cleanup_free_ char *joined = NULL; const char *homework, *suffix, *unix_path; /* Child */ suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX"); - if (suffix) - unix_path = strjoina("/run/systemd/home/notify.", suffix); - else + if (suffix) { + joined = strjoin("/run/systemd/home/notify.", suffix); + if (!joined) + return log_oom(); + unix_path = joined; + } else unix_path = "/run/systemd/home/notify"; if (setenv("NOTIFY_SOCKET", unix_path, 1) < 0) { @@ -1448,7 +1452,7 @@ static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error) { } /* Let's start a timer to retry deactivation in 15. We'll stop the timer once we manage to deactivate - * the home directory again, or we we start any other operation. */ + * the home directory again, or we start any other operation. */ home_start_retry_deactivate(h); return r; diff --git a/src/home/homed-manager-bus.c b/src/home/homed-manager-bus.c index 47eb287aa..ba5ca42ed 100644 --- a/src/home/homed-manager-bus.c +++ b/src/home/homed-manager-bus.c @@ -39,7 +39,6 @@ static int property_get_auto_login( HASHMAP_FOREACH(h, m->homes_by_name) { _cleanup_(strv_freep) char **seats = NULL; _cleanup_free_ char *home_path = NULL; - char **s; r = home_auto_login(h, &seats); if (r < 0) { diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c index 6c178b8a0..c2b9eb64f 100644 --- a/src/home/homed-manager.c +++ b/src/home/homed-manager.c @@ -482,7 +482,6 @@ static int manager_enumerate_records(Manager *m) { static int search_quota(uid_t uid, const char *exclude_quota_path) { struct stat exclude_st = {}; dev_t previous_devno = 0; - const char *where; int r; /* Checks whether the specified UID owns any files on the files system, but ignore any file system @@ -936,6 +935,7 @@ int manager_enumerate_images(Manager *m) { } static int manager_connect_bus(Manager *m) { + _cleanup_free_ char *b = NULL; const char *suffix, *busname; int r; @@ -955,9 +955,12 @@ static int manager_connect_bus(Manager *m) { return r; suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX"); - if (suffix) - busname = strjoina("org.freedesktop.home1.", suffix); - else + if (suffix) { + b = strjoin("org.freedesktop.home1.", suffix); + if (!b) + return log_oom(); + busname = b; + } else busname = "org.freedesktop.home1"; r = sd_bus_request_name_async(m->bus, NULL, busname, 0, NULL, NULL); @@ -974,6 +977,7 @@ static int manager_connect_bus(Manager *m) { } static int manager_bind_varlink(Manager *m) { + _cleanup_free_ char *p = NULL; const char *suffix, *socket_path; int r; @@ -999,9 +1003,12 @@ static int manager_bind_varlink(Manager *m) { /* To make things easier to debug, when working from a homed managed home directory, let's optionally * use a different varlink socket name */ suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX"); - if (suffix) - socket_path = strjoina("/run/systemd/userdb/io.systemd.Home.", suffix); - else + if (suffix) { + p = strjoin("/run/systemd/userdb/io.systemd.Home.", suffix); + if (!p) + return log_oom(); + socket_path = p; + } else socket_path = "/run/systemd/userdb/io.systemd.Home"; r = varlink_server_listen_address(m->varlink_server, socket_path, 0666); @@ -1159,9 +1166,11 @@ static int manager_listen_notify(Manager *m) { suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX"); if (suffix) { - const char *unix_path; + _cleanup_free_ char *unix_path = NULL; - unix_path = strjoina("/run/systemd/home/notify.", suffix); + unix_path = strjoin("/run/systemd/home/notify.", suffix); + if (!unix_path) + return log_oom(); r = sockaddr_un_set_path(&sa.un, unix_path); if (r < 0) return log_error_errno(r, "Socket path %s does not fit in sockaddr_un: %m", unix_path); @@ -1229,10 +1238,7 @@ static int manager_add_device(Manager *m, sd_device *d) { return 0; if (r < 0) return log_error_errno(r, "Failed to acquire ID_PART_ENTRY_TYPE device property, ignoring: %m"); - r = sd_id128_from_string(parttype, &id); - if (r < 0) - return log_debug_errno(r, "Failed to parse ID_PART_ENTRY_TYPE field '%s', ignoring: %m", parttype); - if (!sd_id128_equal(id, GPT_USER_HOME)) { + if (id128_equal_string(parttype, GPT_USER_HOME) <= 0) { log_debug("Found partition (%s) we don't care about, ignoring.", sysfs); return 0; } @@ -1544,7 +1550,6 @@ static int manager_load_public_key_one(Manager *m, const char *path) { static int manager_load_public_keys(Manager *m) { _cleanup_strv_free_ char **files = NULL; - char **i; int r; assert(m); diff --git a/src/home/homed-varlink.c b/src/home/homed-varlink.c index 96a6ea754..3dd5a1480 100644 --- a/src/home/homed-varlink.c +++ b/src/home/homed-varlink.c @@ -282,7 +282,6 @@ int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMet if (p.user_name) { const char *last = NULL; - char **i; h = hashmap_get(m->homes_by_name, p.user_name); if (!h) @@ -335,9 +334,7 @@ int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMet } else { const char *last_user_name = NULL, *last_group_name = NULL; - HASHMAP_FOREACH(h, m->homes_by_name) { - char **j; - + HASHMAP_FOREACH(h, m->homes_by_name) STRV_FOREACH(j, h->record->member_of) { if (last_user_name) { @@ -353,7 +350,6 @@ int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMet last_user_name = h->user_name; last_group_name = *j; } - } if (last_user_name) { assert(last_group_name); diff --git a/src/home/homework-cifs.c b/src/home/homework-cifs.c index ed06d1f22..6d499f76b 100644 --- a/src/home/homework-cifs.c +++ b/src/home/homework-cifs.c @@ -20,7 +20,6 @@ int home_setup_cifs( HomeSetup *setup) { _cleanup_free_ char *chost = NULL, *cservice = NULL, *cdir = NULL, *chost_and_service = NULL, *j = NULL; - char **pw; int r; assert(h); diff --git a/src/home/homework-fscrypt.c b/src/home/homework-fscrypt.c index f9fef73f7..afa706a1b 100644 --- a/src/home/homework-fscrypt.c +++ b/src/home/homework-fscrypt.c @@ -197,7 +197,6 @@ static int fscrypt_slot_try_many( const uint8_t match_key_descriptor[static FS_KEY_DESCRIPTOR_SIZE], void **ret_decrypted, size_t *ret_decrypted_size) { - char **i; int r; STRV_FOREACH(i, passwords) { @@ -499,7 +498,6 @@ int home_create_fscrypt( _cleanup_free_ char *d = NULL; uint32_t nr = 0; const char *ip; - char **i; int r; assert(h); @@ -649,7 +647,6 @@ int home_passwd_fscrypt( size_t volume_key_size = 0; uint32_t slot = 0; const char *xa; - char **p; int r; assert(h); diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c index 806320635..5416d12fc 100644 --- a/src/home/homework-luks.c +++ b/src/home/homework-luks.c @@ -28,6 +28,7 @@ #include "filesystems.h" #include "fs-util.h" #include "fsck-util.h" +#include "gpt.h" #include "home-util.h" #include "homework-luks.h" #include "homework-mount.h" @@ -303,7 +304,6 @@ static int luks_try_passwords( size_t *volume_key_size, key_serial_t *ret_key_serial) { - char **pp; int r; assert(h); @@ -495,7 +495,7 @@ static int acquire_open_luks_device( return r; r = sym_crypt_init_by_name(&cd, setup->dm_name); - if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT) && graceful) + if ((ERRNO_IS_DEVICE_ABSENT(r) || r == -EINVAL) && graceful) return 0; if (r < 0) return log_error_errno(r, "Failed to initialize cryptsetup context for %s: %m", setup->dm_name); @@ -703,7 +703,7 @@ static int luks_validate( if (!pp) return errno > 0 ? -errno : -EIO; - if (!streq_ptr(blkid_partition_get_type_string(pp), "773f91ef-66d4-49b5-bd83-d683bf40ad16")) + if (id128_equal_string(blkid_partition_get_type_string(pp), GPT_USER_HOME) <= 0) continue; if (!streq_ptr(blkid_partition_get_name(pp), label)) @@ -1150,45 +1150,34 @@ static int lock_image_fd(int image_fd, const char *ip) { * image file, and send it to our parent. homed will keep it open to ensure no other instance of * homed (across the network or such) will also mount the file. */ + assert(image_fd >= 0); + assert(ip); + r = getenv_bool("SYSTEMD_LUKS_LOCK"); if (r == -ENXIO) return 0; if (r < 0) return log_error_errno(r, "Failed to parse $SYSTEMD_LUKS_LOCK environment variable: %m"); - if (r > 0) { - struct stat st; + if (r == 0) + return 0; - if (fstat(image_fd, &st) < 0) - return log_error_errno(errno, "Failed to stat image file: %m"); - if (S_ISBLK(st.st_mode)) { - /* Locking block devices doesn't really make sense, as this might interfere with - * udev's workings, and these locks aren't network propagated anyway, hence not what - * we are after here. */ - log_debug("Not locking image file '%s', since it's a block device.", ip); - return 0; - } - r = stat_verify_regular(&st); - if (r < 0) - return log_error_errno(r, "Image file to lock is not a regular file: %m"); + if (flock(image_fd, LOCK_EX|LOCK_NB) < 0) { - if (flock(image_fd, LOCK_EX|LOCK_NB) < 0) { + if (errno == EAGAIN) + log_error_errno(errno, "Image file '%s' already locked, can't use.", ip); + else + log_error_errno(errno, "Failed to lock image file '%s': %m", ip); - if (errno == EWOULDBLOCK) - log_error_errno(errno, "Image file '%s' already locked, can't use.", ip); - else - log_error_errno(errno, "Failed to lock image file '%s': %m", ip); - - return errno != EWOULDBLOCK ? -errno : -EADDRINUSE; /* Make error recognizable */ - } - - log_info("Successfully locked image file '%s'.", ip); - - /* Now send it to our parent to keep safe while the home dir is active */ - r = sd_pid_notify_with_fds(0, false, "SYSTEMD_LUKS_LOCK_FD=1", &image_fd, 1); - if (r < 0) - log_warning_errno(r, "Failed to send LUKS lock fd to parent, ignoring: %m"); + return errno != EAGAIN ? -errno : -EADDRINUSE; /* Make error recognizable */ } + log_info("Successfully locked image file '%s'.", ip); + + /* Now send it to our parent to keep safe while the home dir is active */ + r = sd_pid_notify_with_fds(0, false, "SYSTEMD_LUKS_LOCK_FD=1", &image_fd, 1); + if (r < 0) + log_warning_errno(r, "Failed to send LUKS lock fd to parent, ignoring: %m"); + return 0; } @@ -1202,6 +1191,8 @@ static int open_image_file( const char *ip; int r; + assert(h || force_image_path); + ip = force_image_path ?: user_record_image_path(h); image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); @@ -1215,9 +1206,14 @@ static int open_image_file( S_ISDIR(st.st_mode) ? SYNTHETIC_ERRNO(EISDIR) : SYNTHETIC_ERRNO(EBADFD), "Image file %s is not a regular file or block device: %m", ip); - r = lock_image_fd(image_fd, ip); - if (r < 0) - return r; + /* Locking block devices doesn't really make sense, as this might interfere with + * udev's workings, and these locks aren't network propagated anyway, hence not what + * we are after here. */ + if (S_ISREG(st.st_mode)) { + r = lock_image_fd(image_fd, ip); + if (r < 0) + return r; + } if (ret_stat) *ret_stat = st; @@ -1635,7 +1631,7 @@ int home_deactivate_luks(UserRecord *h, HomeSetup *setup) { cryptsetup_enable_logging(setup->crypt_device); r = sym_crypt_deactivate_by_name(setup->crypt_device, setup->dm_name, 0); - if (IN_SET(r, -ENODEV, -EINVAL, -ENOENT)) { + if (ERRNO_IS_DEVICE_ABSENT(r) || r == -EINVAL) { log_debug_errno(r, "LUKS device %s is already detached.", setup->dm_node); we_detached = false; } else if (r < 0) @@ -1721,7 +1717,6 @@ static int luks_format( _cleanup_free_ char *text = NULL; size_t volume_key_size; int slot = 0, r; - char **pp; assert(node); assert(dm_name); @@ -1762,7 +1757,7 @@ static int luks_format( CRYPT_LUKS2, user_record_luks_cipher(hr), user_record_luks_cipher_mode(hr), - ID128_TO_UUID_STRING(uuid), + SD_ID128_TO_UUID_STRING(uuid), volume_key, volume_key_size, &(struct crypt_params_luks2) { @@ -1858,7 +1853,7 @@ static int make_partition_table( if (!t) return log_oom(); - r = fdisk_parttype_set_typestr(t, "773f91ef-66d4-49b5-bd83-d683bf40ad16"); + r = fdisk_parttype_set_typestr(t, GPT_USER_HOME_STR); if (r < 0) return log_error_errno(r, "Failed to initialize partition type: %m"); @@ -1917,7 +1912,7 @@ static int make_partition_table( if (r < 0) return log_error_errno(r, "Failed to set partition name: %m"); - r = fdisk_partition_set_uuid(p, ID128_TO_UUID_STRING(uuid)); + r = fdisk_partition_set_uuid(p, SD_ID128_TO_UUID_STRING(uuid)); if (r < 0) return log_error_errno(r, "Failed to set partition UUID: %m"); @@ -2203,12 +2198,10 @@ int home_create_luks( /* Let's place the home directory on a real device, i.e. an USB stick or such */ - setup->image_fd = open(ip, O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK); + setup->image_fd = open_image_file(h, ip, &st); if (setup->image_fd < 0) - return log_error_errno(errno, "Failed to open device %s: %m", ip); + return setup->image_fd; - if (fstat(setup->image_fd, &st) < 0) - return log_error_errno(errno, "Failed to stat device %s: %m", ip); if (!S_ISBLK(st.st_mode)) return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Device is not a block device, refusing."); @@ -2745,7 +2738,7 @@ static int ask_cb(struct fdisk_context *c, struct fdisk_ask *ask, void *userdata if (!result) return log_oom(); - fdisk_ask_string_set_result(ask, id128_to_uuid_string(*(sd_id128_t*) userdata, result)); + fdisk_ask_string_set_result(ask, sd_id128_to_uuid_string(*(sd_id128_t*) userdata, result)); break; default: @@ -3664,7 +3657,6 @@ static int luks_try_resume( const char *dm_name, char **password) { - char **pp; int r; assert(cd); diff --git a/src/home/homework-mount.c b/src/home/homework-mount.c index 0b028dad3..3095a10ff 100644 --- a/src/home/homework-mount.c +++ b/src/home/homework-mount.c @@ -209,6 +209,19 @@ static int make_userns(uid_t stored_uid, uid_t exposed_uid) { if (r < 0) return log_oom(); + /* Also map the container range. People can use that to place containers owned by high UIDs in their + * home directories if they really want. We won't manage this UID range for them but pass it through + * 1:1, and it will lose its meaning once migrated between hosts. */ + r = append_identity_range(&text, CONTAINER_UID_BASE_MIN, CONTAINER_UID_BASE_MAX+1, stored_uid); + if (r < 0) + return log_oom(); + + /* Map nspawn's mapped root UID as identity mapping so that people can run nspawn uidmap mounted + * containers off $HOME, if they want. */ + r = strextendf(&text, UID_FMT " " UID_FMT " " UID_FMT "\n", UID_MAPPED_ROOT, UID_MAPPED_ROOT, 1); + if (r < 0) + return log_oom(); + /* Leave everything else unmapped, starting from UID_NOBODY itself. Specifically, this means the * whole space outside of 16bit remains unmapped */ diff --git a/src/home/homework-pkcs11.c b/src/home/homework-pkcs11.c index 15402b100..7868fb606 100644 --- a/src/home/homework-pkcs11.c +++ b/src/home/homework-pkcs11.c @@ -20,7 +20,6 @@ int pkcs11_callback( CK_TOKEN_INFO updated_token_info; size_t decrypted_key_size; CK_OBJECT_HANDLE object; - char **i; CK_RV rv; int r; diff --git a/src/home/homework.c b/src/home/homework.c index 9fdc74b77..0014a7f59 100644 --- a/src/home/homework.c +++ b/src/home/homework.c @@ -97,9 +97,7 @@ int user_record_authenticate( log_info("None of the supplied plaintext passwords unlock the user record's hashed recovery keys."); /* Second, test cached PKCS#11 passwords */ - for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) { - char **pp; - + for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) STRV_FOREACH(pp, cache->pkcs11_passwords) { r = test_password_one(h->pkcs11_encrypted_key[n].hashed_password, *pp); if (r < 0) @@ -109,12 +107,9 @@ int user_record_authenticate( return 1; } } - } /* Third, test cached FIDO2 passwords */ - for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) { - char **pp; - + for (size_t n = 0; n < h->n_fido2_hmac_salt; n++) /* See if any of the previously calculated passwords work */ STRV_FOREACH(pp, cache->fido2_passwords) { r = test_password_one(h->fido2_hmac_salt[n].hashed_password, *pp); @@ -125,7 +120,6 @@ int user_record_authenticate( return 1; } } - } /* Fourth, let's see if any of the PKCS#11 security tokens are plugged in and help us */ for (size_t n = 0; n < h->n_pkcs11_encrypted_key; n++) { @@ -1096,7 +1090,6 @@ static int user_record_compile_effective_passwords( _cleanup_(strv_free_erasep) char **effective = NULL; size_t n; - char **i; int r; assert(h); @@ -1116,7 +1109,6 @@ static int user_record_compile_effective_passwords( STRV_FOREACH(i, h->hashed_password) { bool found = false; - char **j; log_debug("Looking for plaintext password for: %s", *i); @@ -1144,7 +1136,6 @@ static int user_record_compile_effective_passwords( for (n = 0; n < h->n_recovery_key; n++) { bool found = false; - char **j; log_debug("Looking for plaintext recovery key for: %s", h->recovery_key[n].hashed_password); diff --git a/src/home/user-record-pwquality.c b/src/home/user-record-pwquality.c index 23c335783..609e62051 100644 --- a/src/home/user-record-pwquality.c +++ b/src/home/user-record-pwquality.c @@ -17,7 +17,7 @@ int user_record_quality_check_password( sd_bus_error *error) { _cleanup_(sym_pwquality_free_settingsp) pwquality_settings_t *pwq = NULL; - char buf[PWQ_MAX_ERROR_MESSAGE_LEN], **pp; + char buf[PWQ_MAX_ERROR_MESSAGE_LEN]; void *auxerror; int r; @@ -37,7 +37,6 @@ int user_record_quality_check_password( /* Iterate through all new passwords */ STRV_FOREACH(pp, secret->password) { bool called = false; - char **old; r = test_password_many(hr->hashed_password, *pp); if (r < 0) diff --git a/src/home/user-record-util.c b/src/home/user-record-util.c index f4578783f..2b727df53 100644 --- a/src/home/user-record-util.c +++ b/src/home/user-record-util.c @@ -332,9 +332,9 @@ int user_record_add_binding( r = json_build(&new_binding_entry, JSON_BUILD_OBJECT( JSON_BUILD_PAIR_CONDITION(!!image_path, "imagePath", JSON_BUILD_STRING(image_path)), - JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(partition_uuid))), - JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(luks_uuid))), - JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(ID128_TO_UUID_STRING(fs_uuid))), + JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(partition_uuid), "partitionUuid", JSON_BUILD_STRING(SD_ID128_TO_UUID_STRING(partition_uuid))), + JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(luks_uuid), "luksUuid", JSON_BUILD_STRING(SD_ID128_TO_UUID_STRING(luks_uuid))), + JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(fs_uuid), "fileSystemUuid", JSON_BUILD_STRING(SD_ID128_TO_UUID_STRING(fs_uuid))), JSON_BUILD_PAIR_CONDITION(!!luks_cipher, "luksCipher", JSON_BUILD_STRING(luks_cipher)), JSON_BUILD_PAIR_CONDITION(!!luks_cipher_mode, "luksCipherMode", JSON_BUILD_STRING(luks_cipher_mode)), JSON_BUILD_PAIR_CONDITION(luks_volume_key_size != UINT64_MAX, "luksVolumeKeySize", JSON_BUILD_UNSIGNED(luks_volume_key_size)), @@ -563,7 +563,6 @@ int user_record_test_image_path_and_warn(UserRecord *h) { } int user_record_test_password(UserRecord *h, UserRecord *secret) { - char **i; int r; assert(h); @@ -585,7 +584,6 @@ int user_record_test_password(UserRecord *h, UserRecord *secret) { } int user_record_test_recovery_key(UserRecord *h, UserRecord *secret) { - char **i; int r; assert(h); @@ -779,7 +777,6 @@ int user_record_update_last_changed(UserRecord *h, bool with_password) { int user_record_make_hashed_password(UserRecord *h, char **secret, bool extend) { _cleanup_(json_variant_unrefp) JsonVariant *priv = NULL; _cleanup_strv_free_ char **np = NULL; - char **i; int r; assert(h); diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c index 58af5f186..d94194d21 100644 --- a/src/hostname/hostnamectl.c +++ b/src/hostname/hostnamectl.c @@ -55,21 +55,21 @@ typedef struct StatusInfo { static const char* chassis_string_to_glyph(const char *chassis) { if (streq_ptr(chassis, "laptop")) - return "\U0001F4BB"; /* Personal Computer */ + return u8"💻"; /* Personal Computer */ if (streq_ptr(chassis, "desktop")) - return "\U0001F5A5"; /* Desktop Computer */ + return u8"🖥️"; /* Desktop Computer */ if (streq_ptr(chassis, "server")) - return "\U0001F5B3"; /* Old Personal Computer */ + return u8"🖳"; /* Old Personal Computer */ if (streq_ptr(chassis, "tablet")) - return "\u5177"; /* Ideograph tool, implement; draw up, write, looks vaguely tabletty */ + return u8"具"; /* Ideograph tool, implement; draw up, write, looks vaguely tabletty */ if (streq_ptr(chassis, "watch")) - return "\u231A"; /* Watch */ + return u8"⌚"; /* Watch */ if (streq_ptr(chassis, "handset")) - return "\U0001F57B"; /* Left Hand Telephone Receiver */ + return u8"🕻"; /* Left Hand Telephone Receiver */ if (streq_ptr(chassis, "vm")) - return "\U0001F5B4"; /* Hard disk */ + return u8"🖴"; /* Hard disk */ if (streq_ptr(chassis, "container")) - return "\u2610"; /* Ballot Box */ + return u8"☐"; /* Ballot Box */ return NULL; } diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index b20a93ad8..5d381e157 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -43,7 +43,7 @@ /* Properties we cache are indexed by an enum, to make invalidation easy and systematic (as we can iterate * through them all, and they are uniformly strings). */ -enum { +typedef enum { /* Read from /etc/hostname */ PROP_STATIC_HOSTNAME, @@ -53,6 +53,8 @@ enum { PROP_CHASSIS, PROP_DEPLOYMENT, PROP_LOCATION, + PROP_HARDWARE_VENDOR, + PROP_HARDWARE_MODEL, /* Read from /etc/os-release (or /usr/lib/os-release) */ PROP_OS_PRETTY_NAME, @@ -60,7 +62,7 @@ enum { PROP_OS_HOME_URL, _PROP_MAX, _PROP_INVALID = -EINVAL, -}; +} HostProperty; typedef struct Context { char *data[_PROP_MAX]; @@ -126,14 +128,18 @@ static void context_read_machine_info(Context *c) { (UINT64_C(1) << PROP_ICON_NAME) | (UINT64_C(1) << PROP_CHASSIS) | (UINT64_C(1) << PROP_DEPLOYMENT) | - (UINT64_C(1) << PROP_LOCATION)); + (UINT64_C(1) << PROP_LOCATION) | + (UINT64_C(1) << PROP_HARDWARE_VENDOR) | + (UINT64_C(1) << PROP_HARDWARE_MODEL)); r = parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &c->data[PROP_PRETTY_HOSTNAME], "ICON_NAME", &c->data[PROP_ICON_NAME], "CHASSIS", &c->data[PROP_CHASSIS], "DEPLOYMENT", &c->data[PROP_DEPLOYMENT], - "LOCATION", &c->data[PROP_LOCATION]); + "LOCATION", &c->data[PROP_LOCATION], + "HARDWARE_VENDOR", &c->data[PROP_HARDWARE_VENDOR], + "HARDWARE_MODEL", &c->data[PROP_HARDWARE_MODEL]); if (r < 0 && r != -ENOENT) log_warning_errno(r, "Failed to read /etc/machine-info, ignoring: %m"); @@ -166,6 +172,69 @@ static void context_read_os_release(Context *c) { c->etc_os_release_stat = current_stat; } +static int get_dmi_data(const char *database_key, const char *regular_key, char **ret) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + _cleanup_free_ char *b = NULL; + const char *s = NULL; + int r; + + r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id"); + if (r < 0) + return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m"); + + if (database_key) + (void) sd_device_get_property_value(device, database_key, &s); + if (!s && regular_key) + (void) sd_device_get_property_value(device, regular_key, &s); + + if (!ret) + return !!s; + + if (s) { + b = strdup(s); + if (!b) + return -ENOMEM; + } + + *ret = TAKE_PTR(b); + return !!s; +} + +static int get_hardware_vendor(char **ret) { + return get_dmi_data("ID_VENDOR_FROM_DATABASE", "ID_VENDOR", ret); +} + +static int get_hardware_model(char **ret) { + return get_dmi_data("ID_MODEL_FROM_DATABASE", "ID_MODEL", ret); +} + +static int get_hardware_serial(char **ret) { + _cleanup_(sd_device_unrefp) sd_device *device = NULL; + _cleanup_free_ char *b = NULL; + const char *s = NULL; + int r; + + r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id"); + if (r < 0) + return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m"); + + (void) sd_device_get_sysattr_value(device, "product_serial", &s); + if (isempty(s)) + /* Fallback to board serial */ + (void) sd_device_get_sysattr_value(device, "board_serial", &s); + + if (!isempty(s)) { + b = strdup(s); + if (!b) + return -ENOMEM; + } + + if (ret) + *ret = TAKE_PTR(b); + + return !isempty(s); +} + static const char* valid_chassis(const char *chassis) { assert(chassis); @@ -218,18 +287,20 @@ static const char* fallback_chassis(void) { /* We only list the really obvious cases here. The DMI data is unreliable enough, so let's not do any * additional guesswork on top of that. * - * See the SMBIOS Specification 3.0 section 7.4.1 for details about the values listed here: + * See the SMBIOS Specification 3.5.0 section 7.4.1 for details about the values listed here: * - * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.5.0.pdf */ switch (t) { - case 0x3: /* Desktop */ - case 0x4: /* Low Profile Desktop */ - case 0x6: /* Mini Tower */ - case 0x7: /* Tower */ - case 0xD: /* All in one (i.e. PC built into monitor) */ + case 0x03: /* Desktop */ + case 0x04: /* Low Profile Desktop */ + case 0x06: /* Mini Tower */ + case 0x07: /* Tower */ + case 0x0D: /* All in one (i.e. PC built into monitor) */ + case 0x23: /* Mini PC */ + case 0x24: /* Stick PC */ return "desktop"; case 0x8: /* Portable */ @@ -253,6 +324,10 @@ static const char* fallback_chassis(void) { case 0x20: /* Detachable */ return "convertible"; + case 0x21: /* IoT Gateway */ + case 0x22: /* Embedded PC */ + return "embedded"; + default: log_debug("Unhandled DMI chassis type 0x%02x, ignoring.", t); } @@ -318,15 +393,31 @@ try_devicetree: return chassis; } -static char* context_fallback_icon_name(Context *c) { - const char *chassis; +static char* context_get_chassis(Context *c) { + const char *fallback; + char *dmi; assert(c); if (!isempty(c->data[PROP_CHASSIS])) - return strjoin("computer-", c->data[PROP_CHASSIS]); + return strdup(c->data[PROP_CHASSIS]); - chassis = fallback_chassis(); + if (get_dmi_data("ID_CHASSIS", NULL, &dmi) >= 0) + return dmi; + + fallback = fallback_chassis(); + if (fallback) + return strdup(fallback); + + return NULL; +} + +static char* context_fallback_icon_name(Context *c) { + _cleanup_free_ char *chassis = NULL; + + assert(c); + + chassis = context_get_chassis(c); if (chassis) return strjoin("computer-", chassis); @@ -464,39 +555,25 @@ static int context_write_data_machine_info(Context *c) { return 0; } -static int get_dmi_data(const char *database_key, const char *regular_key, char **ret) { - _cleanup_(sd_device_unrefp) sd_device *device = NULL; - _cleanup_free_ char *b = NULL; - const char *s = NULL; - int r; +static int property_get_hardware_property( + sd_bus_message *reply, + Context *c, + HostProperty prop, + int (*getter)(char **)) { - r = sd_device_new_from_syspath(&device, "/sys/class/dmi/id"); - if (r < 0) - return log_debug_errno(r, "Failed to open /sys/class/dmi/id device, ignoring: %m"); + _cleanup_free_ char *from_dmi = NULL; - if (database_key) - (void) sd_device_get_property_value(device, database_key, &s); - if (!s && regular_key) - (void) sd_device_get_property_value(device, regular_key, &s); + assert(reply); + assert(c); + assert(IN_SET(prop, PROP_HARDWARE_VENDOR, PROP_HARDWARE_MODEL)); + assert(getter); - if (s) { - b = strdup(s); - if (!b) - return -ENOMEM; - } + context_read_machine_info(c); - if (ret) - *ret = TAKE_PTR(b); + if (isempty(c->data[prop])) + (void) getter(&from_dmi); - return !!s; -} - -static int get_hardware_vendor(char **ret) { - return get_dmi_data("ID_VENDOR_FROM_DATABASE", "ID_VENDOR", ret); -} - -static int get_hardware_model(char **ret) { - return get_dmi_data("ID_MODEL_FROM_DATABASE", "ID_MODEL", ret); + return sd_bus_message_append(reply, "s", from_dmi ?: c->data[prop]); } static int property_get_hardware_vendor( @@ -508,10 +585,7 @@ static int property_get_hardware_vendor( void *userdata, sd_bus_error *error) { - _cleanup_free_ char *vendor = NULL; - - (void) get_hardware_vendor(&vendor); - return sd_bus_message_append(reply, "s", vendor); + return property_get_hardware_property(reply, userdata, PROP_HARDWARE_VENDOR, get_hardware_vendor); } static int property_get_hardware_model( @@ -523,10 +597,7 @@ static int property_get_hardware_model( void *userdata, sd_bus_error *error) { - _cleanup_free_ char *model = NULL; - - (void) get_hardware_model(&model); - return sd_bus_message_append(reply, "s", model); + return property_get_hardware_property(reply, userdata, PROP_HARDWARE_MODEL, get_hardware_model); } static int property_get_hostname( @@ -724,17 +795,14 @@ static int property_get_chassis( void *userdata, sd_bus_error *error) { + _cleanup_free_ char *chassis = NULL; Context *c = userdata; - const char *name; context_read_machine_info(c); - if (isempty(c->data[PROP_CHASSIS])) - name = fallback_chassis(); - else - name = c->data[PROP_CHASSIS]; + chassis = context_get_chassis(c); - return sd_bus_message_append(reply, "s", name); + return sd_bus_message_append(reply, "s", chassis); } static int property_get_uname_field( @@ -1019,12 +1087,50 @@ static int method_get_product_uuid(sd_bus_message *m, void *userdata, sd_bus_err return sd_bus_send(NULL, reply, NULL); } +static int method_get_hardware_serial(sd_bus_message *m, void *userdata, sd_bus_error *error) { + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *serial = NULL; + Context *c = userdata; + int r; + + assert(m); + assert(c); + + r = bus_verify_polkit_async( + m, + CAP_SYS_ADMIN, + "org.freedesktop.hostname1.get-hardware-serial", + NULL, + false, + UID_INVALID, + &c->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + r = get_hardware_serial(&serial); + if (r < 0) + return r; + + r = sd_bus_message_new_method_return(m, &reply); + if (r < 0) + return r; + + r = sd_bus_message_append(reply, "s", serial); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); +} + static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL, *vendor = NULL, *model = NULL; + _cleanup_free_ char *hn = NULL, *dhn = NULL, *in = NULL, *text = NULL, + *chassis = NULL, *vendor = NULL, *model = NULL, *serial = NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; sd_id128_t product_uuid = SD_ID128_NULL; - const char *chassis = NULL; Context *c = userdata; bool privileged; struct utsname u; @@ -1036,7 +1142,7 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro r = bus_verify_polkit_async( m, CAP_SYS_ADMIN, - "org.freedesktop.hostname1.get-product-uuid", + "org.freedesktop.hostname1.get-description", NULL, false, UID_INVALID, @@ -1071,16 +1177,20 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro if (isempty(c->data[PROP_ICON_NAME])) in = context_fallback_icon_name(c); - if (isempty(c->data[PROP_CHASSIS])) - chassis = fallback_chassis(); + chassis = context_get_chassis(c); assert_se(uname(&u) >= 0); - (void) get_hardware_vendor(&vendor); - (void) get_hardware_model(&model); + if (isempty(c->data[PROP_HARDWARE_VENDOR])) + (void) get_hardware_vendor(&vendor); + if (isempty(c->data[PROP_HARDWARE_MODEL])) + (void) get_hardware_model(&model); - if (privileged) /* The product UUID is only available to privileged clients */ - id128_get_product(&product_uuid); + if (privileged) { + /* The product UUID and hardware serial is only available to privileged clients */ + (void) id128_get_product(&product_uuid); + (void) get_hardware_serial(&serial); + } r = json_build(&v, JSON_BUILD_OBJECT( JSON_BUILD_PAIR("Hostname", JSON_BUILD_STRING(hn)), @@ -1089,7 +1199,7 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro JSON_BUILD_PAIR("DefaultHostname", JSON_BUILD_STRING(dhn)), JSON_BUILD_PAIR("HostnameSource", JSON_BUILD_STRING(hostname_source_to_string(c->hostname_source))), JSON_BUILD_PAIR("IconName", JSON_BUILD_STRING(in ?: c->data[PROP_ICON_NAME])), - JSON_BUILD_PAIR("Chassis", JSON_BUILD_STRING(chassis ?: c->data[PROP_CHASSIS])), + JSON_BUILD_PAIR("Chassis", JSON_BUILD_STRING(chassis)), JSON_BUILD_PAIR("Deployment", JSON_BUILD_STRING(c->data[PROP_DEPLOYMENT])), JSON_BUILD_PAIR("Location", JSON_BUILD_STRING(c->data[PROP_LOCATION])), JSON_BUILD_PAIR("KernelName", JSON_BUILD_STRING(u.sysname)), @@ -1098,8 +1208,9 @@ static int method_describe(sd_bus_message *m, void *userdata, sd_bus_error *erro JSON_BUILD_PAIR("OperatingSystemPrettyName", JSON_BUILD_STRING(c->data[PROP_OS_PRETTY_NAME])), JSON_BUILD_PAIR("OperatingSystemCPEName", JSON_BUILD_STRING(c->data[PROP_OS_CPE_NAME])), JSON_BUILD_PAIR("OperatingSystemHomeURL", JSON_BUILD_STRING(c->data[PROP_OS_HOME_URL])), - JSON_BUILD_PAIR("HardwareVendor", JSON_BUILD_STRING(vendor)), - JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model)), + JSON_BUILD_PAIR("HardwareVendor", JSON_BUILD_STRING(vendor ?: c->data[PROP_HARDWARE_VENDOR])), + JSON_BUILD_PAIR("HardwareModel", JSON_BUILD_STRING(model ?: c->data[PROP_HARDWARE_MODEL])), + JSON_BUILD_PAIR("HardwareSerial", JSON_BUILD_STRING(serial)), JSON_BUILD_PAIR_CONDITION(!sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_ID128(product_uuid)), JSON_BUILD_PAIR_CONDITION(sd_id128_is_null(product_uuid), "ProductUUID", JSON_BUILD_NULL))); @@ -1197,6 +1308,12 @@ static const sd_bus_vtable hostname_vtable[] = { SD_BUS_PARAM(uuid), method_get_product_uuid, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_NAMES("GetHardwareSerial", + NULL,, + "s", + SD_BUS_PARAM(serial), + method_get_hardware_serial, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_ARGS("Describe", SD_BUS_NO_ARGS, SD_BUS_RESULT("s", json), diff --git a/src/hostname/org.freedesktop.hostname1.policy b/src/hostname/org.freedesktop.hostname1.policy index dacea0ff0..a86cead53 100644 --- a/src/hostname/org.freedesktop.hostname1.policy +++ b/src/hostname/org.freedesktop.hostname1.policy @@ -57,4 +57,24 @@ + + Get hardware serial number + Authentication is required to get hardware serial number. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + + + Get system description + Authentication is required to get system description. + + auth_admin_keep + auth_admin_keep + auth_admin_keep + + + diff --git a/src/id128/id128.c b/src/id128/id128.c index 484a72677..6f4d65103 100644 --- a/src/id128/id128.c +++ b/src/id128/id128.c @@ -102,7 +102,6 @@ static int show_one(Table **table, const char *name, sd_id128_t uuid, bool first static int verb_show(int argc, char **argv, void *userdata) { _cleanup_(table_unrefp) Table *table = NULL; - char **p; int r; argv = strv_skip(argv, 1); diff --git a/src/import/curl-util.c b/src/import/curl-util.c index d6a16b4f5..5b39251aa 100644 --- a/src/import/curl-util.c +++ b/src/import/curl-util.c @@ -148,7 +148,7 @@ static int curl_glue_timer_callback(CURLM *curl, long timeout_ms, void *userdata if (sd_event_source_set_enabled(g->timer, SD_EVENT_ONESHOT) < 0) return -1; } else { - if (sd_event_add_time_relative(g->event, &g->timer, clock_boottime_or_monotonic(), usec, 0, curl_glue_on_timer, g) < 0) + if (sd_event_add_time_relative(g->event, &g->timer, CLOCK_BOOTTIME, usec, 0, curl_glue_on_timer, g) < 0) return -1; (void) sd_event_source_set_description(g->timer, "curl-timer"); diff --git a/src/import/import.c b/src/import/import.c index 87ed8767e..c76212494 100644 --- a/src/import/import.c +++ b/src/import/import.c @@ -91,7 +91,7 @@ static int open_source(const char *path, const char *local, int *ret_open_fd) { if (path) { open_fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY); if (open_fd < 0) - return log_error_errno(errno, "Failed to open raw image to import: %m"); + return log_error_errno(errno, "Failed to open source file '%s': %m", path); retval = open_fd; diff --git a/src/import/meson.build b/src/import/meson.build index 23f085881..7e923072a 100644 --- a/src/import/meson.build +++ b/src/import/meson.build @@ -61,9 +61,9 @@ if conf.get('ENABLE_IMPORTD') == 1 endif tests += [ - [['src/import/test-qcow2.c', - 'src/import/qcow2-util.c', - 'src/import/qcow2-util.h'], + [files('test-qcow2.c', + 'qcow2-util.c', + 'qcow2-util.h'), [], [libz], [], 'HAVE_ZLIB', 'manual'], diff --git a/src/journal-remote/fuzz-journal-remote.c b/src/journal-remote/fuzz-journal-remote.c index 32c4e1819..9206b9940 100644 --- a/src/journal-remote/fuzz-journal-remote.c +++ b/src/journal-remote/fuzz-journal-remote.c @@ -21,7 +21,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_(unlink_tempfilep) char name[] = "/tmp/fuzz-journal-remote.XXXXXX.journal"; _cleanup_close_ int fdout = -1; _cleanup_(sd_journal_closep) sd_journal *j = NULL; - RemoteServer s = {}; + _cleanup_(journal_remote_server_destroy) RemoteServer s = {}; int r; if (size <= 2) @@ -43,7 +43,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { /* In */ - r = journal_remote_server_init(&s, name, JOURNAL_WRITE_SPLIT_NONE, false, false); + r = journal_remote_server_init(&s, name, JOURNAL_WRITE_SPLIT_NONE, 0); if (r < 0) { assert_se(IN_SET(r, -ENOMEM, -EMFILE, -ENFILE)); return r; @@ -59,7 +59,6 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { while (s.active) assert_se(journal_remote_handle_raw_source(NULL, fdin, 0, &s) >= 0); - journal_remote_server_destroy(&s); assert_se(close(fdin) < 0 && errno == EBADF); /* Check that the fd is closed already */ /* Out */ diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index 6ab91263b..7c3c9ff44 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -46,7 +46,11 @@ static const char* arg_output = NULL; static char *arg_key = NULL; static char *arg_cert = NULL; static char *arg_trust = NULL; +#if HAVE_GNUTLS static bool arg_trust_all = false; +#else +static bool arg_trust_all = true; +#endif STATIC_DESTRUCTOR_REGISTER(arg_gnutls_log, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_key, freep); @@ -219,9 +223,7 @@ static int process_http_upload( finished = true; for (;;) { - r = process_source(source, - journal_remote_server_global->compress, - journal_remote_server_global->seal); + r = process_source(source, journal_remote_server_global->file_flags); if (r == -EAGAIN) break; if (r < 0) { @@ -594,9 +596,13 @@ static int create_remoteserver( const char* trust) { int r, n, fd; - char **file; - r = journal_remote_server_init(s, arg_output, arg_split_mode, arg_compress, arg_seal); + r = journal_remote_server_init( + s, + arg_output, + arg_split_mode, + (arg_compress ? JOURNAL_COMPRESS : 0) | + (arg_seal ? JOURNAL_SEAL : 0)); if (r < 0) return r; @@ -932,6 +938,7 @@ static int parse_argv(int argc, char *argv[]) { break; case ARG_TRUST: +#if HAVE_GNUTLS if (arg_trust || arg_trust_all) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Confusing trusted CA configuration"); @@ -939,16 +946,14 @@ static int parse_argv(int argc, char *argv[]) { if (streq(optarg, "all")) arg_trust_all = true; else { -#if HAVE_GNUTLS arg_trust = strdup(optarg); if (!arg_trust) return log_oom(); -#else - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Option --trust is not available."); -#endif } - +#else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Option --trust is not available."); +#endif break; case 'o': diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c index 2ece32925..f8d068d6f 100644 --- a/src/journal-remote/journal-remote-parse.c +++ b/src/journal-remote/journal-remote-parse.c @@ -47,7 +47,7 @@ RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer) { return source; } -int process_source(RemoteSource *source, bool compress, bool seal) { +int process_source(RemoteSource *source, JournalFileFlags file_flags) { int r; assert(source); @@ -72,7 +72,7 @@ int process_source(RemoteSource *source, bool compress, bool seal) { &source->importer.iovw, &source->importer.ts, &source->importer.boot_id, - compress, seal); + file_flags); if (r == -EBADMSG) { log_warning_errno(r, "Entry is invalid, ignoring."); r = 0; diff --git a/src/journal-remote/journal-remote-parse.h b/src/journal-remote/journal-remote-parse.h index a5b51ad4d..703035b1e 100644 --- a/src/journal-remote/journal-remote-parse.h +++ b/src/journal-remote/journal-remote-parse.h @@ -17,4 +17,4 @@ typedef struct RemoteSource { RemoteSource* source_new(int fd, bool passive_fd, char *name, Writer *writer); void source_free(RemoteSource *source); -int process_source(RemoteSource *source, bool compress, bool seal); +int process_source(RemoteSource *source, JournalFileFlags file_flags); diff --git a/src/journal-remote/journal-remote-write.c b/src/journal-remote/journal-remote-write.c index b82cb1011..f4f3b6481 100644 --- a/src/journal-remote/journal-remote-write.c +++ b/src/journal-remote/journal-remote-write.c @@ -3,8 +3,10 @@ #include "alloc-util.h" #include "journal-remote.h" -static int do_rotate(JournaldFile **f, MMapCache *m, bool compress, bool seal) { - int r = journald_file_rotate(f, m, compress, UINT64_MAX, seal, NULL); +static int do_rotate(ManagedJournalFile **f, MMapCache *m, JournalFileFlags file_flags) { + int r; + + r = managed_journal_file_rotate(f, m, file_flags, UINT64_MAX, NULL); if (r < 0) { if (*f) log_error_errno(r, "Failed to rotate %s: %m", (*f)->file->path); @@ -40,7 +42,7 @@ static Writer* writer_free(Writer *w) { if (w->journal) { log_debug("Closing journal file %s.", w->journal->file->path); - journald_file_close(w->journal); + managed_journal_file_close(w->journal); } if (w->server && w->hashmap_key) @@ -57,11 +59,10 @@ static Writer* writer_free(Writer *w) { DEFINE_TRIVIAL_REF_UNREF_FUNC(Writer, writer, writer_free); int writer_write(Writer *w, - struct iovec_wrapper *iovw, - dual_timestamp *ts, - sd_id128_t *boot_id, - bool compress, - bool seal) { + const struct iovec_wrapper *iovw, + const dual_timestamp *ts, + const sd_id128_t *boot_id, + JournalFileFlags file_flags) { int r; assert(w); @@ -71,7 +72,7 @@ int writer_write(Writer *w, if (journal_file_rotate_suggested(w->journal->file, 0, LOG_DEBUG)) { log_info("%s: Journal header limits reached or header out-of-date, rotating", w->journal->file->path); - r = do_rotate(&w->journal, w->mmap, compress, seal); + r = do_rotate(&w->journal, w->mmap, file_flags); if (r < 0) return r; } @@ -87,7 +88,7 @@ int writer_write(Writer *w, return r; log_debug_errno(r, "%s: Write failed, rotating: %m", w->journal->file->path); - r = do_rotate(&w->journal, w->mmap, compress, seal); + r = do_rotate(&w->journal, w->mmap, file_flags); if (r < 0) return r; else diff --git a/src/journal-remote/journal-remote-write.h b/src/journal-remote/journal-remote-write.h index d97f6c674..2079214e2 100644 --- a/src/journal-remote/journal-remote-write.h +++ b/src/journal-remote/journal-remote-write.h @@ -1,13 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include "journald-file.h" #include "journal-importer.h" +#include "managed-journal-file.h" typedef struct RemoteServer RemoteServer; typedef struct Writer { - JournaldFile *journal; + ManagedJournalFile *journal; JournalMetrics metrics; MMapCache *mmap; @@ -26,11 +26,10 @@ Writer* writer_unref(Writer *w); DEFINE_TRIVIAL_CLEANUP_FUNC(Writer*, writer_unref); int writer_write(Writer *s, - struct iovec_wrapper *iovw, - dual_timestamp *ts, - sd_id128_t *boot_id, - bool compress, - bool seal); + const struct iovec_wrapper *iovw, + const dual_timestamp *ts, + const sd_id128_t *boot_id, + JournalFileFlags file_flags); typedef enum JournalWriteSplitMode { JOURNAL_WRITE_SPLIT_NONE, diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index e91b46473..d1f394b02 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -14,11 +14,11 @@ #include "errno-util.h" #include "escape.h" #include "fd-util.h" -#include "journald-file.h" #include "journal-remote-write.h" #include "journal-remote.h" #include "journald-native.h" #include "macro.h" +#include "managed-journal-file.h" #include "parse-util.h" #include "process-util.h" #include "socket-util.h" @@ -61,12 +61,17 @@ static int open_output(RemoteServer *s, Writer *w, const char* host) { assert_not_reached(); } - r = journald_file_open_reliably(filename, - O_RDWR|O_CREAT, 0640, - s->compress, UINT64_MAX, s->seal, - &w->metrics, - w->mmap, NULL, - NULL, &w->journal); + r = managed_journal_file_open_reliably( + filename, + O_RDWR|O_CREAT, + s->file_flags, + 0640, + UINT64_MAX, + &w->metrics, + w->mmap, + NULL, + NULL, + &w->journal); if (r < 0) return log_error_errno(r, "Failed to open output journal %s: %m", filename); @@ -302,8 +307,7 @@ int journal_remote_server_init( RemoteServer *s, const char *output, JournalWriteSplitMode split_mode, - bool compress, - bool seal) { + JournalFileFlags file_flags) { int r; @@ -313,8 +317,7 @@ int journal_remote_server_init( journal_remote_server_global = s; s->split_mode = split_mode; - s->compress = compress; - s->seal = seal; + s->file_flags = file_flags; if (output) s->output = output; @@ -391,7 +394,7 @@ int journal_remote_handle_raw_source( source = s->sources[fd]; assert(source->importer.fd == fd); - r = process_source(source, s->compress, s->seal); + r = process_source(source, s->file_flags); if (journal_importer_eof(&source->importer)) { size_t remaining; diff --git a/src/journal-remote/journal-remote.h b/src/journal-remote/journal-remote.h index 45176e964..facf1516e 100644 --- a/src/journal-remote/journal-remote.h +++ b/src/journal-remote/journal-remote.h @@ -38,8 +38,7 @@ struct RemoteServer { const char *output; /* either the output file or directory */ JournalWriteSplitMode split_mode; - bool compress; - bool seal; + JournalFileFlags file_flags; bool check_trust; }; extern RemoteServer *journal_remote_server_global; @@ -48,8 +47,7 @@ int journal_remote_server_init( RemoteServer *s, const char *output, JournalWriteSplitMode split_mode, - bool compress, - bool seal); + JournalFileFlags file_flags); int journal_remote_get_writer(RemoteServer *s, const char *host, Writer **writer); diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index fc52c546e..6ccea604e 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -569,11 +569,11 @@ finalize: static int parse_config(void) { const ConfigTableItem items[] = { - { "Upload", "URL", config_parse_string, 0, &arg_url }, - { "Upload", "ServerKeyFile", config_parse_path_or_ignore, 0, &arg_key }, - { "Upload", "ServerCertificateFile", config_parse_path_or_ignore, 0, &arg_cert }, - { "Upload", "TrustedCertificateFile", config_parse_path_or_ignore, 0, &arg_trust }, - { "Upload", "NetworkTimeoutSec", config_parse_sec, 0, &arg_network_timeout_usec }, + { "Upload", "URL", config_parse_string, CONFIG_PARSE_STRING_SAFE, &arg_url }, + { "Upload", "ServerKeyFile", config_parse_path_or_ignore, 0, &arg_key }, + { "Upload", "ServerCertificateFile", config_parse_path_or_ignore, 0, &arg_cert }, + { "Upload", "TrustedCertificateFile", config_parse_path_or_ignore, 0, &arg_trust }, + { "Upload", "NetworkTimeoutSec", config_parse_sec, 0, &arg_network_timeout_usec }, {} }; diff --git a/src/journal-remote/meson.build b/src/journal-remote/meson.build index 88c0aca9b..4bbbc1431 100644 --- a/src/journal-remote/meson.build +++ b/src/journal-remote/meson.build @@ -75,7 +75,7 @@ endif ############################################################ fuzzers += [ - [['src/journal-remote/fuzz-journal-remote.c'], + [files('fuzz-journal-remote.c'), [libsystemd_journal_remote, libshared], [], diff --git a/src/journal-remote/microhttpd-util.c b/src/journal-remote/microhttpd-util.c index e6a825449..48e4b1414 100644 --- a/src/journal-remote/microhttpd-util.c +++ b/src/journal-remote/microhttpd-util.c @@ -151,18 +151,17 @@ static int log_enable_gnutls_category(const char *cat) { } int setup_gnutls_logger(char **categories) { - char **cat; int r; gnutls_global_set_log_function(log_func_gnutls); - if (categories) { + if (categories) STRV_FOREACH(cat, categories) { r = log_enable_gnutls_category(*cat); if (r < 0) return r; } - } else + else log_reset_gnutls_level(); return 0; @@ -300,7 +299,7 @@ int check_permissions(struct MHD_Connection *connection, int *code, char **hostn #else int check_permissions(struct MHD_Connection *connection, int *code, char **hostname) { - return -EPERM; + assert_not_reached(); } int setup_gnutls_logger(char **categories) { diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c index 3c4a7c0a7..cff34fd58 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -33,6 +33,7 @@ #include "dissect-image.h" #include "fd-util.h" #include "fileio.h" +#include "format-table.h" #include "format-util.h" #include "fs-util.h" #include "fsprg.h" @@ -84,6 +85,7 @@ enum { }; static OutputMode arg_output = OUTPUT_SHORT; +static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; static bool arg_utc = false; static bool arg_follow = false; static bool arg_full = true; @@ -577,6 +579,11 @@ static int parse_argv(int argc, char *argv[]) { if (IN_SET(arg_output, OUTPUT_EXPORT, OUTPUT_JSON, OUTPUT_JSON_PRETTY, OUTPUT_JSON_SSE, OUTPUT_JSON_SEQ, OUTPUT_CAT)) arg_quiet = true; + if (OUTPUT_MODE_IS_JSON(arg_output)) + arg_json_format_flags = output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO; + else + arg_json_format_flags = JSON_FORMAT_OFF; + break; case 'l': @@ -1139,7 +1146,6 @@ static int parse_argv(int argc, char *argv[]) { } static int add_matches(sd_journal *j, char **args) { - char **i; bool have_term = false; assert(j); @@ -1327,7 +1333,7 @@ static int get_boots( bool skip_once; int r, count = 0; - BootId *head = NULL, *tail = NULL, *id; + BootId *head = NULL, *tail = NULL; const bool advance_older = boot_id && offset <= 0; sd_id128_t previous_boot_id; @@ -1441,8 +1447,9 @@ finish: } static int list_boots(sd_journal *j) { - int w, i, count; - BootId *id, *all_ids; + _cleanup_(table_unrefp) Table *table = NULL; + BootId *all_ids; + int count, i, r; assert(j); @@ -1452,23 +1459,37 @@ static int list_boots(sd_journal *j) { if (count == 0) return count; - pager_open(arg_pager_flags); + table = table_new("idx", "boot id", "first entry", "last entry"); + if (!table) + return log_oom(); - /* numbers are one less, but we need an extra char for the sign */ - w = DECIMAL_STR_WIDTH(count - 1) + 1; + if (arg_full) + table_set_width(table, 0); + + r = table_set_json_field_name(table, 0, "index"); + if (r < 0) + return log_error_errno(r, "Failed to set JSON field name of column 0: %m"); + + (void) table_set_sort(table, (size_t) 0); + (void) table_set_reverse(table, 0, arg_reverse); i = 0; LIST_FOREACH(boot_list, id, all_ids) { - char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX]; - - printf("% *i " SD_ID128_FORMAT_STR " %s—%s\n", - w, i - count + 1, - SD_ID128_FORMAT_VAL(id->id), - format_timestamp_maybe_utc(a, sizeof(a), id->first), - format_timestamp_maybe_utc(b, sizeof(b), id->last)); + r = table_add_many(table, + TABLE_INT, i - count + 1, + TABLE_SET_ALIGN_PERCENT, 100, + TABLE_ID128, id->id, + TABLE_TIMESTAMP, id->first, + TABLE_TIMESTAMP, id->last); + if (r < 0) + return table_log_add_error(r); i++; } + r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, !arg_quiet); + if (r < 0) + return table_log_print_error(r); + boot_id_free_all(all_ids); return 0; @@ -1562,7 +1583,7 @@ static int get_possible_units( return r; SD_JOURNAL_FOREACH_UNIQUE(j, data, size) { - char **pattern, *eq; + char *eq; size_t prefix; _cleanup_free_ char *u = NULL; @@ -1615,7 +1636,6 @@ static int get_possible_units( static int add_units(sd_journal *j) { _cleanup_strv_free_ char **patterns = NULL; int r, count = 0; - char **i; assert(j); @@ -1760,7 +1780,6 @@ static int add_facilities(sd_journal *j) { static int add_syslog_identifier(sd_journal *j) { int r; - char **i; assert(j); diff --git a/src/journal/journald-file.h b/src/journal/journald-file.h deleted file mode 100644 index 79c0ef824..000000000 --- a/src/journal/journald-file.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#include "journal-file.h" - -typedef struct { - JournalFile *file; -} JournaldFile; - -int journald_file_open( - int fd, - const char *fname, - int flags, - mode_t mode, - bool compress, - uint64_t compress_threshold_bytes, - bool seal, - JournalMetrics *metrics, - MMapCache *mmap_cache, - Set *deferred_closes, - JournaldFile *template, - JournaldFile **ret); - -int journald_file_set_offline(JournaldFile *f, bool wait); -bool journald_file_is_offlining(JournaldFile *f); -JournaldFile* journald_file_close(JournaldFile *f); -DEFINE_TRIVIAL_CLEANUP_FUNC(JournaldFile*, journald_file_close); - -int journald_file_open_reliably( - const char *fname, - int flags, - mode_t mode, - bool compress, - uint64_t compress_threshold_bytes, - bool seal, - JournalMetrics *metrics, - MMapCache *mmap_cache, - Set *deferred_closes, - JournaldFile *template, - JournaldFile **ret); - -JournaldFile* journald_file_initiate_close(JournaldFile *f, Set *deferred_closes); -int journald_file_rotate(JournaldFile **f, MMapCache *mmap_cache, bool compress, uint64_t compress_threshold_bytes, bool seal, Set *deferred_closes); diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c index 8accd7c7f..842882bc5 100644 --- a/src/journal/journald-rate-limit.c +++ b/src/journal/journald-rate-limit.c @@ -185,10 +185,10 @@ static unsigned burst_modulate(unsigned burst, uint64_t available) { } int journal_ratelimit_test(JournalRateLimit *r, const char *id, usec_t rl_interval, unsigned rl_burst, int priority, uint64_t available) { - uint64_t h; - JournalRateLimitGroup *g; + JournalRateLimitGroup *g, *found = NULL; JournalRateLimitPool *p; unsigned burst; + uint64_t h; usec_t ts; assert(id); @@ -208,23 +208,25 @@ int journal_ratelimit_test(JournalRateLimit *r, const char *id, usec_t rl_interv h = siphash24_string(id, r->hash_key); g = r->buckets[h % BUCKETS_MAX]; - LIST_FOREACH(bucket, g, g) - if (streq(g->id, id)) + LIST_FOREACH(bucket, i, g) + if (streq(i->id, id)) { + found = i; break; + } - if (!g) { - g = journal_ratelimit_group_new(r, id, rl_interval, ts); - if (!g) + if (!found) { + found = journal_ratelimit_group_new(r, id, rl_interval, ts); + if (!found) return -ENOMEM; } else - g->interval = rl_interval; + found->interval = rl_interval; if (rl_interval == 0 || rl_burst == 0) return 1; burst = burst_modulate(rl_burst, available); - p = &g->pools[priority_map[priority]]; + p = &found->pools[priority_map[priority]]; if (p->begin <= 0) { p->suppressed = 0; diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c index 9bfe22906..2b3d3dc03 100644 --- a/src/journal/journald-server.c +++ b/src/journal/journald-server.c @@ -29,7 +29,6 @@ #include "id128-util.h" #include "io-util.h" #include "journal-authenticate.h" -#include "journald-file.h" #include "journal-internal.h" #include "journal-vacuum.h" #include "journald-audit.h" @@ -242,7 +241,7 @@ static bool uid_for_system_journal(uid_t uid) { return uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY; } -static void server_add_acls(JournaldFile *f, uid_t uid) { +static void server_add_acls(ManagedJournalFile *f, uid_t uid) { assert(f); #if HAVE_ACL @@ -261,26 +260,46 @@ static int open_journal( Server *s, bool reliably, const char *fname, - int flags, + int open_flags, bool seal, JournalMetrics *metrics, - JournaldFile **ret) { + ManagedJournalFile **ret) { - _cleanup_(journald_file_closep) JournaldFile *f = NULL; + _cleanup_(managed_journal_file_closep) ManagedJournalFile *f = NULL; + JournalFileFlags file_flags; int r; assert(s); assert(fname); assert(ret); + file_flags = (s->compress.enabled ? JOURNAL_COMPRESS : 0) | (seal ? JOURNAL_SEAL : 0); + if (reliably) - r = journald_file_open_reliably(fname, flags, 0640, s->compress.enabled, - s->compress.threshold_bytes, seal, metrics, s->mmap, - s->deferred_closes, NULL, &f); + r = managed_journal_file_open_reliably( + fname, + open_flags, + file_flags, + 0640, + s->compress.threshold_bytes, + metrics, + s->mmap, + s->deferred_closes, + NULL, + &f); else - r = journald_file_open(-1, fname, flags, 0640, s->compress.enabled, - s->compress.threshold_bytes, seal, metrics, s->mmap, - s->deferred_closes, NULL, &f); + r = managed_journal_file_open( + -1, + fname, + open_flags, + file_flags, + 0640, + s->compress.threshold_bytes, + metrics, + s->mmap, + s->deferred_closes, + NULL, + &f); if (r < 0) return r; @@ -389,9 +408,9 @@ static int system_journal_open(Server *s, bool flush_requested, bool relinquish_ return r; } -static JournaldFile* find_journal(Server *s, uid_t uid) { +static ManagedJournalFile* find_journal(Server *s, uid_t uid) { _cleanup_free_ char *p = NULL; - JournaldFile *f; + ManagedJournalFile *f; int r; assert(s); @@ -434,7 +453,7 @@ static JournaldFile* find_journal(Server *s, uid_t uid) { /* Too many open? Then let's close one (or more) */ while (ordered_hashmap_size(s->user_journals) >= USER_JOURNALS_MAX) { assert_se(f = ordered_hashmap_steal_first(s->user_journals)); - (void) journald_file_close(f); + (void) managed_journal_file_close(f); } r = open_journal(s, true, p, O_RDWR|O_CREAT, s->seal, &s->system_storage.metrics, &f); @@ -443,7 +462,7 @@ static JournaldFile* find_journal(Server *s, uid_t uid) { r = ordered_hashmap_put(s->user_journals, UID_TO_PTR(uid), f); if (r < 0) { - (void) journald_file_close(f); + (void) managed_journal_file_close(f); return s->system_journal; } @@ -453,18 +472,24 @@ static JournaldFile* find_journal(Server *s, uid_t uid) { static int do_rotate( Server *s, - JournaldFile **f, + ManagedJournalFile **f, const char* name, bool seal, uint32_t uid) { + JournalFileFlags file_flags; int r; + assert(s); if (!*f) return -EINVAL; - r = journald_file_rotate(f, s->mmap, s->compress.enabled, s->compress.threshold_bytes, seal, s->deferred_closes); + file_flags = + (s->compress.enabled ? JOURNAL_COMPRESS : 0)| + (seal ? JOURNAL_SEAL : 0); + + r = managed_journal_file_rotate(f, s->mmap, file_flags, s->compress.threshold_bytes, s->deferred_closes); if (r < 0) { if (*f) return log_error_errno(r, "Failed to rotate %s: %m", (*f)->file->path); @@ -477,15 +502,15 @@ static int do_rotate( } static void server_process_deferred_closes(Server *s) { - JournaldFile *f; + ManagedJournalFile *f; /* Perform any deferred closes which aren't still offlining. */ SET_FOREACH(f, s->deferred_closes) { - if (journald_file_is_offlining(f)) + if (managed_journal_file_is_offlining(f)) continue; (void) set_remove(s->deferred_closes, f); - (void) journald_file_close(f); + (void) managed_journal_file_close(f); } } @@ -501,10 +526,10 @@ static void server_vacuum_deferred_closes(Server *s) { /* And now, let's close some more until we reach the limit again. */ while (set_size(s->deferred_closes) >= DEFERRED_CLOSES_MAX) { - JournaldFile *f; + ManagedJournalFile *f; assert_se(f = set_steal_first(s->deferred_closes)); - journald_file_close(f); + managed_journal_file_close(f); } } @@ -527,7 +552,7 @@ static int vacuum_offline_user_journals(Server *s) { _cleanup_close_ int fd = -1; const char *a, *b; struct dirent *de; - JournaldFile *f; + ManagedJournalFile *f; uid_t uid; errno = 0; @@ -575,18 +600,19 @@ static int vacuum_offline_user_journals(Server *s) { server_vacuum_deferred_closes(s); /* Open the file briefly, so that we can archive it */ - r = journald_file_open(fd, - full, - O_RDWR, - 0640, - s->compress.enabled, - s->compress.threshold_bytes, - s->seal, - &s->system_storage.metrics, - s->mmap, - s->deferred_closes, - NULL, - &f); + r = managed_journal_file_open( + fd, + full, + O_RDWR, + (s->compress.enabled ? JOURNAL_COMPRESS : 0) | + (s->seal ? JOURNAL_SEAL : 0), + 0640, + s->compress.threshold_bytes, + &s->system_storage.metrics, + s->mmap, + s->deferred_closes, + NULL, + &f); if (r < 0) { log_warning_errno(r, "Failed to read journal file %s for rotation, trying to move it out of the way: %m", full); @@ -599,13 +625,13 @@ static int vacuum_offline_user_journals(Server *s) { continue; } - TAKE_FD(fd); /* Donated to journald_file_open() */ + TAKE_FD(fd); /* Donated to managed_journal_file_open() */ r = journal_file_archive(f->file, NULL); if (r < 0) log_debug_errno(r, "Failed to archive journal file '%s', ignoring: %m", full); - journald_file_initiate_close(f, s->deferred_closes); + managed_journal_file_initiate_close(f, s->deferred_closes); f = NULL; } @@ -613,7 +639,7 @@ static int vacuum_offline_user_journals(Server *s) { } void server_rotate(Server *s) { - JournaldFile *f; + ManagedJournalFile *f; void *k; int r; @@ -642,17 +668,17 @@ void server_rotate(Server *s) { } void server_sync(Server *s) { - JournaldFile *f; + ManagedJournalFile *f; int r; if (s->system_journal) { - r = journald_file_set_offline(s->system_journal, false); + r = managed_journal_file_set_offline(s->system_journal, false); if (r < 0) log_warning_errno(r, "Failed to sync system journal, ignoring: %m"); } ORDERED_HASHMAP_FOREACH(f, s->user_journals) { - r = journald_file_set_offline(f, false); + r = managed_journal_file_set_offline(f, false); if (r < 0) log_warning_errno(r, "Failed to sync user journal, ignoring: %m"); } @@ -797,7 +823,7 @@ static bool shall_try_append_again(JournalFile *f, int r) { static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, size_t n, int priority) { bool vacuumed = false, rotate = false; struct dual_timestamp ts; - JournaldFile *f; + ManagedJournalFile *f; int r; assert(s); @@ -1207,7 +1233,7 @@ finish: if (s->system_journal) journal_file_post_change(s->system_journal->file); - s->runtime_journal = journald_file_close(s->runtime_journal); + s->runtime_journal = managed_journal_file_close(s->runtime_journal); if (r >= 0) (void) rm_rf(s->runtime_storage.path, REMOVE_ROOT); @@ -1247,9 +1273,9 @@ static int server_relinquish_var(Server *s) { (void) system_journal_open(s, false, true); - s->system_journal = journald_file_close(s->system_journal); - ordered_hashmap_clear_with_destructor(s->user_journals, journald_file_close); - set_clear_with_destructor(s->deferred_closes, journald_file_close); + s->system_journal = managed_journal_file_close(s->system_journal); + ordered_hashmap_clear_with_destructor(s->user_journals, managed_journal_file_close); + set_clear_with_destructor(s->deferred_closes, managed_journal_file_close); fn = strjoina(s->runtime_directory, "/flushed"); if (unlink(fn) < 0 && errno != ENOENT) @@ -1448,12 +1474,82 @@ static int dispatch_sigusr2(sd_event_source *es, const struct signalfd_siginfo * } static int dispatch_sigterm(sd_event_source *es, const struct signalfd_siginfo *si, void *userdata) { + _cleanup_(sd_event_source_disable_unrefp) sd_event_source *news = NULL; Server *s = userdata; + int r; assert(s); log_received_signal(LOG_INFO, si); + (void) sd_event_source_set_enabled(es, false); /* Make sure this handler is called at most once */ + + /* So on one hand we want to ensure that SIGTERMs are definitely handled in appropriate, bounded + * time. On the other hand we want that everything pending is first comprehensively processed and + * written to disk. These goals are incompatible, hence we try to find a middle ground: we'll process + * SIGTERM with high priority, but from the handler (this one right here) we'll install two new event + * sources: one low priority idle one that will issue the exit once everything else is processed (and + * which is hopefully the regular, clean codepath); and one high priority timer that acts as safety + * net: if our idle handler isn't run within 10s, we'll exit anyway. + * + * TLDR: we'll exit either when everything is processed, or after 10s max, depending on what happens + * first. + * + * Note that exiting before the idle event is hit doesn't typically mean that we lose any data, as + * messages will remain queued in the sockets they came in from, and thus can be processed when we + * start up next – unless we are going down for the final system shutdown, in which case everything + * is lost. */ + + r = sd_event_add_defer(s->event, &news, NULL, NULL); /* NULL handler means → exit when triggered */ + if (r < 0) { + log_error_errno(r, "Failed to allocate exit idle event handler: %m"); + goto fail; + } + + (void) sd_event_source_set_description(news, "exit-idle"); + + /* Run everything relevant before this. */ + r = sd_event_source_set_priority(news, SD_EVENT_PRIORITY_NORMAL+20); + if (r < 0) { + log_error_errno(r, "Failed to adjust priority of exit idle event handler: %m"); + goto fail; + } + + /* Give up ownership, so that this event source is freed automatically when the event loop is freed. */ + r = sd_event_source_set_floating(news, true); + if (r < 0) { + log_error_errno(r, "Failed to make exit idle event handler floating: %m"); + goto fail; + } + + news = sd_event_source_unref(news); + + r = sd_event_add_time_relative(s->event, &news, CLOCK_MONOTONIC, 10 * USEC_PER_SEC, 0, NULL, NULL); + if (r < 0) { + log_error_errno(r, "Failed to allocate exit timeout event handler: %m"); + goto fail; + } + + (void) sd_event_source_set_description(news, "exit-timeout"); + + r = sd_event_source_set_priority(news, SD_EVENT_PRIORITY_IMPORTANT-20); /* This is a safety net, with highest priority */ + if (r < 0) { + log_error_errno(r, "Failed to adjust priority of exit timeout event handler: %m"); + goto fail; + } + + r = sd_event_source_set_floating(news, true); + if (r < 0) { + log_error_errno(r, "Failed to make exit timeout event handler floating: %m"); + goto fail; + } + + news = sd_event_source_unref(news); + + log_debug("Exit event sources are now pending."); + return 0; + +fail: sd_event_exit(s->event, 0); return 0; } @@ -1505,8 +1601,8 @@ static int setup_signals(Server *s) { if (r < 0) return r; - /* Let's process SIGTERM late, so that we flush all queued messages to disk before we exit */ - r = sd_event_source_set_priority(s->sigterm_event_source, SD_EVENT_PRIORITY_NORMAL+20); + /* Let's process SIGTERM early, so that we definitely react to it */ + r = sd_event_source_set_priority(s->sigterm_event_source, SD_EVENT_PRIORITY_IMPORTANT-10); if (r < 0) return r; @@ -1516,7 +1612,7 @@ static int setup_signals(Server *s) { if (r < 0) return r; - r = sd_event_source_set_priority(s->sigint_event_source, SD_EVENT_PRIORITY_NORMAL+20); + r = sd_event_source_set_priority(s->sigint_event_source, SD_EVENT_PRIORITY_IMPORTANT-10); if (r < 0) return r; @@ -2444,7 +2540,7 @@ int server_init(Server *s, const char *namespace) { void server_maybe_append_tags(Server *s) { #if HAVE_GCRYPT - JournaldFile *f; + ManagedJournalFile *f; usec_t n; n = now(CLOCK_REALTIME); @@ -2463,17 +2559,17 @@ void server_done(Server *s) { free(s->namespace); free(s->namespace_field); - set_free_with_destructor(s->deferred_closes, journald_file_close); + set_free_with_destructor(s->deferred_closes, managed_journal_file_close); while (s->stdout_streams) stdout_stream_free(s->stdout_streams); client_context_flush_all(s); - (void) journald_file_close(s->system_journal); - (void) journald_file_close(s->runtime_journal); + (void) managed_journal_file_close(s->system_journal); + (void) managed_journal_file_close(s->runtime_journal); - ordered_hashmap_free_with_destructor(s->user_journals, journald_file_close); + ordered_hashmap_free_with_destructor(s->user_journals, managed_journal_file_close); varlink_server_unref(s->varlink_server); diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 92c78a0d7..ea51515db 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -10,11 +10,11 @@ typedef struct Server Server; #include "conf-parser.h" #include "hashmap.h" -#include "journald-file.h" #include "journald-context.h" #include "journald-rate-limit.h" #include "journald-stream.h" #include "list.h" +#include "managed-journal-file.h" #include "prioq.h" #include "ratelimit.h" #include "time-util.h" @@ -89,8 +89,8 @@ struct Server { sd_event_source *watchdog_event_source; sd_event_source *idle_event_source; - JournaldFile *runtime_journal; - JournaldFile *system_journal; + ManagedJournalFile *runtime_journal; + ManagedJournalFile *system_journal; OrderedHashmap *user_journals; uint64_t seqnum; diff --git a/src/journal/journald-file.c b/src/journal/managed-journal-file.c similarity index 72% rename from src/journal/journald-file.c rename to src/journal/managed-journal-file.c index 35ca30538..7e89bb30d 100644 --- a/src/journal/journald-file.c +++ b/src/journal/managed-journal-file.c @@ -5,10 +5,11 @@ #include "chattr-util.h" #include "copy.h" +#include "errno-util.h" #include "fd-util.h" #include "format-util.h" #include "journal-authenticate.h" -#include "journald-file.h" +#include "managed-journal-file.h" #include "path-util.h" #include "random-util.h" #include "set.h" @@ -18,12 +19,12 @@ #define PAYLOAD_BUFFER_SIZE (16U * 1024U) #define MINIMUM_HOLE_SIZE (1U * 1024U * 1024U / 2U) -static int journald_file_truncate(JournalFile *f) { +static int managed_journal_file_truncate(JournalFile *f) { uint64_t p; int r; /* truncate excess from the end of archives */ - r = journal_file_tail_end(f, &p); + r = journal_file_tail_end_by_pread(f, &p); if (r < 0) return log_debug_errno(r, "Failed to determine end of tail object: %m"); @@ -31,12 +32,12 @@ static int journald_file_truncate(JournalFile *f) { f->header->arena_size = htole64(p - le64toh(f->header->header_size)); if (ftruncate(f->fd, p) < 0) - log_debug_errno(errno, "Failed to truncate %s: %m", f->path); + return log_debug_errno(errno, "Failed to truncate %s: %m", f->path); - return 0; + return journal_file_fstat(f); } -static int journald_file_entry_array_punch_hole(JournalFile *f, uint64_t p, uint64_t n_entries) { +static int managed_journal_file_entry_array_punch_hole(JournalFile *f, uint64_t p, uint64_t n_entries) { Object o; uint64_t offset, sz, n_items = 0, n_unused; int r; @@ -45,7 +46,7 @@ static int journald_file_entry_array_punch_hole(JournalFile *f, uint64_t p, uint return 0; for (uint64_t q = p; q != 0; q = le64toh(o.entry_array.next_entry_array_offset)) { - r = journal_file_read_object(f, OBJECT_ENTRY_ARRAY, q, &o); + r = journal_file_read_object_header(f, OBJECT_ENTRY_ARRAY, q, &o); if (r < 0) return r; @@ -72,19 +73,44 @@ static int journald_file_entry_array_punch_hole(JournalFile *f, uint64_t p, uint if (sz < MINIMUM_HOLE_SIZE) return 0; - if (fallocate(f->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, sz) < 0) + if (p == le64toh(f->header->tail_object_offset) && !f->seal) { + ssize_t n; + + o.object.size = htole64(offset - p); + + n = pwrite(f->fd, &o, sizeof(EntryArrayObject), p); + if (n < 0) + return log_debug_errno(errno, "Failed to modify entry array object size: %m"); + if ((size_t) n != sizeof(EntryArrayObject)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Short pwrite() while modifying entry array object size."); + + f->header->arena_size = htole64(ALIGN64(offset) - le64toh(f->header->header_size)); + + if (ftruncate(f->fd, ALIGN64(offset)) < 0) + return log_debug_errno(errno, "Failed to truncate %s: %m", f->path); + + return 0; + } + + if (fallocate(f->fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, sz) < 0) { + if (ERRNO_IS_NOT_SUPPORTED(errno)) { + log_debug("Hole punching not supported by backing file system, skipping."); + return -EOPNOTSUPP; /* Make recognizable */ + } + return log_debug_errno(errno, "Failed to punch hole in entry array of %s: %m", f->path); + } return 0; } -static int journald_file_punch_holes(JournalFile *f) { +static int managed_journal_file_punch_holes(JournalFile *f) { HashItem items[PAYLOAD_BUFFER_SIZE / sizeof(HashItem)]; uint64_t p, sz; ssize_t n = SSIZE_MAX; int r; - r = journald_file_entry_array_punch_hole( + r = managed_journal_file_entry_array_punch_hole( f, le64toh(f->header->entry_array_offset), le64toh(f->header->n_entries)); if (r < 0) return r; @@ -93,9 +119,10 @@ static int journald_file_punch_holes(JournalFile *f) { sz = le64toh(f->header->data_hash_table_size); for (uint64_t i = p; i < p + sz && n > 0; i += n) { - n = pread(f->fd, items, MIN(sizeof(items), p + sz - i), i); + size_t m = MIN(sizeof(items), p + sz - i); + n = pread(f->fd, items, m, i); if (n < 0) - return n; + return log_debug_errno(errno, "Failed to read hash table items: %m"); /* Let's ignore any partial hash items by rounding down to the nearest multiple of HashItem. */ n -= n % sizeof(HashItem); @@ -106,7 +133,7 @@ static int journald_file_punch_holes(JournalFile *f) { for (uint64_t q = le64toh(items[j].head_hash_offset); q != 0; q = le64toh(o.data.next_hash_offset)) { - r = journal_file_read_object(f, OBJECT_DATA, q, &o); + r = journal_file_read_object_header(f, OBJECT_DATA, q, &o); if (r < 0) { log_debug_errno(r, "Invalid data object: %m, ignoring"); break; @@ -115,8 +142,12 @@ static int journald_file_punch_holes(JournalFile *f) { if (le64toh(o.data.n_entries) == 0) continue; - (void) journald_file_entry_array_punch_hole( - f, le64toh(o.data.entry_array_offset), le64toh(o.data.n_entries) - 1); + r = managed_journal_file_entry_array_punch_hole( + f, le64toh(o.data.entry_array_offset), le64toh(o.data.n_entries) - 1); + if (r == -EOPNOTSUPP) + return -EOPNOTSUPP; + + /* Ignore other errors */ } } } @@ -127,7 +158,7 @@ static int journald_file_punch_holes(JournalFile *f) { /* This may be called from a separate thread to prevent blocking the caller for the duration of fsync(). * As a result we use atomic operations on f->offline_state for inter-thread communications with * journal_file_set_offline() and journal_file_set_online(). */ -static void journald_file_set_offline_internal(JournaldFile *f) { +static void managed_journal_file_set_offline_internal(ManagedJournalFile *f) { int r; assert(f); @@ -153,8 +184,8 @@ static void journald_file_set_offline_internal(JournaldFile *f) { case OFFLINE_SYNCING: if (f->file->archive) { - (void) journald_file_truncate(f->file); - (void) journald_file_punch_holes(f->file); + (void) managed_journal_file_truncate(f->file); + (void) managed_journal_file_punch_holes(f->file); } (void) fsync(f->file->fd); @@ -179,7 +210,10 @@ static void journald_file_set_offline_internal(JournaldFile *f) { log_debug_errno(r, "Failed to re-enable copy-on-write for %s: %m, rewriting file", f->file->path); - r = copy_file_atomic(f->file->path, f->file->path, f->file->mode, 0, FS_NOCOW_FL, COPY_REPLACE | COPY_FSYNC); + r = copy_file_atomic(FORMAT_PROC_FD_PATH(f->file->fd), f->file->path, f->file->mode, + 0, + FS_NOCOW_FL, + COPY_REPLACE | COPY_FSYNC | COPY_HOLES | COPY_ALL_XATTRS); if (r < 0) { log_debug_errno(r, "Failed to rewrite %s: %m", f->file->path); continue; @@ -202,18 +236,18 @@ static void journald_file_set_offline_internal(JournaldFile *f) { } } -static void * journald_file_set_offline_thread(void *arg) { - JournaldFile *f = arg; +static void * managed_journal_file_set_offline_thread(void *arg) { + ManagedJournalFile *f = arg; (void) pthread_setname_np(pthread_self(), "journal-offline"); - journald_file_set_offline_internal(f); + managed_journal_file_set_offline_internal(f); return NULL; } /* Trigger a restart if the offline thread is mid-flight in a restartable state. */ -static bool journald_file_set_offline_try_restart(JournaldFile *f) { +static bool managed_journal_file_set_offline_try_restart(ManagedJournalFile *f) { for (;;) { switch (f->file->offline_state) { case OFFLINE_AGAIN_FROM_SYNCING: @@ -251,7 +285,7 @@ static bool journald_file_set_offline_try_restart(JournaldFile *f) { * and joined, or if none exists the offline is simply performed in this * context without involving another thread. */ -int journald_file_set_offline(JournaldFile *f, bool wait) { +int managed_journal_file_set_offline(ManagedJournalFile *f, bool wait) { int target_state; bool restarted; int r; @@ -270,11 +304,11 @@ int journald_file_set_offline(JournaldFile *f, bool wait) { * we must also join any potentially lingering offline thread when already in * the desired offline state. */ - if (!journald_file_is_offlining(f) && f->file->header->state == target_state) + if (!managed_journal_file_is_offlining(f) && f->file->header->state == target_state) return journal_file_set_offline_thread_join(f->file); /* Restart an in-flight offline thread and wait if needed, or join a lingering done one. */ - restarted = journald_file_set_offline_try_restart(f); + restarted = managed_journal_file_set_offline_try_restart(f); if ((restarted && wait) || !restarted) { r = journal_file_set_offline_thread_join(f->file); if (r < 0) @@ -288,7 +322,7 @@ int journald_file_set_offline(JournaldFile *f, bool wait) { f->file->offline_state = OFFLINE_SYNCING; if (wait) /* Without using a thread if waiting. */ - journald_file_set_offline_internal(f); + managed_journal_file_set_offline_internal(f); else { sigset_t ss, saved_ss; int k; @@ -302,7 +336,7 @@ int journald_file_set_offline(JournaldFile *f, bool wait) { if (r > 0) return -r; - r = pthread_create(&f->file->offline_thread, NULL, journald_file_set_offline_thread, f); + r = pthread_create(&f->file->offline_thread, NULL, managed_journal_file_set_offline_thread, f); k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL); if (r > 0) { @@ -316,7 +350,7 @@ int journald_file_set_offline(JournaldFile *f, bool wait) { return 0; } -bool journald_file_is_offlining(JournaldFile *f) { +bool managed_journal_file_is_offlining(ManagedJournalFile *f) { assert(f); __sync_synchronize(); @@ -327,7 +361,7 @@ bool journald_file_is_offlining(JournaldFile *f) { return true; } -JournaldFile* journald_file_close(JournaldFile *f) { +ManagedJournalFile* managed_journal_file_close(ManagedJournalFile *f) { if (!f) return NULL; @@ -349,36 +383,35 @@ JournaldFile* journald_file_close(JournaldFile *f) { sd_event_source_disable_unref(f->file->post_change_timer); } - journald_file_set_offline(f, true); + managed_journal_file_set_offline(f, true); journal_file_close(f->file); return mfree(f); } -int journald_file_open( +int managed_journal_file_open( int fd, const char *fname, - int flags, + int open_flags, + JournalFileFlags file_flags, mode_t mode, - bool compress, uint64_t compress_threshold_bytes, - bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, Set *deferred_closes, - JournaldFile *template, - JournaldFile **ret) { - _cleanup_free_ JournaldFile *f = NULL; + ManagedJournalFile *template, + ManagedJournalFile **ret) { + _cleanup_free_ ManagedJournalFile *f = NULL; int r; - set_clear_with_destructor(deferred_closes, journald_file_close); + set_clear_with_destructor(deferred_closes, managed_journal_file_close); - f = new0(JournaldFile, 1); + f = new0(ManagedJournalFile, 1); if (!f) return -ENOMEM; - r = journal_file_open(fd, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, + r = journal_file_open(fd, fname, open_flags, file_flags, mode, compress_threshold_bytes, metrics, mmap_cache, template ? template->file : NULL, &f->file); if (r < 0) return r; @@ -389,7 +422,7 @@ int journald_file_open( } -JournaldFile* journald_file_initiate_close(JournaldFile *f, Set *deferred_closes) { +ManagedJournalFile* managed_journal_file_initiate_close(ManagedJournalFile *f, Set *deferred_closes) { int r; assert(f); @@ -399,24 +432,23 @@ JournaldFile* journald_file_initiate_close(JournaldFile *f, Set *deferred_closes if (r < 0) log_debug_errno(r, "Failed to add file to deferred close set, closing immediately."); else { - (void) journald_file_set_offline(f, false); + (void) managed_journal_file_set_offline(f, false); return NULL; } } - return journald_file_close(f); + return managed_journal_file_close(f); } -int journald_file_rotate( - JournaldFile **f, +int managed_journal_file_rotate( + ManagedJournalFile **f, MMapCache *mmap_cache, - bool compress, + JournalFileFlags file_flags, uint64_t compress_threshold_bytes, - bool seal, Set *deferred_closes) { _cleanup_free_ char *path = NULL; - JournaldFile *new_file = NULL; + ManagedJournalFile *new_file = NULL; int r; assert(f); @@ -426,42 +458,40 @@ int journald_file_rotate( if (r < 0) return r; - r = journald_file_open( + r = managed_journal_file_open( -1, path, - (*f)->file->flags, + (*f)->file->open_flags, + file_flags, (*f)->file->mode, - compress, compress_threshold_bytes, - seal, NULL, /* metrics */ mmap_cache, deferred_closes, *f, /* template */ &new_file); - journald_file_initiate_close(*f, deferred_closes); + managed_journal_file_initiate_close(*f, deferred_closes); *f = new_file; return r; } -int journald_file_open_reliably( +int managed_journal_file_open_reliably( const char *fname, - int flags, + int open_flags, + JournalFileFlags file_flags, mode_t mode, - bool compress, uint64_t compress_threshold_bytes, - bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, Set *deferred_closes, - JournaldFile *template, - JournaldFile **ret) { + ManagedJournalFile *template, + ManagedJournalFile **ret) { int r; - r = journald_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, + r = managed_journal_file_open(-1, fname, open_flags, file_flags, mode, compress_threshold_bytes, metrics, mmap_cache, deferred_closes, template, ret); if (!IN_SET(r, -EBADMSG, /* Corrupted */ @@ -475,10 +505,10 @@ int journald_file_open_reliably( -ETXTBSY)) /* File is from the future */ return r; - if ((flags & O_ACCMODE) == O_RDONLY) + if ((open_flags & O_ACCMODE) == O_RDONLY) return r; - if (!(flags & O_CREAT)) + if (!(open_flags & O_CREAT)) return r; if (!endswith(fname, ".journal")) @@ -491,6 +521,6 @@ int journald_file_open_reliably( if (r < 0) return r; - return journald_file_open(-1, fname, flags, mode, compress, compress_threshold_bytes, seal, metrics, + return managed_journal_file_open(-1, fname, open_flags, file_flags, mode, compress_threshold_bytes, metrics, mmap_cache, deferred_closes, template, ret); } diff --git a/src/journal/managed-journal-file.h b/src/journal/managed-journal-file.h new file mode 100644 index 000000000..0ac69a798 --- /dev/null +++ b/src/journal/managed-journal-file.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "journal-file.h" + +typedef struct { + JournalFile *file; +} ManagedJournalFile; + +int managed_journal_file_open( + int fd, + const char *fname, + int open_flags, + JournalFileFlags file_flags, + mode_t mode, + uint64_t compress_threshold_bytes, + JournalMetrics *metrics, + MMapCache *mmap_cache, + Set *deferred_closes, + ManagedJournalFile *template, + ManagedJournalFile **ret); + +int managed_journal_file_set_offline(ManagedJournalFile *f, bool wait); +bool managed_journal_file_is_offlining(ManagedJournalFile *f); +ManagedJournalFile* managed_journal_file_close(ManagedJournalFile *f); +DEFINE_TRIVIAL_CLEANUP_FUNC(ManagedJournalFile*, managed_journal_file_close); + +int managed_journal_file_open_reliably( + const char *fname, + int open_flags, + JournalFileFlags file_flags, + mode_t mode, + uint64_t compress_threshold_bytes, + JournalMetrics *metrics, + MMapCache *mmap_cache, + Set *deferred_closes, + ManagedJournalFile *template, + ManagedJournalFile **ret); + +ManagedJournalFile* managed_journal_file_initiate_close(ManagedJournalFile *f, Set *deferred_closes); +int managed_journal_file_rotate(ManagedJournalFile **f, MMapCache *mmap_cache, JournalFileFlags file_flags, uint64_t compress_threshold_bytes, Set *deferred_closes); diff --git a/src/journal/meson.build b/src/journal/meson.build index 1372849cb..1e41ea149 100644 --- a/src/journal/meson.build +++ b/src/journal/meson.build @@ -7,8 +7,6 @@ sources = files( 'journald-console.h', 'journald-context.c', 'journald-context.h', - 'journald-file.c', - 'journald-file.h', 'journald-kmsg.c', 'journald-kmsg.h', 'journald-native.c', @@ -23,6 +21,8 @@ sources = files( 'journald-syslog.h', 'journald-wall.c', 'journald-wall.h', + 'managed-journal-file.c', + 'managed-journal-file.h', ) sources += custom_target( @@ -77,7 +77,7 @@ endif ############################################################ tests += [ - [['src/journal/test-journal-syslog.c'], + [files('test-journal-syslog.c'), [libjournal_core, libshared], [threads, @@ -85,67 +85,67 @@ tests += [ liblz4, libselinux]], - [['src/journal/test-journal-config.c'], + [files('test-journal-config.c'), [libjournal_core, libshared], [libxz, liblz4, libselinux]], - [['src/journal/test-journal.c'], + [files('test-journal.c'), [libjournal_core, libshared]], - [['src/journal/test-journal-stream.c'], + [files('test-journal-stream.c'), [libjournal_core, libshared]], - [['src/journal/test-journal-flush.c'], + [files('test-journal-flush.c'), [libjournal_core, libshared]], - [['src/journal/test-journal-verify.c'], + [files('test-journal-verify.c'), [libjournal_core, libshared]], - [['src/journal/test-journal-interleaving.c'], + [files('test-journal-interleaving.c'), [libjournal_core, libshared]], ] fuzzers += [ - [['src/journal/fuzz-journald-audit.c', - 'src/journal/fuzz-journald.c'], + [files('fuzz-journald-audit.c', + 'fuzz-journald.c'), [libjournal_core, libshared], [libselinux]], - [['src/journal/fuzz-journald-kmsg.c', - 'src/journal/fuzz-journald.c'], + [files('fuzz-journald-kmsg.c', + 'fuzz-journald.c'), [libjournal_core, libshared], [libselinux]], - [['src/journal/fuzz-journald-native.c', - 'src/journal/fuzz-journald.c'], + [files('fuzz-journald-native.c', + 'fuzz-journald.c'), [libjournal_core, libshared], [libselinux]], - [['src/journal/fuzz-journald-native-fd.c', - 'src/journal/fuzz-journald.c'], + [files('fuzz-journald-native-fd.c', + 'fuzz-journald.c'), [libjournal_core, libshared], [libselinux]], - [['src/journal/fuzz-journald-stream.c', - 'src/journal/fuzz-journald.c'], + [files('fuzz-journald-stream.c', + 'fuzz-journald.c'), [libjournal_core, libshared], [libselinux]], - [['src/journal/fuzz-journald-syslog.c', - 'src/journal/fuzz-journald.c'], + [files('fuzz-journald-syslog.c', + 'fuzz-journald.c'), [libjournal_core, libshared], [libselinux]], diff --git a/src/journal/test-journal-config.c b/src/journal/test-journal-config.c index bd0de9600..1a6c531f4 100644 --- a/src/journal/test-journal-config.c +++ b/src/journal/test-journal-config.c @@ -3,6 +3,7 @@ #include #include "journald-server.h" +#include "tests.h" #define _COMPRESS_PARSE_CHECK(str, enab, thresh, varname) \ do { \ @@ -17,7 +18,7 @@ #define COMPRESS_PARSE_CHECK(str, enabled, threshold) \ _COMPRESS_PARSE_CHECK(str, enabled, threshold, conf##__COUNTER__) -static void test_config_compress(void) { +TEST(config_compress) { COMPRESS_PARSE_CHECK("yes", true, 111); COMPRESS_PARSE_CHECK("no", false, 111); COMPRESS_PARSE_CHECK("y", true, 111); @@ -46,8 +47,4 @@ static void test_config_compress(void) { COMPRESS_PARSE_CHECK("", true, UINT64_MAX); } -int main(int argc, char *argv[]) { - test_config_compress(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c index dc05126b5..06eeb1a57 100644 --- a/src/journal/test-journal-flush.c +++ b/src/journal/test-journal-flush.c @@ -7,9 +7,9 @@ #include "alloc-util.h" #include "chattr-util.h" -#include "journald-file.h" #include "journal-internal.h" #include "macro.h" +#include "managed-journal-file.h" #include "path-util.h" #include "string-util.h" @@ -17,7 +17,7 @@ int main(int argc, char *argv[]) { _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL; _cleanup_free_ char *fn = NULL; char dn[] = "/var/tmp/test-journal-flush.XXXXXX"; - JournaldFile *new_journal = NULL; + ManagedJournalFile *new_journal = NULL; sd_journal *j = NULL; unsigned n = 0; int r; @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) { fn = path_join(dn, "test.journal"); - r = journald_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, 0, false, NULL, m, NULL, NULL, &new_journal); + r = managed_journal_file_open(-1, fn, O_CREAT|O_RDWR, 0, 0644, 0, NULL, m, NULL, NULL, &new_journal); assert_se(r >= 0); if (argc > 1) @@ -66,7 +66,7 @@ int main(int argc, char *argv[]) { sd_journal_close(j); - (void) journald_file_close(new_journal); + (void) managed_journal_file_close(new_journal); unlink(fn); assert_se(rmdir(dn) == 0); diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c index c543b87b6..a27ffe560 100644 --- a/src/journal/test-journal-interleaving.c +++ b/src/journal/test-journal-interleaving.c @@ -8,9 +8,9 @@ #include "alloc-util.h" #include "chattr-util.h" #include "io-util.h" -#include "journald-file.h" #include "journal-vacuum.h" #include "log.h" +#include "managed-journal-file.h" #include "parse-util.h" #include "rm-rf.h" #include "tests.h" @@ -33,22 +33,22 @@ _noreturn_ static void log_assert_errno(const char *text, int error, const char log_assert_errno(#expr, -_r_, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \ } while (false) -static JournaldFile *test_open(const char *name) { +static ManagedJournalFile *test_open(const char *name) { _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL; - JournaldFile *f; + ManagedJournalFile *f; m = mmap_cache_new(); assert_se(m != NULL); - assert_ret(journald_file_open(-1, name, O_RDWR|O_CREAT, 0644, true, UINT64_MAX, false, NULL, m, NULL, NULL, &f)); + assert_ret(managed_journal_file_open(-1, name, O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644, UINT64_MAX, NULL, m, NULL, NULL, &f)); return f; } -static void test_close(JournaldFile *f) { - (void) journald_file_close(f); +static void test_close(ManagedJournalFile *f) { + (void) managed_journal_file_close(f); } -static void append_number(JournaldFile *f, int n, uint64_t *seqnum) { +static void append_number(ManagedJournalFile *f, int n, uint64_t *seqnum) { char *p; dual_timestamp ts; static dual_timestamp previous_ts = {}; @@ -70,7 +70,7 @@ static void append_number(JournaldFile *f, int n, uint64_t *seqnum) { free(p); } -static void test_check_number (sd_journal *j, int n) { +static void test_check_number(sd_journal *j, int n) { const void *d; _cleanup_free_ char *k; size_t l; @@ -84,7 +84,7 @@ static void test_check_number (sd_journal *j, int n) { assert_se(n == x); } -static void test_check_numbers_down (sd_journal *j, int count) { +static void test_check_numbers_down(sd_journal *j, int count) { int i; for (i = 1; i <= count; i++) { @@ -99,7 +99,7 @@ static void test_check_numbers_down (sd_journal *j, int count) { } -static void test_check_numbers_up (sd_journal *j, int count) { +static void test_check_numbers_up(sd_journal *j, int count) { for (int i = count; i >= 1; i--) { int r; test_check_number(j, i); @@ -113,7 +113,7 @@ static void test_check_numbers_up (sd_journal *j, int count) { } static void setup_sequential(void) { - JournaldFile *one, *two; + ManagedJournalFile *one, *two; one = test_open("one.journal"); two = test_open("two.journal"); append_number(one, 1, NULL); @@ -125,7 +125,7 @@ static void setup_sequential(void) { } static void setup_interleaved(void) { - JournaldFile *one, *two; + ManagedJournalFile *one, *two; one = test_open("one.journal"); two = test_open("two.journal"); append_number(one, 1, NULL); @@ -145,7 +145,7 @@ static void mkdtemp_chdir_chattr(char *path) { (void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL); } -static void test_skip(void (*setup)(void)) { +static void test_skip_one(void (*setup)(void)) { char t[] = "/var/tmp/journal-skip-XXXXXX"; sd_journal *j; int r; @@ -201,11 +201,15 @@ static void test_skip(void (*setup)(void)) { puts("------------------------------------------------------------"); } -static void test_sequence_numbers(void) { +TEST(skip) { + test_skip_one(setup_sequential); + test_skip_one(setup_interleaved); +} +TEST(sequence_numbers) { _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL; char t[] = "/var/tmp/journal-seq-XXXXXX"; - JournaldFile *one, *two; + ManagedJournalFile *one, *two; uint64_t seqnum = 0; sd_id128_t seqnum_id; @@ -214,8 +218,8 @@ static void test_sequence_numbers(void) { mkdtemp_chdir_chattr(t); - assert_se(journald_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644, - true, UINT64_MAX, false, NULL, m, NULL, NULL, &one) == 0); + assert_se(managed_journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644, + UINT64_MAX, NULL, m, NULL, NULL, &one) == 0); append_number(one, 1, &seqnum); printf("seqnum=%"PRIu64"\n", seqnum); @@ -231,8 +235,8 @@ static void test_sequence_numbers(void) { memcpy(&seqnum_id, &one->file->header->seqnum_id, sizeof(sd_id128_t)); - assert_se(journald_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0644, - true, UINT64_MAX, false, NULL, m, NULL, one, &two) == 0); + assert_se(managed_journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0644, + UINT64_MAX, NULL, m, NULL, one, &two) == 0); assert_se(two->file->header->state == STATE_ONLINE); assert_se(!sd_id128_equal(two->file->header->file_id, one->file->header->file_id)); @@ -262,8 +266,8 @@ static void test_sequence_numbers(void) { /* restart server */ seqnum = 0; - assert_se(journald_file_open(-1, "two.journal", O_RDWR, 0, - true, UINT64_MAX, false, NULL, m, NULL, NULL, &two) == 0); + assert_se(managed_journal_file_open(-1, "two.journal", O_RDWR, JOURNAL_COMPRESS, 0, + UINT64_MAX, NULL, m, NULL, NULL, &two) == 0); assert_se(sd_id128_equal(two->file->header->seqnum_id, seqnum_id)); @@ -287,19 +291,14 @@ static void test_sequence_numbers(void) { } } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_DEBUG); - - /* journald_file_open requires a valid machine id */ +static int intro(void) { + /* managed_journal_file_open requires a valid machine id */ if (access("/etc/machine-id", F_OK) != 0) return log_tests_skipped("/etc/machine-id not found"); - arg_keep = argc > 1; + arg_keep = saved_argc > 1; - test_skip(setup_sequential); - test_skip(setup_interleaved); - - test_sequence_numbers(); - - return 0; + return EXIT_SUCCESS; } + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c index 115ce807c..f7bbd4bff 100644 --- a/src/journal/test-journal-stream.c +++ b/src/journal/test-journal-stream.c @@ -8,10 +8,10 @@ #include "alloc-util.h" #include "chattr-util.h" #include "io-util.h" -#include "journald-file.h" #include "journal-internal.h" #include "log.h" #include "macro.h" +#include "managed-journal-file.h" #include "parse-util.h" #include "rm-rf.h" #include "tests.h" @@ -61,7 +61,7 @@ static void verify_contents(sd_journal *j, unsigned skip) { static void run_test(void) { _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL; - JournaldFile *one, *two, *three; + ManagedJournalFile *one, *two, *three; char t[] = "/var/tmp/journal-stream-XXXXXX"; unsigned i; _cleanup_(sd_journal_closep) sd_journal *j = NULL; @@ -77,9 +77,9 @@ static void run_test(void) { assert_se(chdir(t) >= 0); (void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL); - assert_se(journald_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, m, NULL, NULL, &one) == 0); - assert_se(journald_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, m, NULL, NULL, &two) == 0); - assert_se(journald_file_open(-1, "three.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, m, NULL, NULL, &three) == 0); + assert_se(managed_journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, NULL, &one) == 0); + assert_se(managed_journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, NULL, &two) == 0); + assert_se(managed_journal_file_open(-1, "three.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, NULL, &three) == 0); for (i = 0; i < N_ENTRIES; i++) { char *p, *q; @@ -116,9 +116,9 @@ static void run_test(void) { free(q); } - (void) journald_file_close(one); - (void) journald_file_close(two); - (void) journald_file_close(three); + (void) managed_journal_file_close(one); + (void) managed_journal_file_close(two); + (void) managed_journal_file_close(three); assert_se(sd_journal_open_directory(&j, t, 0) >= 0); @@ -178,7 +178,7 @@ static void run_test(void) { int main(int argc, char *argv[]) { - /* journald_file_open requires a valid machine id */ + /* managed_journal_file_open requires a valid machine id */ if (access("/etc/machine-id", F_OK) != 0) return log_tests_skipped("/etc/machine-id not found"); diff --git a/src/journal/test-journal-syslog.c b/src/journal/test-journal-syslog.c index 33f412956..84cfcefc3 100644 --- a/src/journal/test-journal-syslog.c +++ b/src/journal/test-journal-syslog.c @@ -5,8 +5,9 @@ #include "macro.h" #include "string-util.h" #include "syslog-util.h" +#include "tests.h" -static void test_syslog_parse_identifier(const char *str, +static void test_syslog_parse_identifier_one(const char *str, const char *ident, const char *pid, const char *rest, int ret) { const char *buf = str; _cleanup_free_ char *ident2 = NULL, *pid2 = NULL; @@ -20,40 +21,50 @@ static void test_syslog_parse_identifier(const char *str, assert_se(streq(buf, rest)); } -static void test_syslog_parse_priority(const char *str, int priority, int ret) { - const char *buf = str; +static void test_syslog_parse_priority_one(const char *str, bool with_facility, int priority, int ret) { int priority2 = 0, ret2; - ret2 = syslog_parse_priority(&buf, &priority2, false); + ret2 = syslog_parse_priority(&str, &priority2, with_facility); assert_se(ret == ret2); if (ret2 == 1) assert_se(priority == priority2); } -int main(void) { - test_syslog_parse_identifier("pidu[111]: xxx", "pidu", "111", "xxx", 11); - test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, "xxx", 6); - test_syslog_parse_identifier("pidu: xxx", "pidu", NULL, " xxx", 6); - test_syslog_parse_identifier("pidu xxx", NULL, NULL, "pidu xxx", 0); - test_syslog_parse_identifier(" pidu xxx", NULL, NULL, " pidu xxx", 0); - test_syslog_parse_identifier("", NULL, NULL, "", 0); - test_syslog_parse_identifier(" ", NULL, NULL, " ", 0); - test_syslog_parse_identifier(":", "", NULL, "", 1); - test_syslog_parse_identifier(": ", "", NULL, " ", 2); - test_syslog_parse_identifier(" :", "", NULL, "", 2); - test_syslog_parse_identifier(" pidu:", "pidu", NULL, "", 8); - test_syslog_parse_identifier("pidu:", "pidu", NULL, "", 5); - test_syslog_parse_identifier("pidu: ", "pidu", NULL, "", 6); - test_syslog_parse_identifier("pidu : ", NULL, NULL, "pidu : ", 0); - - test_syslog_parse_priority("<>", 0, 0); - test_syslog_parse_priority("<>aaa", 0, 0); - test_syslog_parse_priority("", 0, 0); - test_syslog_parse_priority("aaa", 0, 0); - test_syslog_parse_priority(" ", 0, 0); - test_syslog_parse_priority(" aaa", 0, 0); - /* TODO: add test cases of valid priorities */ - - return 0; +TEST(syslog_parse_identifier) { + test_syslog_parse_identifier_one("pidu[111]: xxx", "pidu", "111", "xxx", 11); + test_syslog_parse_identifier_one("pidu: xxx", "pidu", NULL, "xxx", 6); + test_syslog_parse_identifier_one("pidu: xxx", "pidu", NULL, " xxx", 6); + test_syslog_parse_identifier_one("pidu xxx", NULL, NULL, "pidu xxx", 0); + test_syslog_parse_identifier_one(" pidu xxx", NULL, NULL, " pidu xxx", 0); + test_syslog_parse_identifier_one("", NULL, NULL, "", 0); + test_syslog_parse_identifier_one(" ", NULL, NULL, " ", 0); + test_syslog_parse_identifier_one(":", "", NULL, "", 1); + test_syslog_parse_identifier_one(": ", "", NULL, " ", 2); + test_syslog_parse_identifier_one(" :", "", NULL, "", 2); + test_syslog_parse_identifier_one(" pidu:", "pidu", NULL, "", 8); + test_syslog_parse_identifier_one("pidu:", "pidu", NULL, "", 5); + test_syslog_parse_identifier_one("pidu: ", "pidu", NULL, "", 6); + test_syslog_parse_identifier_one("pidu : ", NULL, NULL, "pidu : ", 0); } + +TEST(syslog_parse_priority) { + test_syslog_parse_priority_one("", false, 0, 0); + test_syslog_parse_priority_one("<>", false, 0, 0); + test_syslog_parse_priority_one("<>aaa", false, 0, 0); + test_syslog_parse_priority_one("", false, 0, 0); + test_syslog_parse_priority_one("aaa", false, 0, 0); + test_syslog_parse_priority_one(" ", false, 0, 0); + test_syslog_parse_priority_one(" aaa", false, 0, 0); + test_syslog_parse_priority_one(" aaa", false, 0, 0); + test_syslog_parse_priority_one(" <1>", false, 0, 0); + test_syslog_parse_priority_one("<1>", false, 1, 1); + test_syslog_parse_priority_one("<7>", false, 7, 1); + test_syslog_parse_priority_one("<8>", false, 0, 0); + test_syslog_parse_priority_one("<9>", true, 9, 1); + test_syslog_parse_priority_one("<22>", true, 22, 1); + test_syslog_parse_priority_one("<111>", false, 0, 0); + test_syslog_parse_priority_one("<111>", true, 111, 1); +} + +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index 6abfaeb0d..1b30ebffa 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -7,9 +7,9 @@ #include "chattr-util.h" #include "fd-util.h" #include "io-util.h" -#include "journald-file.h" #include "journal-verify.h" #include "log.h" +#include "managed-journal-file.h" #include "mmap-cache.h" #include "rm-rf.h" #include "terminal-util.h" @@ -46,7 +46,7 @@ static int raw_verify(const char *fn, const char *verification_key) { m = mmap_cache_new(); assert_se(m != NULL); - r = journal_file_open(-1, fn, O_RDONLY, 0666, true, UINT64_MAX, !!verification_key, NULL, m, NULL, &f); + r = journal_file_open(-1, fn, O_RDONLY, JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL : 0), 0666, UINT64_MAX, NULL, m, NULL, &f); if (r < 0) return r; @@ -61,7 +61,7 @@ int main(int argc, char *argv[]) { char t[] = "/var/tmp/journal-XXXXXX"; unsigned n; JournalFile *f; - JournaldFile *df; + ManagedJournalFile *df; const char *verification_key = argv[1]; usec_t from = 0, to = 0, total = 0; struct stat st; @@ -70,7 +70,7 @@ int main(int argc, char *argv[]) { m = mmap_cache_new(); assert_se(m != NULL); - /* journald_file_open requires a valid machine id */ + /* managed_journal_file_open requires a valid machine id */ if (access("/etc/machine-id", F_OK) != 0) return log_tests_skipped("/etc/machine-id not found"); @@ -82,7 +82,7 @@ int main(int argc, char *argv[]) { log_info("Generating..."); - assert_se(journald_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, !!verification_key, NULL, m, NULL, NULL, &df) == 0); + assert_se(managed_journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL : 0), 0666, UINT64_MAX, NULL, m, NULL, NULL, &df) == 0); for (n = 0; n < N_ENTRIES; n++) { struct iovec iovec; @@ -100,11 +100,11 @@ int main(int argc, char *argv[]) { free(test); } - (void) journald_file_close(df); + (void) managed_journal_file_close(df); log_info("Verifying..."); - assert_se(journal_file_open(-1, "test.journal", O_RDONLY, 0666, true, UINT64_MAX, !!verification_key, NULL, m, NULL, &f) == 0); + assert_se(journal_file_open(-1, "test.journal", O_RDONLY, JOURNAL_COMPRESS|(verification_key ? JOURNAL_SEAL: 0), 0666, UINT64_MAX, NULL, m, NULL, &f) == 0); /* journal_file_print_header(f); */ journal_file_dump(f); diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index 3afe66db8..b95e716b9 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -6,9 +6,9 @@ #include "chattr-util.h" #include "io-util.h" #include "journal-authenticate.h" -#include "journald-file.h" #include "journal-vacuum.h" #include "log.h" +#include "managed-journal-file.h" #include "rm-rf.h" #include "tests.h" @@ -23,25 +23,23 @@ static void mkdtemp_chdir_chattr(char *path) { (void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL); } -static void test_non_empty(void) { +TEST(non_empty) { _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL; dual_timestamp ts; - JournaldFile *f; + ManagedJournalFile *f; struct iovec iovec; static const char test[] = "TEST1=1", test2[] = "TEST2=2"; - Object *o; + Object *o, *d; uint64_t p; sd_id128_t fake_boot_id; char t[] = "/var/tmp/journal-XXXXXX"; - test_setup_logging(LOG_DEBUG); - m = mmap_cache_new(); assert_se(m != NULL); mkdtemp_chdir_chattr(t); - assert_se(journald_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, true, NULL, m, NULL, NULL, &f) == 0); + assert_se(managed_journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, NULL, &f) == 0); assert_se(dual_timestamp_get(&ts)); assert_se(sd_id128_randomize(&fake_boot_id) == 0); @@ -75,21 +73,21 @@ static void test_non_empty(void) { assert_se(journal_file_next_entry(f->file, 0, DIRECTION_DOWN, &o, &p) == 1); assert_se(le64toh(o->entry.seqnum) == 1); - assert_se(journal_file_find_data_object(f->file, test, strlen(test), NULL, &p) == 1); - assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(journal_file_find_data_object(f->file, test, strlen(test), &d, NULL) == 1); + assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_DOWN, &o, NULL) == 1); assert_se(le64toh(o->entry.seqnum) == 1); - assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_UP, &o, NULL) == 1); + assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_UP, &o, NULL) == 1); assert_se(le64toh(o->entry.seqnum) == 3); - assert_se(journal_file_find_data_object(f->file, test2, strlen(test2), NULL, &p) == 1); - assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_UP, &o, NULL) == 1); + assert_se(journal_file_find_data_object(f->file, test2, strlen(test2), &d, NULL) == 1); + assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_UP, &o, NULL) == 1); assert_se(le64toh(o->entry.seqnum) == 2); - assert_se(journal_file_next_entry_for_data(f->file, p, DIRECTION_DOWN, &o, NULL) == 1); + assert_se(journal_file_next_entry_for_data(f->file, d, DIRECTION_DOWN, &o, NULL) == 1); assert_se(le64toh(o->entry.seqnum) == 2); - assert_se(journal_file_find_data_object(f->file, "quux", 4, NULL, &p) == 0); + assert_se(journal_file_find_data_object(f->file, "quux", 4, &d, NULL) == 0); assert_se(journal_file_move_to_entry_by_seqnum(f->file, 1, DIRECTION_DOWN, &o, NULL) == 1); assert_se(le64toh(o->entry.seqnum) == 1); @@ -102,10 +100,10 @@ static void test_non_empty(void) { assert_se(journal_file_move_to_entry_by_seqnum(f->file, 10, DIRECTION_DOWN, &o, NULL) == 0); - journald_file_rotate(&f, m, true, UINT64_MAX, true, NULL); - journald_file_rotate(&f, m, true, UINT64_MAX, true, NULL); + managed_journal_file_rotate(&f, m, JOURNAL_SEAL|JOURNAL_COMPRESS, UINT64_MAX, NULL); + managed_journal_file_rotate(&f, m, JOURNAL_SEAL|JOURNAL_COMPRESS, UINT64_MAX, NULL); - (void) journald_file_close(f); + (void) managed_journal_file_close(f); log_info("Done..."); @@ -120,22 +118,20 @@ static void test_non_empty(void) { puts("------------------------------------------------------------"); } -static void test_empty(void) { +TEST(empty) { _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL; - JournaldFile *f1, *f2, *f3, *f4; + ManagedJournalFile *f1, *f2, *f3, *f4; char t[] = "/var/tmp/journal-XXXXXX"; - test_setup_logging(LOG_DEBUG); - m = mmap_cache_new(); assert_se(m != NULL); mkdtemp_chdir_chattr(t); - assert_se(journald_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, UINT64_MAX, false, NULL, m, NULL, NULL, &f1) == 0); - assert_se(journald_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, false, NULL, m, NULL, NULL, &f2) == 0); - assert_se(journald_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, 0666, false, UINT64_MAX, true, NULL, m, NULL, NULL, &f3) == 0); - assert_se(journald_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, 0666, true, UINT64_MAX, true, NULL, m, NULL, NULL, &f4) == 0); + assert_se(managed_journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0, 0666, UINT64_MAX, NULL, m, NULL, NULL, &f1) == 0); + assert_se(managed_journal_file_open(-1, "test-compress.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS, 0666, UINT64_MAX, NULL, m, NULL, NULL, &f2) == 0); + assert_se(managed_journal_file_open(-1, "test-seal.journal", O_RDWR|O_CREAT, JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, NULL, &f3) == 0); + assert_se(managed_journal_file_open(-1, "test-seal-compress.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, UINT64_MAX, NULL, m, NULL, NULL, &f4) == 0); journal_file_print_header(f1->file); puts(""); @@ -156,17 +152,17 @@ static void test_empty(void) { assert_se(rm_rf(t, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); } - (void) journald_file_close(f1); - (void) journald_file_close(f2); - (void) journald_file_close(f3); - (void) journald_file_close(f4); + (void) managed_journal_file_close(f1); + (void) managed_journal_file_close(f2); + (void) managed_journal_file_close(f3); + (void) managed_journal_file_close(f4); } #if HAVE_COMPRESSION static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) { _cleanup_(mmap_cache_unrefp) MMapCache *m = NULL; dual_timestamp ts; - JournaldFile *f; + ManagedJournalFile *f; struct iovec iovec; Object *o; uint64_t p; @@ -177,14 +173,12 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) { assert_se(data_size <= sizeof(data)); - test_setup_logging(LOG_DEBUG); - m = mmap_cache_new(); assert_se(m != NULL); mkdtemp_chdir_chattr(t); - assert_se(journald_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, compress_threshold, true, NULL, m, NULL, NULL, &f) == 0); + assert_se(managed_journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, JOURNAL_COMPRESS|JOURNAL_SEAL, 0666, compress_threshold, NULL, m, NULL, NULL, &f) == 0); dual_timestamp_get(&ts); @@ -211,7 +205,7 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) { is_compressed = (o->object.flags & OBJECT_COMPRESSION_MASK) != 0; - (void) journald_file_close(f); + (void) managed_journal_file_close(f); log_info("Done..."); @@ -228,7 +222,7 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) { return is_compressed; } -static void test_min_compress_size(void) { +TEST(min_compress_size) { /* Note that XZ will actually fail to compress anything under 80 bytes, so you have to choose the limits * carefully */ @@ -249,20 +243,14 @@ static void test_min_compress_size(void) { } #endif -int main(int argc, char *argv[]) { - arg_keep = argc > 1; +static int intro(void) { + arg_keep = saved_argc > 1; - test_setup_logging(LOG_INFO); - - /* journald_file_open requires a valid machine id */ + /* managed_journal_file_open requires a valid machine id */ if (access("/etc/machine-id", F_OK) != 0) return log_tests_skipped("/etc/machine-id not found"); - test_non_empty(); - test_empty(); -#if HAVE_COMPRESSION - test_min_compress_size(); -#endif - - return 0; + return EXIT_SUCCESS; } + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/kernel-install/50-depmod.install b/src/kernel-install/50-depmod.install index fd00c4363..61afdd4e9 100644 --- a/src/kernel-install/50-depmod.install +++ b/src/kernel-install/50-depmod.install @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- # ex: ts=8 sw=4 sts=4 et filetype=sh # SPDX-License-Identifier: LGPL-2.1-or-later @@ -20,23 +20,27 @@ COMMAND="$1" KERNEL_VERSION="$2" -ENTRY_DIR_ABS="$3" -KERNEL_IMAGE="$4" -INITRD_OPTIONS_START="5" - -[[ $KERNEL_VERSION ]] || exit 1 case "$COMMAND" in add) - [[ -d "/lib/modules/${KERNEL_VERSION}/kernel" ]] || exit 0 - [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ - echo "Running depmod -a ${KERNEL_VERSION}" - exec depmod -a "${KERNEL_VERSION}" + [ -d "/lib/modules/$KERNEL_VERSION/kernel" ] || exit 0 + [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "+depmod -a $KERNEL_VERSION" + exec depmod -a "$KERNEL_VERSION" ;; remove) [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ echo "Removing /lib/modules/${KERNEL_VERSION}/modules.dep and associated files" - exec rm -f /lib/modules/"${KERNEL_VERSION}"/modules.{alias{,.bin},builtin{,.alias}.bin,dep{,.bin},devname,softdep,symbols{,.bin}} + exec rm -f \ + "/lib/modules/$KERNEL_VERSION/modules.alias" \ + "/lib/modules/$KERNEL_VERSION/modules.alias.bin" \ + "/lib/modules/$KERNEL_VERSION/modules.builtin.bin" \ + "/lib/modules/$KERNEL_VERSION/modules.builtin.alias.bin" \ + "/lib/modules/$KERNEL_VERSION/modules.dep" \ + "/lib/modules/$KERNEL_VERSION/modules.dep.bin" \ + "/lib/modules/$KERNEL_VERSION/modules.devname" \ + "/lib/modules/$KERNEL_VERSION/modules.softdep" \ + "/lib/modules/$KERNEL_VERSION/modules.symbols" \ + "/lib/modules/$KERNEL_VERSION/modules.symbols.bin" ;; *) exit 0 diff --git a/src/kernel-install/90-loaderentry.install b/src/kernel-install/90-loaderentry.install index 044eced3f..0e57df775 100644 --- a/src/kernel-install/90-loaderentry.install +++ b/src/kernel-install/90-loaderentry.install @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- # ex: ts=8 sw=4 sts=4 et filetype=sh # SPDX-License-Identifier: LGPL-2.1-or-later @@ -18,132 +18,136 @@ # You should have received a copy of the GNU Lesser General Public License # along with systemd; If not, see . +shopt -s nullglob + COMMAND="$1" KERNEL_VERSION="$2" ENTRY_DIR_ABS="$3" KERNEL_IMAGE="$4" -INITRD_OPTIONS_START="5" +INITRD_OPTIONS_SHIFT=4 -if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then - exit 0 -fi - -if [ "$KERNEL_INSTALL_LAYOUT" != "bls" ]; then - exit 0 -fi +[ "$KERNEL_INSTALL_LAYOUT" = "bls" ] || exit 0 MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID" +ENTRY_TOKEN="$KERNEL_INSTALL_ENTRY_TOKEN" BOOT_ROOT="$KERNEL_INSTALL_BOOT_ROOT" BOOT_MNT="$(stat -c %m "$BOOT_ROOT")" -if [[ "$BOOT_MNT" == '/' ]]; then +if [ "$BOOT_MNT" = '/' ]; then ENTRY_DIR="$ENTRY_DIR_ABS" else ENTRY_DIR="${ENTRY_DIR_ABS#$BOOT_MNT}" fi -if [[ $COMMAND == remove ]]; then - rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf" - rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+"*".conf" - exit 0 -fi +case "$COMMAND" in + remove) + [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ + echo "Removing $BOOT_ROOT/loader/entries/$ENTRY_TOKEN-$KERNEL_VERSION*.conf" + exec rm -f \ + "$BOOT_ROOT/loader/entries/$ENTRY_TOKEN-$KERNEL_VERSION.conf" \ + "$BOOT_ROOT/loader/entries/$ENTRY_TOKEN-$KERNEL_VERSION+"*".conf" + ;; + add) + ;; + *) + exit 1 + ;; +esac -if ! [[ $COMMAND == add ]]; then - exit 1 -fi - -if ! [[ $KERNEL_IMAGE ]]; then - exit 1 -fi - -if [[ -f /etc/os-release ]]; then +if [ -r /etc/os-release ]; then . /etc/os-release -elif [[ -f /usr/lib/os-release ]]; then +elif [ -r /usr/lib/os-release ]; then . /usr/lib/os-release fi -if ! [[ $PRETTY_NAME ]]; then - PRETTY_NAME="Linux $KERNEL_VERSION" -fi +[ -n "$PRETTY_NAME" ] || PRETTY_NAME="Linux $KERNEL_VERSION" -if [[ -f /etc/kernel/cmdline ]]; then - read -r -d '' -a BOOT_OPTIONS < /etc/kernel/cmdline -elif [[ -f /usr/lib/kernel/cmdline ]]; then - read -r -d '' -a BOOT_OPTIONS < /usr/lib/kernel/cmdline +SORT_KEY="$IMAGE_ID" +[ -z "$SORT_KEY" ] && SORT_KEY="$ID" + +if [ -r /etc/kernel/cmdline ]; then + BOOT_OPTIONS="$(tr -s "$IFS" ' ' &2 exit 1 fi - LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION+$TRIES.conf" + LOADER_ENTRY="$BOOT_ROOT/loader/entries/$ENTRY_TOKEN-$KERNEL_VERSION+$TRIES.conf" else - LOADER_ENTRY="$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf" + LOADER_ENTRY="$BOOT_ROOT/loader/entries/$ENTRY_TOKEN-$KERNEL_VERSION.conf" fi if ! [ -d "$ENTRY_DIR_ABS" ]; then - if [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ]; then - echo "+mkdir -v -p $ENTRY_DIR_ABS" - mkdir -v -p "$ENTRY_DIR_ABS" - else - mkdir -p "$ENTRY_DIR_ABS" - fi + echo "Error: entry directory '$ENTRY_DIR_ABS' does not exist" >&2 + exit 1 fi install -g root -o root -m 0644 "$KERNEL_IMAGE" "$ENTRY_DIR_ABS/linux" || { - echo "Could not copy '$KERNEL_IMAGE' to '$ENTRY_DIR_ABS/linux'." >&2 + echo "Error: could not copy '$KERNEL_IMAGE' to '$ENTRY_DIR_ABS/linux'." >&2 exit 1 } -INITRD_OPTIONS=( "${@:${INITRD_OPTIONS_START}}" ) +shift "$INITRD_OPTIONS_SHIFT" +# All files listed as arguments, and staged files called "initrd*" are installed as initrds. +for initrd in "$@" "${KERNEL_INSTALL_STAGING_AREA}"/initrd*; do + [ -f "$initrd" ] || { + echo "Error: initrd '$initrd' not a file." >&2 + exit 1 + } -for initrd in "${INITRD_OPTIONS[@]}"; do - if [[ -f "${initrd}" ]]; then - initrd_basename="$(basename ${initrd})" - [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ - echo "Installing $ENTRY_DIR_ABS/${initrd_basename}" - install -g root -o root -m 0644 "${initrd}" "$ENTRY_DIR_ABS/${initrd_basename}" || { - echo "Could not copy '${initrd}' to '$ENTRY_DIR_ABS/${initrd_basename}'." >&2 - exit 1 - } - fi + initrd_basename="${initrd##*/}" + [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Installing $ENTRY_DIR_ABS/$initrd_basename" + install -g root -o root -m 0644 "$initrd" "$ENTRY_DIR_ABS/$initrd_basename" || { + echo "Error: could not copy '$initrd' to '$ENTRY_DIR_ABS/$initrd_basename'." >&2 + exit 1 + } done -# If no initrd option is supplied, fall back to "initrd" which is -# the name used by dracut when generating it in its kernel-install hook -[[ ${#INITRD_OPTIONS[@]} == 0 ]] && INITRD_OPTIONS=( initrd ) - mkdir -p "${LOADER_ENTRY%/*}" || { - echo "Could not create loader entry directory '${LOADER_ENTRY%/*}'." >&2 + echo "Error: could not create loader entry directory '${LOADER_ENTRY%/*}'." >&2 exit 1 } -[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ - echo "Creating $LOADER_ENTRY" +[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "Creating $LOADER_ENTRY" { echo "title $PRETTY_NAME" echo "version $KERNEL_VERSION" - echo "machine-id $MACHINE_ID" - echo "options ${BOOT_OPTIONS[*]}" + if [ "$ENTRY_TOKEN" = "$MACHINE_ID" ]; then + # See similar logic above for the systemd.machine_id= kernel command line option + echo "machine-id $MACHINE_ID" + fi + [ -n "$SORT_KEY" ] && echo "sort-key $SORT_KEY" + echo "options $BOOT_OPTIONS" echo "linux $ENTRY_DIR/linux" - for initrd in "${INITRD_OPTIONS[@]}"; do - [[ -f $ENTRY_DIR_ABS/$(basename ${initrd}) ]] && \ - echo "initrd $ENTRY_DIR/$(basename ${initrd})" + + have_initrd= + for initrd in "${@}" "${KERNEL_INSTALL_STAGING_AREA}"/initrd*; do + echo "initrd $ENTRY_DIR/${initrd##*/}" + have_initrd=yes done + + # Try "initrd", generated by dracut in its kernel-install hook, if no initrds were supplied + [ -z "$have_initrd" ] && [ -f "$ENTRY_DIR_ABS/initrd" ] && echo "initrd $ENTRY_DIR/initrd" : -} > "$LOADER_ENTRY" || { - echo "Could not create loader entry '$LOADER_ENTRY'." >&2 +} >"$LOADER_ENTRY" || { + echo "Error: could not create loader entry '$LOADER_ENTRY'." >&2 exit 1 } exit 0 diff --git a/src/kernel-install/install.conf b/src/kernel-install/install.conf index e4802e6fa..43b6e7d79 100644 --- a/src/kernel-install/install.conf +++ b/src/kernel-install/install.conf @@ -8,3 +8,4 @@ # See kernel-install(8) for details. #layout=bls|other|... +#initrd_generator=dracut|... diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install index d85852532..a09b99836 100755 --- a/src/kernel-install/kernel-install +++ b/src/kernel-install/kernel-install @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- # ex: ts=8 sw=4 sts=4 et filetype=sh # SPDX-License-Identifier: LGPL-2.1-or-later @@ -18,38 +18,32 @@ # You should have received a copy of the GNU Lesser General Public License # along with systemd; If not, see . -SKIP_REMAINING=77 +skip_remaining=77 usage() { echo "Usage:" echo " $0 [OPTIONS...] add KERNEL-VERSION KERNEL-IMAGE [INITRD-FILE ...]" echo " $0 [OPTIONS...] remove KERNEL-VERSION" + echo " $0 [OPTIONS...] inspect" echo "Options:" - echo " -h,--help Print this help" - echo " -v,--verbose Increase verbosity" + echo " -h, --help Print this help" + echo " -v, --verbose Increase verbosity" } dropindirs_sort() { - local suffix=$1; shift - local -a files - local f d i + suffix="$1" + shift - readarray -t files <<<"$( - for d in "$@"; do - for i in "$d/"*"$suffix"; do - if [[ -e "$i" ]]; then - echo "${i##*/}" - fi - done - done | sort -Vu - )" - - for f in "${files[@]}"; do - for d in "$@"; do - if [[ -e "$d/$f" ]]; then - echo "$d/$f" + for d; do + for i in "$d/"*"$suffix"; do + [ -e "$i" ] && echo "${i##*/}" + done + done | sort -Vu | while read -r f; do + for d; do + if [ -e "$d/$f" ]; then + [ -x "$d/$f" ] && echo "$d/$f" continue 2 fi done @@ -58,37 +52,42 @@ dropindirs_sort() export LC_COLLATE=C -for i in "$@"; do - if [ "$i" == "--help" -o "$i" == "-h" ]; then +for i; do + if [ "$i" = "--help" ] || [ "$i" = "-h" ]; then usage exit 0 fi done -KERNEL_INSTALL_VERBOSE=0 -if [ "$1" == "--verbose" -o "$1" == "-v" ]; then +export KERNEL_INSTALL_VERBOSE=0 +if [ "$1" = "--verbose" ] || [ "$1" = "-v" ]; then shift KERNEL_INSTALL_VERBOSE=1 fi -export KERNEL_INSTALL_VERBOSE -if [[ "${0##*/}" == 'installkernel' ]]; then - COMMAND='add' - # make install doesn't pass any parameter wrt initrd handling - INITRD_OPTIONS=() +if [ "${0##*/}" = "installkernel" ]; then + COMMAND=add + # make install doesn't pass any initrds else COMMAND="$1" + [ $# -ge 1 ] && shift +fi + +if [ "$COMMAND" = "inspect" ]; then + KERNEL_VERSION="" +else + if [ $# -lt 1 ]; then + echo "Error: not enough arguments" >&2 + exit 1 + fi + + KERNEL_VERSION="$1" shift - INITRD_OPTIONS=( "${@:3}" ) fi -KERNEL_VERSION="$1" -KERNEL_IMAGE="$2" - -if [[ ! $COMMAND ]] || [[ ! $KERNEL_VERSION ]]; then - echo "Not enough arguments" >&2 - exit 1 -fi +# These two settings are settable in install.conf +layout= +initrd_generator= if [ -r "/etc/kernel/install.conf" ]; then . /etc/kernel/install.conf @@ -96,118 +95,164 @@ elif [ -r "/usr/lib/kernel/install.conf" ]; then . /usr/lib/kernel/install.conf fi -# Prefer to use an existing machine ID from /etc/machine-info or /etc/machine-id. If we're using the machine -# ID /etc/machine-id, try to persist it in /etc/machine-info. If no machine ID is found, try to generate -# a new machine ID in /etc/machine-info. If that fails, use "Default". +# If /etc/machine-id is initialized we'll use it, otherwise we'll use a freshly +# generated one. If the user configured an explicit machine ID to use in +# /etc/machine-info to use for our purpose, we'll use that instead (for +# compatibility). +[ -z "$MACHINE_ID" ] && [ -r /etc/machine-info ] && . /etc/machine-info && MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID" +[ -z "$MACHINE_ID" ] && [ -r /etc/machine-id ] && read -r MACHINE_ID >/etc/machine-info -[ -z "$MACHINE_ID" ] && NEW_MACHINE_ID="$(systemd-id128 new)" && echo "KERNEL_INSTALL_MACHINE_ID=$NEW_MACHINE_ID" >>/etc/machine-info -[ -z "$MACHINE_ID" ] && [ -f /etc/machine-info ] && source /etc/machine-info && MACHINE_ID="$KERNEL_INSTALL_MACHINE_ID" -[ -z "$MACHINE_ID" ] && MACHINE_ID="Default" +# Now that we determined the machine ID to use, let's determine the "token" for +# the boot loader entry to generate. We use that for naming the directory below +# $BOOT where we want to place the kernel/initrd and related resources, as well +# for naming the .conf boot loader spec entry. Typically this is just the +# machine ID, but it can be anything else, too, if we are told so. +[ -z "$ENTRY_TOKEN" ] && [ -r /etc/kernel/entry-token ] && read -r ENTRY_TOKEN &2 + if [ $# -lt 1 ]; then + echo "Error: command 'add' requires a kernel image" >&2 exit 1 fi - if [[ ! -f "$KERNEL_IMAGE" ]]; then - echo "Kernel image argument ${KERNEL_IMAGE} not a file" >&2 + if ! [ -f "$1" ]; then + echo "Error: kernel image argument $1 not a file" >&2 exit 1 fi if [ "$MAKE_ENTRY_DIR_ABS" -eq 0 ]; then - # Compatibility with earlier versions that used the presence of $BOOT_ROOT/$MACHINE_ID + # Compatibility with earlier versions that used the presence of $BOOT_ROOT/$ENTRY_TOKEN # to signal to 00-entry-directory to create $ENTRY_DIR_ABS # to serve as the indication to use or to not use the BLS if [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ]; then echo "+mkdir -v -p $ENTRY_DIR_ABS" - mkdir -v -p "$ENTRY_DIR_ABS" + mkdir -v -p "$ENTRY_DIR_ABS" || exit 1 else - mkdir -p "$ENTRY_DIR_ABS" + mkdir -p "$ENTRY_DIR_ABS" || exit 1 fi fi - for f in "${PLUGINS[@]}"; do - if [[ -x $f ]]; then - [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ - echo "+$f add $KERNEL_VERSION $ENTRY_DIR_ABS $KERNEL_IMAGE ${INITRD_OPTIONS[@]}" - "$f" add "$KERNEL_VERSION" "$ENTRY_DIR_ABS" "$KERNEL_IMAGE" "${INITRD_OPTIONS[@]}" - x=$? - if [[ $x == $SKIP_REMAINING ]]; then - break - fi - ((ret+=$x)) - fi + for f in $PLUGINS; do + [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "+$f add $KERNEL_VERSION $ENTRY_DIR_ABS $*" + "$f" add "$KERNEL_VERSION" "$ENTRY_DIR_ABS" "$@" + err=$? + [ $err -eq $skip_remaining ] && break + ret=$(( ret + err )) done ;; remove) - for f in "${PLUGINS[@]}"; do - if [[ -x $f ]]; then - [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ - echo "+$f remove $KERNEL_VERSION $ENTRY_DIR_ABS" - "$f" remove "$KERNEL_VERSION" "$ENTRY_DIR_ABS" - x=$? - if [[ $x == $SKIP_REMAINING ]]; then - break - fi - ((ret+=$x)) - fi + for f in $PLUGINS; do + [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "+$f remove $KERNEL_VERSION $ENTRY_DIR_ABS" + "$f" remove "$KERNEL_VERSION" "$ENTRY_DIR_ABS" + err=$? + [ $err -eq $skip_remaining ] && break + ret=$(( ret + err )) done if [ "$MAKE_ENTRY_DIR_ABS" -eq 0 ]; then @@ -216,10 +261,22 @@ case $COMMAND in fi ;; + inspect) + echo "KERNEL_INSTALL_MACHINE_ID: $KERNEL_INSTALL_MACHINE_ID" + echo "KERNEL_INSTALL_ENTRY_TOKEN: $KERNEL_INSTALL_ENTRY_TOKEN" + echo "KERNEL_INSTALL_BOOT_ROOT: $KERNEL_INSTALL_BOOT_ROOT" + echo "KERNEL_INSTALL_LAYOUT: $KERNEL_INSTALL_LAYOUT" + echo "KERNEL_INSTALL_INITRD_GENERATOR: $KERNEL_INSTALL_INITRD_GENERATOR" + echo "ENTRY_DIR_ABS: $KERNEL_INSTALL_BOOT_ROOT/$ENTRY_TOKEN/\$KERNEL_VERSION" + + # Assert that ENTRY_DIR_ABS actually matches what we are printing here + [ "${ENTRY_DIR_ABS%/*}" = "$KERNEL_INSTALL_BOOT_ROOT/$ENTRY_TOKEN" ] || { echo "Assertion didn't pass." >&2; exit 1; } + + ;; *) - echo "Unknown command '$COMMAND'" >&2 + echo "Error: unknown command '$COMMAND'" >&2 exit 1 ;; esac -exit $ret +exit "$ret" diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c index 3ea6c7ce2..d2b190244 100644 --- a/src/libsystemd-network/dhcp-identifier.c +++ b/src/libsystemd-network/dhcp-identifier.c @@ -8,20 +8,27 @@ #include "sd-id128.h" #include "dhcp-identifier.h" -#include "dhcp6-protocol.h" #include "netif-util.h" #include "siphash24.h" #include "sparse-endian.h" #include "stat-util.h" -#include "stdio-util.h" +#include "string-table.h" #include "udev-util.h" -#include "virt.h" #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09) #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03) #define USEC_2000 ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */ -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { +static const char * const duid_type_table[_DUID_TYPE_MAX] = { + [DUID_TYPE_LLT] = "DUID-LLT", + [DUID_TYPE_EN] = "DUID-EN/Vendor", + [DUID_TYPE_LL] = "DUID-LL", + [DUID_TYPE_UUID] = "UUID", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(duid_type, DUIDType); + +int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict) { struct duid d; assert_cc(sizeof(d.raw) >= MAX_DUID_LEN); @@ -57,112 +64,146 @@ int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict) { return 0; } -int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { +static int dhcp_identifier_set_duid_llt(const uint8_t *addr, size_t addr_len, uint16_t arp_type, usec_t t, struct duid *ret_duid, size_t *ret_len) { uint16_t time_from_2000y; - assert(duid); - assert(len); assert(addr); + assert(ret_duid); + assert(ret_len); + + if (addr_len == 0) + return -EOPNOTSUPP; if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); else if (arp_type == ARPHRD_INFINIBAND) assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else - return -EINVAL; + return -EOPNOTSUPP; if (t < USEC_2000) time_from_2000y = 0; else time_from_2000y = (uint16_t) (((t - USEC_2000) / USEC_PER_SEC) & 0xffffffff); - unaligned_write_be16(&duid->type, DUID_TYPE_LLT); - unaligned_write_be16(&duid->llt.htype, arp_type); - unaligned_write_be32(&duid->llt.time, time_from_2000y); - memcpy(duid->llt.haddr, addr, addr_len); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_LLT); + unaligned_write_be16(&ret_duid->llt.htype, arp_type); + unaligned_write_be32(&ret_duid->llt.time, time_from_2000y); + memcpy(ret_duid->llt.haddr, addr, addr_len); - *len = sizeof(duid->type) + sizeof(duid->llt.htype) + sizeof(duid->llt.time) + addr_len; + *ret_len = offsetof(struct duid, llt.haddr) + addr_len; return 0; } -int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len) { - assert(duid); - assert(len); +static int dhcp_identifier_set_duid_ll(const uint8_t *addr, size_t addr_len, uint16_t arp_type, struct duid *ret_duid, size_t *ret_len) { assert(addr); + assert(ret_duid); + assert(ret_len); + + if (addr_len == 0) + return -EOPNOTSUPP; if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); else if (arp_type == ARPHRD_INFINIBAND) assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else - return -EINVAL; + return -EOPNOTSUPP; - unaligned_write_be16(&duid->type, DUID_TYPE_LL); - unaligned_write_be16(&duid->ll.htype, arp_type); - memcpy(duid->ll.haddr, addr, addr_len); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_LL); + unaligned_write_be16(&ret_duid->ll.htype, arp_type); + memcpy(ret_duid->ll.haddr, addr, addr_len); - *len = sizeof(duid->type) + sizeof(duid->ll.htype) + addr_len; + *ret_len = offsetof(struct duid, ll.haddr) + addr_len; return 0; } -int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len) { +int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len) { sd_id128_t machine_id; uint64_t hash; int r; - assert(duid); - assert(len); + assert(ret_duid); + assert(ret_len); - r = sd_id128_get_machine(&machine_id); - if (r < 0) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (!test_mode) { + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return r; + } else + /* For tests, especially for fuzzers, reproducibility is important. + * Hence, use a static and constant machine ID. + * See 9216fddc5a8ac2742e6cfa7660f95c20ca4f2193. */ machine_id = SD_ID128_MAKE(01, 02, 03, 04, 05, 06, 07, 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, 10); -#else - return r; -#endif - } - unaligned_write_be16(&duid->type, DUID_TYPE_EN); - unaligned_write_be32(&duid->en.pen, SYSTEMD_PEN); - - *len = sizeof(duid->type) + sizeof(duid->en); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_EN); + unaligned_write_be32(&ret_duid->en.pen, SYSTEMD_PEN); /* a bit of snake-oil perhaps, but no need to expose the machine-id * directly; duid->en.id might not be aligned, so we need to copy */ hash = htole64(siphash24(&machine_id, sizeof(machine_id), HASH_KEY.bytes)); - memcpy(duid->en.id, &hash, sizeof(duid->en.id)); + memcpy(ret_duid->en.id, &hash, sizeof(ret_duid->en.id)); + + *ret_len = offsetof(struct duid, en.id) + sizeof(ret_duid->en.id); + + if (test_mode) + assert_se(memcmp(ret_duid, (const uint8_t[]) { 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 }, *ret_len) == 0); return 0; } -int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len) { +static int dhcp_identifier_set_duid_uuid(struct duid *ret_duid, size_t *ret_len) { sd_id128_t machine_id; int r; - assert(duid); - assert(len); + assert(ret_duid); + assert(ret_len); r = sd_id128_get_machine_app_specific(APPLICATION_ID, &machine_id); if (r < 0) return r; - unaligned_write_be16(&duid->type, DUID_TYPE_UUID); - memcpy(&duid->raw.data, &machine_id, sizeof(machine_id)); + unaligned_write_be16(&ret_duid->type, DUID_TYPE_UUID); + memcpy(&ret_duid->uuid.uuid, &machine_id, sizeof(machine_id)); - *len = sizeof(duid->type) + sizeof(machine_id); + *ret_len = offsetof(struct duid, uuid.uuid) + sizeof(machine_id); return 0; } +int dhcp_identifier_set_duid( + DUIDType duid_type, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type, + usec_t llt_time, + bool test_mode, + struct duid *ret_duid, + size_t *ret_len) { + + switch (duid_type) { + case DUID_TYPE_LLT: + return dhcp_identifier_set_duid_llt(addr, addr_len, arp_type, llt_time, ret_duid, ret_len); + case DUID_TYPE_EN: + return dhcp_identifier_set_duid_en(test_mode, ret_duid, ret_len); + case DUID_TYPE_LL: + return dhcp_identifier_set_duid_ll(addr, addr_len, arp_type, ret_duid, ret_len); + case DUID_TYPE_UUID: + return dhcp_identifier_set_duid_uuid(ret_duid, ret_len); + default: + return -EINVAL; + } +} + int dhcp_identifier_set_iaid( int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, bool use_mac, - void *_id) { + void *ret) { /* name is a pointer to memory in the sd_device struct, so must * have the same scope */ @@ -214,6 +255,6 @@ int dhcp_identifier_set_iaid( * behavior. */ id32 = be32toh(id32); - unaligned_write_ne32(_id, id32); + unaligned_write_ne32(ret, id32); return 0; } diff --git a/src/libsystemd-network/dhcp-identifier.h b/src/libsystemd-network/dhcp-identifier.h index 6c24af032..697ba3bfb 100644 --- a/src/libsystemd-network/dhcp-identifier.h +++ b/src/libsystemd-network/dhcp-identifier.h @@ -54,9 +54,23 @@ struct duid { }; } _packed_; -int dhcp_validate_duid_len(uint16_t duid_type, size_t duid_len, bool strict); -int dhcp_identifier_set_duid_llt(struct duid *duid, usec_t t, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); -int dhcp_identifier_set_duid_ll(struct duid *duid, const uint8_t *addr, size_t addr_len, uint16_t arp_type, size_t *len); -int dhcp_identifier_set_duid_en(struct duid *duid, size_t *len); -int dhcp_identifier_set_duid_uuid(struct duid *duid, size_t *len); -int dhcp_identifier_set_iaid(int ifindex, const uint8_t *mac, size_t mac_len, bool legacy_unstable_byteorder, bool use_mac, void *_id); +int dhcp_validate_duid_len(DUIDType duid_type, size_t duid_len, bool strict); +int dhcp_identifier_set_duid_en(bool test_mode, struct duid *ret_duid, size_t *ret_len); +int dhcp_identifier_set_duid( + DUIDType duid_type, + const uint8_t *addr, + size_t addr_len, + uint16_t arp_type, + usec_t llt_time, + bool test_mode, + struct duid *ret_duid, + size_t *ret_len); +int dhcp_identifier_set_iaid( + int ifindex, + const uint8_t *mac, + size_t mac_len, + bool legacy_unstable_byteorder, + bool use_mac, + void *ret); + +const char *duid_type_to_string(DUIDType t) _const_; diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index 6538f05bd..466d8e4b3 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -53,8 +53,8 @@ typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len, int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message); int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, - uint8_t type, uint16_t arp_type, size_t optlen, - size_t *optoffset); + uint8_t type, uint16_t arp_type, uint8_t hlen, const uint8_t *chaddr, + size_t optlen, size_t *optoffset); uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len); diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index 992ac9f32..c67e9511a 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -16,8 +16,6 @@ struct sd_dhcp_route { struct in_addr dst_addr; struct in_addr gw_addr; unsigned char dst_prefixlen; - - uint8_t option; }; struct sd_dhcp_raw_option { @@ -52,8 +50,10 @@ struct sd_dhcp_lease { DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; - struct sd_dhcp_route *static_route; - size_t static_route_size; + struct sd_dhcp_route *static_routes; + size_t n_static_routes; + struct sd_dhcp_route *classless_routes; + size_t n_classless_routes; uint16_t mtu; /* 0 if unset */ diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index ebe8eecc9..efb676e60 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -58,7 +58,6 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, case SD_DHCP_OPTION_USER_CLASS: { size_t total = 0; - char **s; if (strv_isempty((char **) optval)) return -EINVAL; diff --git a/src/libsystemd-network/dhcp-packet.c b/src/libsystemd-network/dhcp-packet.c index cace916f4..d1a1cf57f 100644 --- a/src/libsystemd-network/dhcp-packet.c +++ b/src/libsystemd-network/dhcp-packet.c @@ -10,21 +10,44 @@ #include "dhcp-internal.h" #include "dhcp-protocol.h" +#include "memory-util.h" #define DHCP_CLIENT_MIN_OPTIONS_SIZE 312 -int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, - uint8_t type, uint16_t arp_type, size_t optlen, - size_t *optoffset) { +int dhcp_message_init( + DHCPMessage *message, + uint8_t op, + uint32_t xid, + uint8_t type, + uint16_t arp_type, + uint8_t hlen, + const uint8_t *chaddr, + size_t optlen, + size_t *optoffset) { + size_t offset = 0; int r; assert(IN_SET(op, BOOTREQUEST, BOOTREPLY)); - assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND)); + assert(chaddr || hlen == 0); message->op = op; message->htype = arp_type; - message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0; + + /* RFC2131 section 4.1.1: + The client MUST include its hardware address in the ’chaddr’ field, if + necessary for delivery of DHCP reply messages. + + RFC 4390 section 2.1: + A DHCP client, when working over an IPoIB interface, MUST follow the + following rules: + "htype" (hardware address type) MUST be 32 [ARPPARAM]. + "hlen" (hardware address length) MUST be 0. + "chaddr" (client hardware address) field MUST be zeroed. + */ + message->hlen = (arp_type == ARPHRD_INFINIBAND) ? 0 : hlen; + memcpy_safe(message->chaddr, chaddr, message->hlen); + message->xid = htobe32(xid); message->magic = htobe32(DHCP_MAGIC_COOKIE); diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h index 11f4201ab..dd54bcf6e 100644 --- a/src/libsystemd-network/dhcp-protocol.h +++ b/src/libsystemd-network/dhcp-protocol.h @@ -73,15 +73,24 @@ enum { }; enum { - DHCP_DISCOVER = 1, - DHCP_OFFER = 2, - DHCP_REQUEST = 3, - DHCP_DECLINE = 4, - DHCP_ACK = 5, - DHCP_NAK = 6, - DHCP_RELEASE = 7, - DHCP_INFORM = 8, - DHCP_FORCERENEW = 9, + DHCP_DISCOVER = 1, /* [RFC2132] */ + DHCP_OFFER = 2, /* [RFC2132] */ + DHCP_REQUEST = 3, /* [RFC2132] */ + DHCP_DECLINE = 4, /* [RFC2132] */ + DHCP_ACK = 5, /* [RFC2132] */ + DHCP_NAK = 6, /* [RFC2132] */ + DHCP_RELEASE = 7, /* [RFC2132] */ + DHCP_INFORM = 8, /* [RFC2132] */ + DHCP_FORCERENEW = 9, /* [RFC3203] */ + DHCPLEASEQUERY = 10, /* [RFC4388] */ + DHCPLEASEUNASSIGNED = 11, /* [RFC4388] */ + DHCPLEASEUNKNOWN = 12, /* [RFC4388] */ + DHCPLEASEACTIVE = 13, /* [RFC4388] */ + DHCPBULKLEASEQUERY = 14, /* [RFC6926] */ + DHCPLEASEQUERYDONE = 15, /* [RFC6926] */ + DHCPACTIVELEASEQUERY = 16, /* [RFC7724] */ + DHCPLEASEQUERYSTATUS = 17, /* [RFC7724] */ + DHCPTLS = 18, /* [RFC7724] */ }; enum { diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 13d8cd77b..607f9f0c2 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -26,7 +26,7 @@ typedef enum DHCPRawOption { typedef struct DHCPClientId { size_t length; - void *data; + uint8_t *data; } DHCPClientId; typedef struct DHCPLease { @@ -34,6 +34,8 @@ typedef struct DHCPLease { DHCPClientId client_id; + uint8_t htype; /* e.g. ARPHRD_ETHER */ + uint8_t hlen; /* e.g. ETH_ALEN */ be32_t address; be32_t gateway; uint8_t chaddr[16]; @@ -63,6 +65,9 @@ struct sd_dhcp_server { char *timezone; DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX]; + struct in_addr boot_server_address; + char *boot_server_name; + char *boot_filename; OrderedSet *extra_options; OrderedSet *vendor_options; diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index f94340985..0d7813f61 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -11,138 +11,83 @@ #include "sd-event.h" #include "sd-dhcp6-client.h" +#include "dhcp-identifier.h" +#include "dhcp6-option.h" #include "dhcp6-protocol.h" +#include "ether-addr-util.h" #include "hashmap.h" -#include "list.h" #include "macro.h" #include "network-common.h" +#include "ordered-set.h" #include "sparse-endian.h" +#include "time-util.h" -typedef struct sd_dhcp6_option { +/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ +typedef enum DHCP6RequestIA { + DHCP6_REQUEST_IA_NA = 1 << 0, + DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */ + DHCP6_REQUEST_IA_PD = 1 << 2, +} DHCP6RequestIA; + +struct sd_dhcp6_client { unsigned n_ref; - uint32_t enterprise_identifier; - uint16_t option; - void *data; - size_t length; -} sd_dhcp6_option; + int ifindex; + char *ifname; -extern const struct hash_ops dhcp6_option_hash_ops; + struct in6_addr local_address; + struct hw_addr_data hw_addr; + uint16_t arp_type; -/* Common option header */ -typedef struct DHCP6Option { - be16_t code; - be16_t len; - uint8_t data[]; -} _packed_ DHCP6Option; + sd_event *event; + sd_event_source *receive_message; + sd_event_source *timeout_resend; + sd_event_source *timeout_expire; + sd_event_source *timeout_t1; + sd_event_source *timeout_t2; + int event_priority; + int fd; -/* Address option */ -struct iaaddr { - struct in6_addr address; - be32_t lifetime_preferred; - be32_t lifetime_valid; -} _packed_; + DHCP6State state; + bool information_request; + usec_t information_request_time_usec; + usec_t information_refresh_time_usec; + be32_t transaction_id; + usec_t transaction_start; + usec_t retransmit_time; + uint8_t retransmit_count; -/* Prefix Delegation Prefix option */ -struct iapdprefix { - be32_t lifetime_preferred; - be32_t lifetime_valid; - uint8_t prefixlen; - struct in6_addr address; -} _packed_; + bool iaid_set; + DHCP6IA ia_na; + DHCP6IA ia_pd; + DHCP6RequestIA request_ia; + struct duid duid; + size_t duid_len; + be16_t *req_opts; + size_t req_opts_len; + char *fqdn; + char *mudurl; + char **user_class; + char **vendor_class; + OrderedHashmap *extra_options; + OrderedSet *vendor_options; -typedef struct DHCP6Address DHCP6Address; + struct sd_dhcp6_lease *lease; -struct DHCP6Address { - LIST_FIELDS(DHCP6Address, addresses); + sd_dhcp6_client_callback_t callback; + void *userdata; - union { - struct iaaddr iaaddr; - struct iapdprefix iapdprefix; - }; + /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ + bool test_mode; }; -/* Non-temporary Address option */ -struct ia_na { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; -} _packed_; - -/* Prefix Delegation option */ -struct ia_pd { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; -} _packed_; - -/* Temporary Address option */ -struct ia_ta { - be32_t id; -} _packed_; - -typedef struct DHCP6IA { - uint16_t type; - union { - struct ia_na ia_na; - struct ia_pd ia_pd; - struct ia_ta ia_ta; - }; - - LIST_HEAD(DHCP6Address, addresses); -} DHCP6IA; - -typedef struct sd_dhcp6_client sd_dhcp6_client; - -bool dhcp6_option_can_request(uint16_t option); -int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, - size_t optlen, const void *optval); -int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); -int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix); -int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); -int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class); -int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class); -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options); - -int dhcp6_option_parse( - const uint8_t *buf, - size_t buflen, - size_t *offset, - uint16_t *ret_option_code, - size_t *ret_option_data_len, - const uint8_t **ret_option_data); -int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message); -int dhcp6_option_parse_ia( - sd_dhcp6_client *client, - be32_t iaid, - uint16_t option_code, - size_t option_data_len, - const uint8_t *option_data, - DHCP6IA *ret); -int dhcp6_option_parse_addresses( - const uint8_t *optval, - size_t optlen, - struct in6_addr **addrs, - size_t *count); -int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret); -int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret); - int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, const void *packet, size_t len); -int client_parse_message( - sd_dhcp6_client *client, - DHCP6Message *message, - size_t len, - sd_dhcp6_lease *lease); - -const char *dhcp6_message_type_to_string(int s) _const_; -int dhcp6_message_type_from_string(const char *s) _pure_; -const char *dhcp6_message_status_to_string(int s) _const_; -int dhcp6_message_status_from_string(const char *s) _pure_; - +int dhcp6_client_send_message(sd_dhcp6_client *client); void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode); +int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id); #define log_dhcp6_client_errno(client, error, fmt, ...) \ log_interface_prefix_full_errno( \ diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 8ae2ecd6d..1f10dccbb 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -5,11 +5,13 @@ Copyright © 2014-2015 Intel Corporation. All rights reserved. ***/ -#include +#include #include "sd-dhcp6-lease.h" -#include "dhcp6-internal.h" +#include "dhcp6-option.h" +#include "macro.h" +#include "time-util.h" struct sd_dhcp6_lease { unsigned n_ref; @@ -21,10 +23,13 @@ struct sd_dhcp6_lease { uint8_t preference; bool rapid_commit; triple_timestamp timestamp; + usec_t lifetime_t1; + usec_t lifetime_t2; + usec_t lifetime_valid; struct in6_addr server_address; - DHCP6IA ia; - DHCP6IA pd; + DHCP6IA *ia_na; /* Identity association non-temporary addresses */ + DHCP6IA *ia_pd; /* Identity association prefix delegation */ DHCP6Address *addr_iter; DHCP6Address *prefix_iter; @@ -40,17 +45,15 @@ struct sd_dhcp6_lease { char *fqdn; }; -int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire); -DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia); - +int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid); int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len); int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len); int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference); -int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference); +int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret); int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease); -int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); +int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret); int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); @@ -59,3 +62,10 @@ int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_new(sd_dhcp6_lease **ret); +int dhcp6_lease_new_from_message( + sd_dhcp6_client *client, + const DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address, + sd_dhcp6_lease **ret); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 28fe036a4..4e6ee7970 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -9,14 +9,12 @@ #include "sd-dhcp6-client.h" #include "alloc-util.h" -#include "dhcp-identifier.h" #include "dhcp6-internal.h" -#include "dhcp6-lease-internal.h" +#include "dhcp6-option.h" #include "dhcp6-protocol.h" #include "dns-domain.h" #include "escape.h" #include "memory-util.h" -#include "sparse-endian.h" #include "strv.h" #include "unaligned.h" @@ -74,8 +72,8 @@ bool dhcp6_option_can_request(uint16_t option) { return false; case SD_DHCP6_OPTION_CLIENT_FQDN: case SD_DHCP6_OPTION_PANA_AGENT: - case SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE: - case SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE: + case SD_DHCP6_OPTION_POSIX_TIMEZONE: + case SD_DHCP6_OPTION_TZDB_TIMEZONE: return true; case SD_DHCP6_OPTION_ERO: case SD_DHCP6_OPTION_LQ_QUERY: @@ -212,19 +210,15 @@ bool dhcp6_option_can_request(uint16_t option) { } static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { - DHCP6Option *option; - assert_return(buf, -EINVAL); assert_return(*buf, -EINVAL); assert_return(buflen, -EINVAL); - option = (DHCP6Option*) *buf; - if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data)) return -ENOBUFS; - option->code = htobe16(optcode); - option->len = htobe16(optlen); + unaligned_write_be16(*buf + offsetof(DHCP6Option, code), optcode); + unaligned_write_be16(*buf + offsetof(DHCP6Option, len), optlen); *buf += offsetof(DHCP6Option, data); *buflen -= offsetof(DHCP6Option, data); @@ -242,15 +236,13 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, if (r < 0) return r; - memcpy_safe(*buf, optval, optlen); - - *buf += optlen; + *buf = mempcpy_safe(*buf, optval, optlen); *buflen -= optlen; return 0; } -int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) { +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options) { sd_dhcp6_option *options; int r; @@ -259,7 +251,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash assert(buflen); assert(vendor_options); - ORDERED_HASHMAP_FOREACH(options, vendor_options) { + ORDERED_SET_FOREACH(options, vendor_options) { _cleanup_free_ uint8_t *p = NULL; size_t total; @@ -282,14 +274,63 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash return 0; } +static int option_append_ia_address(uint8_t **buf, size_t *buflen, const struct iaaddr *address) { + struct iaaddr a; + int r; + + assert(buf); + assert(*buf); + assert(buflen); + assert(address); + + /* Do not append T1 and T2. */ + a = (struct iaaddr) { + .address = address->address, + }; + + r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr)); + if (r < 0) + return r; + + *buf = mempcpy(*buf, &a, sizeof(struct iaaddr)); + *buflen -= sizeof(struct iaaddr); + + return offsetof(DHCP6Option, data) + sizeof(struct iaaddr); +} + +static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const struct iapdprefix *prefix) { + struct iapdprefix p; + int r; + + assert(buf); + assert(*buf); + assert(buflen); + assert(prefix); + + if (prefix->prefixlen == 0) + return -EINVAL; + + /* Do not append T1 and T2. */ + p = (struct iapdprefix) { + .prefixlen = prefix->prefixlen, + .address = prefix->address, + }; + + r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix)); + if (r < 0) + return r; + + *buf = mempcpy(*buf, &p, sizeof(struct iapdprefix)); + *buflen -= sizeof(struct iapdprefix); + + return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix); +} + int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { - size_t ia_buflen, ia_addrlen = 0; - struct ia_na ia_na; - struct ia_ta ia_ta; - DHCP6Address *addr; + struct ia_header header; + size_t ia_buflen; uint8_t *ia_hdr; uint16_t len; - void *p; int r; assert_return(buf, -EINVAL); @@ -301,23 +342,22 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { switch (ia->type) { case SD_DHCP6_OPTION_IA_NA: - len = DHCP6_OPTION_IA_NA_LEN; - ia_na = (struct ia_na) { - .id = ia->ia_na.id, + case SD_DHCP6_OPTION_IA_PD: + len = sizeof(struct ia_header); + header = (struct ia_header) { + .id = ia->header.id, }; - p = &ia_na; break; case SD_DHCP6_OPTION_IA_TA: - len = DHCP6_OPTION_IA_TA_LEN; - ia_ta = (struct ia_ta) { - .id = ia->ia_ta.id, + len = sizeof(header.id); /* IA_TA does not have lifetime. */ + header = (struct ia_header) { + .id = ia->header.id, }; - p = &ia_ta; break; default: - return -EINVAL; + assert_not_reached(); } if (*buflen < offsetof(DHCP6Option, data) + len) @@ -326,116 +366,25 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) { ia_hdr = *buf; ia_buflen = *buflen; - *buf += offsetof(DHCP6Option, data); - *buflen -= offsetof(DHCP6Option, data); - - memcpy(*buf, p, len); - - *buf += len; - *buflen -= len; - - LIST_FOREACH(addresses, addr, ia->addresses) { - struct iaaddr a = { - .address = addr->iaaddr.address, - }; - - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr)); - if (r < 0) - return r; - - memcpy(*buf, &a, sizeof(struct iaaddr)); - - *buf += sizeof(struct iaaddr); - *buflen -= sizeof(struct iaaddr); - - ia_addrlen += offsetof(DHCP6Option, data) + sizeof(struct iaaddr); - } - - return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen); -} - -static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Address *prefix) { - struct iapdprefix p; - int r; - - assert(buf); - assert(*buf); - assert(buflen); - assert(prefix); - - if (prefix->iapdprefix.prefixlen == 0) - return -EINVAL; - - /* Do not append T1 and T2. */ - - p = (struct iapdprefix) { - .prefixlen = prefix->iapdprefix.prefixlen, - .address = prefix->iapdprefix.address, - }; - - r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix)); - if (r < 0) - return r; - - memcpy(*buf, &p, sizeof(struct iapdprefix)); - - *buf += sizeof(struct iapdprefix); - *buflen -= sizeof(struct iapdprefix); - - return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix); -} - -int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix) { - struct ia_pd ia_pd; - size_t len, pd_buflen; - uint8_t *pd_hdr; - int r; - - assert_return(buf, -EINVAL); - assert_return(*buf, -EINVAL); - assert_return(buflen, -EINVAL); - assert_return(pd, -EINVAL); - assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL); - - /* Do not set T1 and T2. */ - ia_pd = (struct ia_pd) { - .id = pd->ia_pd.id, - }; - len = sizeof(struct ia_pd); - - if (*buflen < offsetof(DHCP6Option, data) + len) - return -ENOBUFS; - - pd_hdr = *buf; - pd_buflen = *buflen; - /* The header will be written at the end of this function. */ *buf += offsetof(DHCP6Option, data); *buflen -= offsetof(DHCP6Option, data); - memcpy(*buf, &ia_pd, len); + *buf = mempcpy(*buf, &header, len); + *buflen -= len; - *buf += sizeof(struct ia_pd); - *buflen -= sizeof(struct ia_pd); - - DHCP6Address *prefix; - LIST_FOREACH(addresses, prefix, pd->addresses) { - r = option_append_pd_prefix(buf, buflen, prefix); + LIST_FOREACH(addresses, addr, ia->addresses) { + if (ia->type == SD_DHCP6_OPTION_IA_PD) + r = option_append_pd_prefix(buf, buflen, &addr->iapdprefix); + else + r = option_append_ia_address(buf, buflen, &addr->iaaddr); if (r < 0) return r; len += r; } - if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) { - r = option_append_pd_prefix(buf, buflen, hint_pd_prefix); - if (r < 0) - return r; - - len += r; - } - - return option_append_hdr(&pd_hdr, &pd_buflen, pd->type, len); + return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len); } int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { @@ -468,7 +417,6 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class) { _cleanup_free_ uint8_t *p = NULL; size_t total = 0, offset = 0; - char * const *s; assert(buf); assert(*buf); @@ -501,7 +449,6 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const _cleanup_free_ uint8_t *p = NULL; uint32_t enterprise_identifier; size_t total, offset; - char * const *s; assert(buf); assert(*buf); @@ -548,7 +495,6 @@ int dhcp6_option_parse( size_t *ret_option_data_len, const uint8_t **ret_option_data) { - const DHCP6Option *option; size_t len; assert(buf); @@ -563,16 +509,15 @@ int dhcp6_option_parse( if (*offset >= buflen - offsetof(DHCP6Option, data)) return -EBADMSG; - option = (const DHCP6Option*) (buf + *offset); - len = be16toh(option->len); + len = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, len)); if (len > buflen - offsetof(DHCP6Option, data) - *offset) return -EBADMSG; - *offset += offsetof(DHCP6Option, data) + len; - *ret_option_code = be16toh(option->code); + *ret_option_code = unaligned_read_be16(buf + *offset + offsetof(DHCP6Option, code)); *ret_option_data_len = len; - *ret_option_data = option->data; + *ret_option_data = buf + *offset + offsetof(DHCP6Option, data); + *offset += offsetof(DHCP6Option, data) + len; return 0; } @@ -601,7 +546,7 @@ int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_s static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) { int r; - assert(buf); + assert(buf || buflen == 0); for(size_t offset = 0; offset < buflen;) { const uint8_t *data; @@ -619,15 +564,15 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t r = dhcp6_option_parse_status(data, data_len, &msg); if (r == -ENOMEM) return r; - if (r < 0) - /* Let's log but ignore the invalid status option. */ - log_dhcp6_client_errno(client, r, - "Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m"); - else if (r > 0) + if (r > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received an IA address or PD prefix option with non-zero status: %s%s%s", strempty(msg), isempty(msg) ? "" : ": ", dhcp6_message_status_to_string(r)); + if (r < 0) + /* Let's log but ignore the invalid status option. */ + log_dhcp6_client_errno(client, r, + "Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m"); break; } default: @@ -638,19 +583,29 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t return 0; } -static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) { +static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) { + _cleanup_free_ DHCP6Address *a = NULL; uint32_t lt_valid, lt_pref; - DHCP6Address *a; int r; - assert(data); - assert(ret); + assert(ia); + assert(data || len == 0); + + if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA address sub-option in an invalid option, ignoring."); if (len < sizeof(struct iaaddr)) return -EBADMSG; - lt_valid = be32toh(((const struct iaaddr*) data)->lifetime_valid); - lt_pref = be32toh(((const struct iaaddr*) data)->lifetime_preferred); + a = new(DHCP6Address, 1); + if (!a) + return -ENOMEM; + + memcpy(&a->iaaddr, data, sizeof(struct iaaddr)); + + lt_valid = be32toh(a->iaaddr.lifetime_valid); + lt_pref = be32toh(a->iaaddr.lifetime_preferred); if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), @@ -667,27 +622,33 @@ static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t return r; } + LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a)); + return 0; +} + +static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia, const uint8_t *data, size_t len) { + _cleanup_free_ DHCP6Address *a = NULL; + uint32_t lt_valid, lt_pref; + int r; + + assert(ia); + assert(data || len == 0); + + if (ia->type != SD_DHCP6_OPTION_IA_PD) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an PD prefix sub-option in an invalid option, ignoring"); + + if (len < sizeof(struct iapdprefix)) + return -EBADMSG; + a = new(DHCP6Address, 1); if (!a) return -ENOMEM; - LIST_INIT(addresses, a); - memcpy(&a->iaaddr, data, sizeof(struct iaaddr)); + memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix)); - *ret = a; - return 0; -} - -static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) { - uint32_t lt_valid, lt_pref; - DHCP6Address *a; - int r; - - if (len < sizeof(struct iapdprefix)) - return -ENOMSG; - - lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid); - lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred); + lt_valid = be32toh(a->iapdprefix.lifetime_valid); + lt_pref = be32toh(a->iapdprefix.lifetime_preferred); if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), @@ -704,14 +665,7 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t return r; } - a = new(DHCP6Address, 1); - if (!a) - return -ENOMEM; - - LIST_INIT(addresses, a); - memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix)); - - *ret = a; + LIST_PREPEND(addresses, ia->addresses, TAKE_PTR(a)); return 0; } @@ -721,16 +675,15 @@ int dhcp6_option_parse_ia( uint16_t option_code, size_t option_data_len, const uint8_t *option_data, - DHCP6IA *ret) { + DHCP6IA **ret) { - _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; - uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX; - be32_t received_iaid; - size_t offset; + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; + uint32_t lt_t1, lt_t2; + size_t header_len; int r; assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD)); - assert(option_data); + assert(option_data || option_data_len == 0); assert(ret); /* This will return the following: @@ -743,56 +696,51 @@ int dhcp6_option_parse_ia( switch (option_code) { case SD_DHCP6_OPTION_IA_NA: - - if (option_data_len < DHCP6_OPTION_IA_NA_LEN) - return -EBADMSG; - - offset = DHCP6_OPTION_IA_NA_LEN; - - received_iaid = ((const struct ia_na*) option_data)->id; - lt_t1 = be32toh(((const struct ia_na*) option_data)->lifetime_t1); - lt_t2 = be32toh(((const struct ia_na*) option_data)->lifetime_t2); - break; - case SD_DHCP6_OPTION_IA_PD: - - if (option_data_len < DHCP6_OPTION_IA_PD_LEN) - return -EBADMSG; - - offset = DHCP6_OPTION_IA_PD_LEN; - - received_iaid = ((const struct ia_pd*) option_data)->id; - lt_t1 = be32toh(((const struct ia_pd*) option_data)->lifetime_t1); - lt_t2 = be32toh(((const struct ia_pd*) option_data)->lifetime_t2); + header_len = sizeof(struct ia_header); break; case SD_DHCP6_OPTION_IA_TA: - if (option_data_len < DHCP6_OPTION_IA_TA_LEN) - return -ENOMSG; - - offset = DHCP6_OPTION_IA_TA_LEN; - - received_iaid = ((const struct ia_ta*) option_data)->id; - lt_t1 = lt_t2 = 0; /* No lifetime for IA_TA. */ + header_len = sizeof(be32_t); /* IA_TA does not have lifetime. */ break; default: assert_not_reached(); } + if (option_data_len < header_len) + return -EBADMSG; + + ia = new(DHCP6IA, 1); + if (!ia) + return -ENOMEM; + + *ia = (DHCP6IA) { + .type = option_code, + }; + memcpy(&ia->header, option_data, header_len); + /* According to RFC8415, IAs which do not match the client's IAID should be ignored, * but not necessary to ignore or refuse the whole message. */ - if (received_iaid != iaid) + if (ia->header.id != iaid) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO), "Received an IA option with a different IAID " "from the one chosen by the client, ignoring."); + /* It is not necessary to check if the lifetime_t2 is zero here, as in that case it will be updated later. */ + lt_t1 = be32toh(ia->header.lifetime_t1); + lt_t2 = be32toh(ia->header.lifetime_t2); + if (lt_t1 > lt_t2) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.", lt_t1, lt_t2); + if (lt_t1 == 0 && lt_t2 > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA option with zero T1 and non-zero T2 (%"PRIu32"sec), ignoring.", + lt_t2); - for (; offset < option_data_len;) { + for (size_t offset = header_len; offset < option_data_len;) { const uint8_t *subdata; size_t subdata_len; uint16_t subopt; @@ -803,41 +751,19 @@ int dhcp6_option_parse_ia( switch (subopt) { case SD_DHCP6_OPTION_IAADDR: { - DHCP6Address *a; - - if (!IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) { - log_dhcp6_client(client, "Received an IA_PD option with an IA address, ignoring."); - continue; - } - - r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a); + r = dhcp6_option_parse_ia_address(client, ia, subdata, subdata_len); if (r == -ENOMEM) return r; - if (r < 0) - /* Ignore the sub-option on non-critical errors. */ - continue; - lt_min = MIN(lt_min, be32toh(a->iaaddr.lifetime_valid)); - LIST_PREPEND(addresses, ia.addresses, a); + /* Ignore non-critical errors in the sub-option. */ break; } case SD_DHCP6_OPTION_IA_PD_PREFIX: { - DHCP6Address *a; - - if (option_code != SD_DHCP6_OPTION_IA_PD) { - log_dhcp6_client(client, "Received an IA_NA or IA_TA option with an PD prefix, ignoring"); - continue; - } - - r = dhcp6_option_parse_ia_pdprefix(client, subdata, subdata_len, &a); + r = dhcp6_option_parse_ia_pdprefix(client, ia, subdata, subdata_len); if (r == -ENOMEM) return r; - if (r < 0) - /* Ignore the sub-option on non-critical errors. */ - continue; - lt_min = MIN(lt_min, be32toh(a->iapdprefix.lifetime_valid)); - LIST_PREPEND(addresses, ia.addresses, a); + /* Ignore non-critical errors in the sub-option. */ break; } case SD_DHCP6_OPTION_STATUS_CODE: { @@ -846,14 +772,14 @@ int dhcp6_option_parse_ia( r = dhcp6_option_parse_status(subdata, subdata_len, &msg); if (r == -ENOMEM) return r; - if (r < 0) - log_dhcp6_client_errno(client, r, - "Received an IA option with an invalid status sub option, ignoring: %m"); - else if (r > 0) + if (r > 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Received an IA option with non-zero status: %s%s%s", strempty(msg), isempty(msg) ? "" : ": ", dhcp6_message_status_to_string(r)); + if (r < 0) + log_dhcp6_client_errno(client, r, + "Received an IA option with an invalid status sub option, ignoring: %m"); break; } default: @@ -861,50 +787,11 @@ int dhcp6_option_parse_ia( } } - if (!ia.addresses) + if (!ia->addresses) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA), "Received an IA option without valid IA addresses or PD prefixes, ignoring."); - if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) && - lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) { - lt_t1 = lt_min / 2; - lt_t2 = lt_min / 10 * 8; - - log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. " - "Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: " - "T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2); - } - - switch(option_code) { - case SD_DHCP6_OPTION_IA_NA: - *ret = (DHCP6IA) { - .type = option_code, - .ia_na.id = iaid, - .ia_na.lifetime_t1 = htobe32(lt_t1), - .ia_na.lifetime_t2 = htobe32(lt_t2), - .addresses = TAKE_PTR(ia.addresses), - }; - break; - case SD_DHCP6_OPTION_IA_TA: - *ret = (DHCP6IA) { - .type = option_code, - .ia_ta.id = iaid, - .addresses = TAKE_PTR(ia.addresses), - }; - break; - case SD_DHCP6_OPTION_IA_PD: - *ret = (DHCP6IA) { - .type = option_code, - .ia_pd.id = iaid, - .ia_pd.lifetime_t1 = htobe32(lt_t1), - .ia_pd.lifetime_t2 = htobe32(lt_t2), - .addresses = TAKE_PTR(ia.addresses), - }; - break; - default: - assert_not_reached(); - } - + *ret = TAKE_PTR(ia); return 0; } diff --git a/src/libsystemd-network/dhcp6-option.h b/src/libsystemd-network/dhcp6-option.h new file mode 100644 index 000000000..80aba7f37 --- /dev/null +++ b/src/libsystemd-network/dhcp6-option.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "sd-dhcp6-client.h" + +#include "hash-funcs.h" +#include "list.h" +#include "macro.h" +#include "ordered-set.h" +#include "sparse-endian.h" + +typedef struct sd_dhcp6_option { + unsigned n_ref; + + uint32_t enterprise_identifier; + uint16_t option; + void *data; + size_t length; +} sd_dhcp6_option; + +extern const struct hash_ops dhcp6_option_hash_ops; + +/* Common option header */ +typedef struct DHCP6Option { + be16_t code; + be16_t len; + uint8_t data[]; +} _packed_ DHCP6Option; + +/* Address option */ +struct iaaddr { + struct in6_addr address; + be32_t lifetime_preferred; + be32_t lifetime_valid; +} _packed_; + +/* Prefix Delegation Prefix option */ +struct iapdprefix { + be32_t lifetime_preferred; + be32_t lifetime_valid; + uint8_t prefixlen; + struct in6_addr address; +} _packed_; + +typedef struct DHCP6Address DHCP6Address; + +struct DHCP6Address { + LIST_FIELDS(DHCP6Address, addresses); + + union { + struct iaaddr iaaddr; + struct iapdprefix iapdprefix; + }; +}; + +struct ia_header { + be32_t id; + be32_t lifetime_t1; + be32_t lifetime_t2; +} _packed_; + +typedef struct DHCP6IA { + uint16_t type; + struct ia_header header; + + LIST_HEAD(DHCP6Address, addresses); +} DHCP6IA; + +void dhcp6_ia_clear_addresses(DHCP6IA *ia); +DHCP6IA *dhcp6_ia_free(DHCP6IA *ia); +DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free); + +bool dhcp6_option_can_request(uint16_t option); + +int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, + size_t optlen, const void *optval); +int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia); +int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); +int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class); +int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class); +int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedSet *vendor_options); + +int dhcp6_option_parse( + const uint8_t *buf, + size_t buflen, + size_t *offset, + uint16_t *ret_option_code, + size_t *ret_option_data_len, + const uint8_t **ret_option_data); +int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message); +int dhcp6_option_parse_ia( + sd_dhcp6_client *client, + be32_t iaid, + uint16_t option_code, + size_t option_data_len, + const uint8_t *option_data, + DHCP6IA **ret); +int dhcp6_option_parse_addresses( + const uint8_t *optval, + size_t optlen, + struct in6_addr **addrs, + size_t *count); +int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret); +int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret); diff --git a/src/libsystemd-network/dhcp6-protocol.c b/src/libsystemd-network/dhcp6-protocol.c new file mode 100644 index 000000000..c399a7ac5 --- /dev/null +++ b/src/libsystemd-network/dhcp6-protocol.c @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "dhcp6-protocol.h" +#include "string-table.h" + +static const char * const dhcp6_state_table[_DHCP6_STATE_MAX] = { + [DHCP6_STATE_STOPPED] = "stopped", + [DHCP6_STATE_INFORMATION_REQUEST] = "information-request", + [DHCP6_STATE_SOLICITATION] = "solicitation", + [DHCP6_STATE_REQUEST] = "request", + [DHCP6_STATE_BOUND] = "bound", + [DHCP6_STATE_RENEW] = "renew", + [DHCP6_STATE_REBIND] = "rebind", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_state, DHCP6State); + +static const char * const dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { + [DHCP6_MESSAGE_SOLICIT] = "Solicit", + [DHCP6_MESSAGE_ADVERTISE] = "Advertise", + [DHCP6_MESSAGE_REQUEST] = "Request", + [DHCP6_MESSAGE_CONFIRM] = "Confirm", + [DHCP6_MESSAGE_RENEW] = "Renew", + [DHCP6_MESSAGE_REBIND] = "Rebind", + [DHCP6_MESSAGE_REPLY] = "Reply", + [DHCP6_MESSAGE_RELEASE] = "Release", + [DHCP6_MESSAGE_DECLINE] = "Decline", + [DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure", + [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request", + [DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward", + [DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply", + [DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query", + [DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply", + [DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done", + [DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data", + [DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request", + [DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply", + [DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query", + [DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response", + [DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query", + [DHCP6_MESSAGE_START_TLS] = "Start TLS", + [DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update", + [DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply", + [DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request", + [DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response", + [DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request", + [DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All", + [DHCP6_MESSAGE_UPDATE_DONE] = "Update Done", + [DHCP6_MESSAGE_CONNECT] = "Connect", + [DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply", + [DHCP6_MESSAGE_DISCONNECT] = "Disconnect", + [DHCP6_MESSAGE_STATE] = "State", + [DHCP6_MESSAGE_CONTACT] = "Contact", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, DHCP6MessageType); + +static const char * const dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { + [DHCP6_STATUS_SUCCESS] = "Success", + [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", + [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", + [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", + [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", + [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", + [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available", + [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type", + [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query", + [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured", + [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed", + [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated", + [DHCP6_STATUS_DATA_MISSING] = "Data missing", + [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete", + [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported", + [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused", + [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use", + [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict", + [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information", + [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information", + [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down", + [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported", + [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew", +}; + +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, DHCP6Status); diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 5d2af439e..f4e47857e 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -28,8 +28,8 @@ typedef struct DHCP6Message DHCP6Message; #define DHCP6_MIN_OPTIONS_SIZE \ 1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr) -#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \ - { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ +#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \ + { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } } enum { @@ -100,13 +100,15 @@ typedef enum DHCP6MessageType { DHCP6_MESSAGE_STATE = 34, /* RFC 8156 */ DHCP6_MESSAGE_CONTACT = 35, /* RFC 8156 */ _DHCP6_MESSAGE_TYPE_MAX, - _DHCP6_MESSAGE_TYPE_INVALID = -EINVAL, + _DHCP6_MESSAGE_TYPE_INVALID = -EINVAL, } DHCP6MessageType; typedef enum DHCP6NTPSubOption { DHCP6_NTP_SUBOPTION_SRV_ADDR = 1, DHCP6_NTP_SUBOPTION_MC_ADDR = 2, DHCP6_NTP_SUBOPTION_SRV_FQDN = 3, + _DHCP6_NTP_SUBOPTION_MAX, + _DHCP6_NTP_SUBOPTION_INVALID = -EINVAL, } DHCP6NTPSubOption; /* @@ -138,7 +140,7 @@ typedef enum DHCP6Status { DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED = 21, DHCP6_STATUS_EXCESSIVE_TIME_SKEW = 22, _DHCP6_STATUS_MAX, - _DHCP6_STATUS_INVALID = -EINVAL, + _DHCP6_STATUS_INVALID = -EINVAL, } DHCP6Status; typedef enum DHCP6FQDNFlag { @@ -146,3 +148,9 @@ typedef enum DHCP6FQDNFlag { DHCP6_FQDN_FLAG_O = 1 << 1, DHCP6_FQDN_FLAG_N = 1 << 2, } DHCP6FQDNFlag; + +const char *dhcp6_state_to_string(DHCP6State s) _const_; +const char *dhcp6_message_type_to_string(DHCP6MessageType s) _const_; +DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_; +const char *dhcp6_message_status_to_string(DHCP6Status s) _const_; +DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_; diff --git a/src/libsystemd-network/fuzz-dhcp-client.c b/src/libsystemd-network/fuzz-dhcp-client.c new file mode 100644 index 000000000..1812a6195 --- /dev/null +++ b/src/libsystemd-network/fuzz-dhcp-client.c @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#include + +#include "alloc-util.h" +#include "fuzz.h" +#include "sd-event.h" + +#include "sd-dhcp-client.c" + +int dhcp_network_bind_raw_socket( + int ifindex, + union sockaddr_union *link, + uint32_t id, + const uint8_t *addr, size_t addr_len, + const uint8_t *bcaddr, size_t bcaddr_len, + uint16_t arp_type, uint16_t port) { + + int fd; + fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (fd < 0) + return -errno; + + return fd; +} + +int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link, const void *packet, size_t len) { + return len; +} + +int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int ip_service_type) { + int fd; + + fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); + if (fd < 0) + return -errno; + + return fd; +} + +int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const void *packet, size_t len) { + return len; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + uint8_t mac_addr[] = {'A', 'B', 'C', '1', '2', '3'}; + uint8_t bcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + _cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL; + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + int res, r; + + if (!getenv("SYSTEMD_LOG_LEVEL")) + log_set_max_level(LOG_CRIT); + + r = sd_dhcp_client_new(&client, false); + assert_se(r >= 0); + assert_se(client); + + assert_se(sd_event_new(&e) >= 0); + + r = sd_dhcp_client_attach_event(client, e, 0); + assert_se(r >= 0); + + assert_se(sd_dhcp_client_set_ifindex(client, 42) >= 0); + assert_se(sd_dhcp_client_set_mac(client, mac_addr, bcast_addr, ETH_ALEN, ARPHRD_ETHER) >= 0); + dhcp_client_set_test_mode(client, true); + + res = sd_dhcp_client_start(client); + assert_se(IN_SET(res, 0, -EINPROGRESS)); + client->xid = 2; + + (void) client_handle_offer(client, (DHCPMessage*) data, size); + + assert_se(sd_dhcp_client_stop(client) >= 0); + + return 0; +} diff --git a/src/libsystemd-network/fuzz-dhcp-server-relay.c b/src/libsystemd-network/fuzz-dhcp-server-relay.c new file mode 100644 index 000000000..a53e1c253 --- /dev/null +++ b/src/libsystemd-network/fuzz-dhcp-server-relay.c @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#include + +#include "fuzz.h" + +#include "sd-dhcp-server.c" + +ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { + return len; +} + +ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; + struct in_addr address = {.s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))}; + union in_addr_union relay_address; + _cleanup_free_ uint8_t *message = NULL; + + if (size < sizeof(DHCPMessage)) + return 0; + + assert_se(sd_dhcp_server_new(&server, 1) >= 0); + assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); + assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0); + assert_se(in_addr_from_string(AF_INET, "192.168.5.1", &relay_address) >= 0); + assert_se(sd_dhcp_server_set_relay_target(server, &relay_address.in) >= 0); + assert_se(sd_dhcp_server_set_bind_to_interface(server, false) >= 0); + assert_se(sd_dhcp_server_set_relay_agent_information(server, "string:sample_circuit_id", "string:sample_remote_id") >= 0); + + size_t buflen = size; + buflen += relay_agent_information_length(server->agent_circuit_id, server->agent_remote_id) + 2; + assert_se(message = malloc(buflen)); + memcpy(message, data, size); + + server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY); + assert_se(server->fd >= 0); + + (void) dhcp_server_relay_message(server, (DHCPMessage *) message, size - sizeof(DHCPMessage), buflen); + return 0; +} diff --git a/src/libsystemd-network/fuzz-dhcp-server.c b/src/libsystemd-network/fuzz-dhcp-server.c index e90284f6f..b35f1ac6d 100644 --- a/src/libsystemd-network/fuzz-dhcp-server.c +++ b/src/libsystemd-network/fuzz-dhcp-server.c @@ -17,39 +17,63 @@ ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) { return 0; } +static void add_lease(sd_dhcp_server *server, const struct in_addr *server_address, uint8_t i) { + static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3}; + DHCPLease *lease; + + assert(server); + + assert_se(lease = new0(DHCPLease, 1)); + lease->client_id.length = 2; + assert_se(lease->client_id.data = malloc(2)); + lease->client_id.data[0] = 2; + lease->client_id.data[1] = i; + lease->address = htobe32(UINT32_C(10) << 24 | i); + lease->gateway = server_address->s_addr; + lease->expiration = UINT64_MAX; + lease->htype = ARPHRD_ETHER; + lease->hlen = ETH_ALEN; + memcpy(lease->chaddr, chaddr, ETH_ALEN); + assert_se(hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease) >= 0); + assert_se(hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease) >= 0); + lease->server = server; +} + +static void add_static_lease(sd_dhcp_server *server, uint8_t i) { + uint8_t id[2] = { 2, i }; + + assert(server); + + assert_se(sd_dhcp_server_set_static_lease(server, + &(struct in_addr) { .s_addr = htobe32(UINT32_C(10) << 24 | i)}, + id, ELEMENTSOF(id)) >= 0); +} + int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; - struct in_addr address = {.s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))}; - static const uint8_t chaddr[] = {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3}; - uint8_t *client_id; - DHCPLease *lease; + struct in_addr address = { .s_addr = htobe32(UINT32_C(10) << 24 | UINT32_C(1))}; + _cleanup_free_ uint8_t *duped = NULL; if (size < sizeof(DHCPMessage)) return 0; + assert_se(duped = memdup(data, size)); + assert_se(sd_dhcp_server_new(&server, 1) >= 0); + assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); server->fd = open("/dev/null", O_RDWR|O_CLOEXEC|O_NOCTTY); assert_se(server->fd >= 0); assert_se(sd_dhcp_server_configure_pool(server, &address, 24, 0, 0) >= 0); - /* add a lease to the pool to expose additional code paths */ - client_id = malloc(2); - assert_se(client_id); - client_id[0] = 2; - client_id[1] = 2; - lease = new0(DHCPLease, 1); - assert_se(lease); - lease->client_id.length = 2; - lease->client_id.data = client_id; - lease->address = htobe32(UINT32_C(10) << 24 | UINT32_C(2)); - lease->gateway = htobe32(UINT32_C(10) << 24 | UINT32_C(1)); - lease->expiration = UINT64_MAX; - memcpy(lease->chaddr, chaddr, 16); - assert_se(hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease) >= 0); - assert_se(hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease) >= 0); - lease->server = server; + /* add leases to the pool to expose additional code paths */ + add_lease(server, &address, 2); + add_lease(server, &address, 3); - (void) dhcp_server_handle_message(server, (DHCPMessage*)data, size); + /* add static leases */ + add_static_lease(server, 3); + add_static_lease(server, 4); + + (void) dhcp_server_handle_message(server, (DHCPMessage*) duped, size); return 0; } diff --git a/src/libsystemd-network/fuzz-dhcp6-client.c b/src/libsystemd-network/fuzz-dhcp6-client.c index f62ab468d..32e35510e 100644 --- a/src/libsystemd-network/fuzz-dhcp6-client.c +++ b/src/libsystemd-network/fuzz-dhcp6-client.c @@ -6,43 +6,59 @@ #include "sd-event.h" #include "dhcp6-internal.h" -#include "dhcp6-protocol.h" +#include "event-util.h" #include "fd-util.h" #include "fuzz.h" static int test_dhcp_fd[2] = { -1, -1 }; -int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, - const void *packet, size_t len) { +int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, const void *packet, size_t len) { return len; } int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) >= 0); - return test_dhcp_fd[0]; + return TAKE_FD(test_dhcp_fd[0]); } -static void fuzz_client(const uint8_t *data, size_t size, bool is_information_request_enabled) { - _cleanup_(sd_event_unrefp) sd_event *e = NULL; - _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; - struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; - - assert_se(sd_event_new(&e) >= 0); - assert_se(sd_dhcp6_client_new(&client) >= 0); - assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); - assert_se(sd_dhcp6_client_set_ifindex(client, 42) == 0); - assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); - assert_se(sd_dhcp6_client_set_information_request(client, is_information_request_enabled) == 0); - dhcp6_client_set_test_mode(client, true); - +static void fuzz_client(sd_dhcp6_client *client, const uint8_t *data, size_t size, DHCP6State state) { + assert_se(sd_dhcp6_client_set_information_request(client, state == DHCP6_STATE_INFORMATION_REQUEST) >= 0); assert_se(sd_dhcp6_client_start(client) >= 0); + client->state = state; + if (size >= sizeof(DHCP6Message)) - assert_se(sd_dhcp6_client_set_transaction_id(client, htobe32(0x00ffffff) & ((const DHCP6Message *) data)->transaction_id) == 0); + assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message *) data)->transaction_id) == 0); + + /* These states does not require lease to send message. */ + if (IN_SET(client->state, DHCP6_STATE_INFORMATION_REQUEST, DHCP6_STATE_SOLICITATION)) + assert_se(dhcp6_client_send_message(client) >= 0); assert_se(write(test_dhcp_fd[1], data, size) == (ssize_t) size); - sd_event_run(e, UINT64_MAX); + assert_se(sd_event_run(sd_dhcp6_client_get_event(client), UINT64_MAX) > 0); + + /* Check the state transition. */ + if (client->state != state) + switch (state) { + case DHCP6_STATE_INFORMATION_REQUEST: + assert_se(client->state == DHCP6_STATE_STOPPED); + break; + case DHCP6_STATE_SOLICITATION: + assert_se(IN_SET(client->state, DHCP6_STATE_REQUEST, DHCP6_STATE_BOUND)); + break; + case DHCP6_STATE_REQUEST: + assert_se(client->state == DHCP6_STATE_BOUND); + break; + default: + assert_not_reached(); + } + + /* Send message if the client has a lease. */ + if (state != DHCP6_STATE_INFORMATION_REQUEST && sd_dhcp6_client_get_lease(client, NULL) >= 0) { + client->state = DHCP6_STATE_REQUEST; + dhcp6_client_send_message(client); + } assert_se(sd_dhcp6_client_stop(client) >= 0); @@ -50,14 +66,43 @@ static void fuzz_client(const uint8_t *data, size_t size, bool is_information_re } int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_(sd_event_unrefp) sd_event *e = NULL; + _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + _cleanup_(sd_dhcp6_option_unrefp) sd_dhcp6_option *v1 = NULL, *v2 = NULL; + struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; + struct in6_addr hint = { { { 0x3f, 0xfe, 0x05, 0x01, 0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } } }; + static const char *v1_data = "hogehoge", *v2_data = "foobar"; + if (size > 65536) return 0; - /* This triggers client_receive_advertise */ - fuzz_client(data, size, false); + assert_se(sd_event_new(&e) >= 0); + assert_se(sd_dhcp6_client_new(&client) >= 0); + assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); + assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0); + assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); + dhcp6_client_set_test_mode(client, true); - /* This triggers client_receive_reply */ - fuzz_client(data, size, true); + /* Used when sending message. */ + assert_se(sd_dhcp6_client_set_fqdn(client, "example.com") == 1); + assert_se(sd_dhcp6_client_set_request_mud_url(client, "https://www.example.com/mudfile.json") >= 0); + assert_se(sd_dhcp6_client_set_request_user_class(client, STRV_MAKE("u1", "u2", "u3")) >= 0); + assert_se(sd_dhcp6_client_set_request_vendor_class(client, STRV_MAKE("v1", "v2", "v3")) >= 0); + assert_se(sd_dhcp6_client_set_prefix_delegation_hint(client, 48, &hint) >= 0); + assert_se(sd_dhcp6_option_new(123, v1_data, strlen(v1_data), 12345, &v1) >= 0); + assert_se(sd_dhcp6_option_new(456, v2_data, strlen(v2_data), 45678, &v2) >= 0); + assert_se(sd_dhcp6_client_add_vendor_option(client, v1) >= 0); + assert_se(sd_dhcp6_client_add_vendor_option(client, v2) >= 0); + + fuzz_client(client, data, size, DHCP6_STATE_INFORMATION_REQUEST); + fuzz_client(client, data, size, DHCP6_STATE_SOLICITATION); + + /* If size is zero, then the resend timer will be triggered at first, + * but in the REQUEST state the client must have a lease. */ + if (size == 0) + return 0; + + fuzz_client(client, data, size, DHCP6_STATE_REQUEST); return 0; } diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c index 44847b2b9..b05601998 100644 --- a/src/libsystemd-network/lldp-neighbor.c +++ b/src/libsystemd-network/lldp-neighbor.c @@ -333,9 +333,9 @@ void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) { usec_t base; /* Use the packet's timestamp if there is one known */ - base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic()); - if (base <= 0 || base == USEC_INFINITY) - base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */ + base = triple_timestamp_by_clock(&n->timestamp, CLOCK_BOOTTIME); + if (!timestamp_is_set(base)) + base = now(CLOCK_BOOTTIME); /* Otherwise, take the current time */ n->until = usec_add(base, n->ttl * USEC_PER_SEC); } else diff --git a/src/libsystemd-network/meson.build b/src/libsystemd-network/meson.build index eff32b7a6..ff59ec0f3 100644 --- a/src/libsystemd-network/meson.build +++ b/src/libsystemd-network/meson.build @@ -17,6 +17,8 @@ sources = files( 'dhcp6-lease-internal.h', 'dhcp6-network.c', 'dhcp6-option.c', + 'dhcp6-option.h', + 'dhcp6-protocol.c', 'dhcp6-protocol.h', 'icmp6-util.c', 'icmp6-util.h', @@ -57,67 +59,75 @@ libsystemd_network_includes = [includes, include_directories('.')] ############################################################ tests += [ - [['src/libsystemd-network/test-dhcp-option.c'], + [files('test-dhcp-option.c'), [libshared, libsystemd_network]], - [['src/libsystemd-network/test-sd-dhcp-lease.c'], + [files('test-sd-dhcp-lease.c'), [libshared, libsystemd_network]], - [['src/libsystemd-network/test-dhcp-client.c'], + [files('test-dhcp-client.c'), [libshared, libsystemd_network]], - [['src/libsystemd-network/test-dhcp-server.c'], + [files('test-dhcp-server.c'), [libshared, libsystemd_network]], - [['src/libsystemd-network/test-ipv4ll.c'], + [files('test-ipv4ll.c'), [libshared, libsystemd_network]], - [['src/libsystemd-network/test-ipv4ll-manual.c'], + [files('test-ipv4ll-manual.c'), [libshared, libsystemd_network], [], [], '', 'manual'], - [['src/libsystemd-network/test-acd.c'], + [files('test-acd.c'), [libshared, libsystemd_network], [], [], '', 'manual'], - [['src/libsystemd-network/test-ndisc-rs.c'], + [files('test-ndisc-rs.c'), [libshared, libsystemd_network]], - [['src/libsystemd-network/test-ndisc-ra.c'], + [files('test-ndisc-ra.c'), [libshared, libsystemd_network]], - [['src/libsystemd-network/test-dhcp6-client.c'], + [files('test-dhcp6-client.c'), [libshared, libsystemd_network]], - [['src/libsystemd-network/test-lldp-rx.c'], + [files('test-lldp-rx.c'), [libshared, libsystemd_network]], ] fuzzers += [ - [['src/libsystemd-network/fuzz-dhcp6-client.c'], + [files('fuzz-dhcp-client.c'), [libshared, libsystemd_network]], - [['src/libsystemd-network/fuzz-dhcp-server.c'], + [files('fuzz-dhcp6-client.c'), + [libshared, + libsystemd_network]], + + [files('fuzz-dhcp-server.c'), [libsystemd_network, libshared]], - [['src/libsystemd-network/fuzz-lldp-rx.c'], + [files('fuzz-dhcp-server-relay.c'), + [libsystemd_network, + libshared]], + + [files('fuzz-lldp-rx.c'), [libshared, libsystemd_network]], - [['src/libsystemd-network/fuzz-ndisc-rs.c'], + [files('fuzz-ndisc-rs.c'), [libshared, libsystemd_network]], ] diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 895a00d01..5aa225e97 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -21,7 +21,7 @@ int deserialize_in6_addrs(struct in6_addr **addresses, const char *string); struct sd_dhcp_route; struct sd_dhcp_lease; -void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size); +void serialize_dhcp_routes(FILE *f, const char *key, struct sd_dhcp_route **routes, size_t size); int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, const char *string); /* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */ diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index c33be947b..e8534e8e8 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -145,11 +145,11 @@ static const uint8_t default_req_opts_anonymize[] = { SD_DHCP_OPTION_ROUTER, /* 3 */ SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */ SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */ - SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */ + SD_DHCP_OPTION_ROUTER_DISCOVERY, /* 31 */ SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */ SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */ - SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */ - SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */ + SD_DHCP_OPTION_NETBIOS_NAME_SERVER, /* 44 */ + SD_DHCP_OPTION_NETBIOS_NODE_TYPE, /* 46 */ SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */ SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */ SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */ @@ -450,7 +450,7 @@ static int dhcp_client_set_iaid_duid_internal( bool iaid_append, bool iaid_set, uint32_t iaid, - uint16_t duid_type, + DUIDType duid_type, const void *duid, size_t duid_len, usec_t llt_time) { @@ -489,37 +489,20 @@ static int dhcp_client_set_iaid_duid_internal( client->client_id.ns.duid.type = htobe16(duid_type); memcpy(&client->client_id.ns.duid.raw.data, duid, duid_len); len = sizeof(client->client_id.ns.duid.type) + duid_len; - } else - switch (duid_type) { - case DUID_TYPE_LLT: - if (client->mac_addr_len == 0) - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set."); - r = dhcp_identifier_set_duid_llt(&client->client_id.ns.duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-LLT: %m"); - break; - case DUID_TYPE_EN: - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-EN: %m"); - break; - case DUID_TYPE_LL: - if (client->mac_addr_len == 0) - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set."); - - r = dhcp_identifier_set_duid_ll(&client->client_id.ns.duid, client->mac_addr, client->mac_addr_len, client->arp_type, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-LL: %m"); - break; - case DUID_TYPE_UUID: - r = dhcp_identifier_set_duid_uuid(&client->client_id.ns.duid, &len); - if (r < 0) - return log_dhcp_client_errno(client, r, "Failed to set DUID-UUID: %m"); - break; - default: - return log_dhcp_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type"); - } + } else { + r = dhcp_identifier_set_duid(duid_type, client->mac_addr, client->mac_addr_len, + client->arp_type, llt_time, client->test_mode, + &client->client_id.ns.duid, &len); + if (r == -EOPNOTSUPP) + return log_dhcp_client_errno(client, r, + "Failed to set %s. MAC address is not set or " + "interface type is not supported.", + duid_type_to_string(duid_type)); + if (r < 0) + return log_dhcp_client_errno(client, r, "Failed to set %s: %m", + duid_type_to_string(duid_type)); + } client->client_id_len = sizeof(client->client_id.type) + len + (iaid_append ? sizeof(client->client_id.ns.iaid) : 0); @@ -612,7 +595,6 @@ int sd_dhcp_client_set_user_class( sd_dhcp_client *client, char * const *user_class) { - char * const *p; char **s = NULL; assert_return(client, -EINVAL); @@ -832,13 +814,14 @@ static int client_message_init( return -ENOMEM; r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type, - client->arp_type, optlen, &optoffset); + client->arp_type, client->mac_addr_len, client->mac_addr, + optlen, &optoffset); if (r < 0) return r; /* Although 'secs' field is a SHOULD in RFC 2131, certain DHCP servers refuse to issue an DHCP lease if 'secs' is set to zero */ - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); if (r < 0) return r; assert(time_now >= client->start_time); @@ -848,7 +831,7 @@ static int client_message_init( secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1; packet->dhcp.secs = htobe16(secs); - /* RFC2132 section 4.1 + /* RFC2131 section 4.1 A client that cannot receive unicast IP datagrams until its protocol software has been configured with an IP address SHOULD set the BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or @@ -862,15 +845,6 @@ static int client_message_init( if (client->request_broadcast || client->arp_type != ARPHRD_ETHER) packet->dhcp.flags = htobe16(0x8000); - /* RFC2132 section 4.1.1: - The client MUST include its hardware address in the ’chaddr’ field, if - necessary for delivery of DHCP reply messages. Non-Ethernet - interfaces will leave 'chaddr' empty and use the client identifier - instead (eg, RFC 4390 section 2.1). - */ - if (client->arp_type == ARPHRD_ETHER) - memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN); - /* If no client identifier exists, construct an RFC 4361-compliant one */ if (client->client_id_len == 0) { size_t duid_len; @@ -884,7 +858,7 @@ static int client_message_init( if (r < 0) return r; - r = dhcp_identifier_set_duid_en(&client->client_id.ns.duid, &duid_len); + r = dhcp_identifier_set_duid_en(client->test_mode, &client->client_id.ns.duid, &duid_len); if (r < 0) return r; @@ -1272,7 +1246,7 @@ static int client_timeout_resend( assert(client); assert(client->event); - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); if (r < 0) goto error; @@ -1320,7 +1294,7 @@ static int client_timeout_resend( } r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, next_timeout, 10 * USEC_PER_MSEC, client_timeout_resend, client, client->event_priority, "dhcp4-resend-timer", true); @@ -1420,12 +1394,12 @@ static int client_initialize_time_events(sd_dhcp_client *client) { assert(client->event); if (client->start_delay > 0) { - assert_se(sd_event_now(client->event, clock_boottime_or_monotonic(), &usec) >= 0); + assert_se(sd_event_now(client->event, CLOCK_BOOTTIME, &usec) >= 0); usec += client->start_delay; } r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, usec, 0, client_timeout_resend, client, client->event_priority, "dhcp4-resend-timer", true); @@ -1466,7 +1440,7 @@ static int client_start_delayed(sd_dhcp_client *client) { client->fd = r; if (IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_INIT_REBOOT)) - client->start_time = now(clock_boottime_or_monotonic()); + client->start_time = now(CLOCK_BOOTTIME); return client_initialize_events(client, client_receive_message_raw); } @@ -1710,7 +1684,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { return 0; } - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); if (r < 0) return r; assert(client->request_sent <= time_now); @@ -1743,7 +1717,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm lifetime timeout */ r = event_reset_time(client->event, &client->timeout_expire, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, client->expire_time, 10 * USEC_PER_MSEC, client_timeout_expire, client, client->event_priority, "dhcp4-lifetime", true); @@ -1759,7 +1733,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm T2 timeout */ r = event_reset_time(client->event, &client->timeout_t2, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, client->t2_time, 10 * USEC_PER_MSEC, client_timeout_t2, client, client->event_priority, "dhcp4-t2-timeout", true); @@ -1775,7 +1749,7 @@ static int client_set_lease_timeouts(sd_dhcp_client *client) { /* arm T1 timeout */ r = event_reset_time(client->event, &client->timeout_t1, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, client->t1_time, 10 * USEC_PER_MSEC, client_timeout_t1, client, client->event_priority, "dhcp4-t1-timer", true); @@ -1810,7 +1784,7 @@ static int client_handle_message(sd_dhcp_client *client, DHCPMessage *message, i client->attempt = 0; r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, 0, 0, client_timeout_resend, client, client->event_priority, "dhcp4-resend-timer", true); diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index b87af0473..e6e0e08ab 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -215,25 +215,38 @@ int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr) { * The returned routes array must be freed by the caller. * Route objects have the same lifetime of the lease and must not be freed. */ -int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes) { - sd_dhcp_route **ret; - unsigned i; +static int dhcp_lease_get_routes(sd_dhcp_route *routes, size_t n_routes, sd_dhcp_route ***ret) { + assert(routes || n_routes == 0); - assert_return(lease, -EINVAL); - assert_return(routes, -EINVAL); - - if (lease->static_route_size <= 0) + if (n_routes <= 0) return -ENODATA; - ret = new(sd_dhcp_route *, lease->static_route_size); - if (!ret) - return -ENOMEM; + if (ret) { + sd_dhcp_route **buf; - for (i = 0; i < lease->static_route_size; i++) - ret[i] = &lease->static_route[i]; + buf = new(sd_dhcp_route*, n_routes); + if (!buf) + return -ENOMEM; - *routes = ret; - return (int) lease->static_route_size; + for (size_t i = 0; i < n_routes; i++) + buf[i] = &routes[i]; + + *ret = buf; + } + + return (int) n_routes; +} + +int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret) { + assert_return(lease, -EINVAL); + + return dhcp_lease_get_routes(lease->static_routes, lease->n_static_routes, ret); +} + +int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret) { + assert_return(lease, -EINVAL); + + return dhcp_lease_get_routes(lease->classless_routes, lease->n_classless_routes, ret); } int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) { @@ -312,7 +325,8 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) free(lease->servers[i].addr); - free(lease->static_route); + free(lease->static_routes); + free(lease->classless_routes); free(lease->client_id); free(lease->vendor_specific); strv_free(lease->search_domains); @@ -467,17 +481,11 @@ static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_a return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret); } -static int lease_parse_routes( - const uint8_t *option, - size_t len, - struct sd_dhcp_route **routes, - size_t *routes_size) { - +static int lease_parse_static_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) { int r; + assert(lease); assert(option || len <= 0); - assert(routes); - assert(routes_size); if (len % 8 != 0) return -EINVAL; @@ -495,73 +503,66 @@ static int lease_parse_routes( len -= 8; r = in4_addr_default_prefixlen(&dst, &prefixlen); - if (r < 0) - return -EINVAL; + if (r < 0) { + log_debug("sd-dhcp-lease: cannot determine class of received static route, ignoring."); + continue; + } (void) in4_addr_mask(&dst, prefixlen); - if (!GREEDY_REALLOC(*routes, *routes_size + 1)) + if (!GREEDY_REALLOC(lease->static_routes, lease->n_static_routes + 1)) return -ENOMEM; - (*routes)[*routes_size] = (struct sd_dhcp_route) { + lease->static_routes[lease->n_static_routes++] = (struct sd_dhcp_route) { .dst_addr = dst, .gw_addr = gw, .dst_prefixlen = prefixlen, - .option = SD_DHCP_OPTION_STATIC_ROUTE, }; - - (*routes_size)++; } return 0; } /* parses RFC3442 Classless Static Route Option */ -static int lease_parse_classless_routes( - const uint8_t *option, size_t len, - struct sd_dhcp_route **routes, size_t *routes_size) { - +static int lease_parse_classless_routes(sd_dhcp_lease *lease, const uint8_t *option, size_t len) { + assert(lease); assert(option || len <= 0); - assert(routes); - assert(routes_size); - if (len <= 0) - return 0; - - /* option format: (subnet-mask-width significant-subnet-octets gateway-ip)* */ + /* option format: (subnet-mask-width significant-subnet-octets gateway-ip) */ while (len > 0) { - uint8_t dst_octets; - struct sd_dhcp_route *route; + uint8_t prefixlen, dst_octets; + struct in_addr dst = {}, gw; - if (!GREEDY_REALLOC(*routes, *routes_size + 1)) - return -ENOMEM; - - route = *routes + *routes_size; - route->option = SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE; - - dst_octets = (*option == 0 ? 0 : ((*option - 1) / 8) + 1); - route->dst_prefixlen = *option; + prefixlen = *option; option++; len--; + dst_octets = DIV_ROUND_UP(prefixlen, 8); + /* can't have more than 4 octets in IPv4 */ if (dst_octets > 4 || len < dst_octets) return -EINVAL; - route->dst_addr.s_addr = 0; - memcpy(&route->dst_addr.s_addr, option, dst_octets); + memcpy(&dst, option, dst_octets); option += dst_octets; len -= dst_octets; if (len < 4) return -EINVAL; - assert_se(lease_parse_be32(option, 4, &route->gw_addr.s_addr) >= 0); + assert_se(lease_parse_be32(option, 4, &gw.s_addr) >= 0); option += 4; len -= 4; - (*routes_size)++; + if (!GREEDY_REALLOC(lease->classless_routes, lease->n_classless_routes + 1)) + return -ENOMEM; + + lease->classless_routes[lease->n_classless_routes++] = (struct sd_dhcp_route) { + .dst_addr = dst, + .gw_addr = gw, + .dst_prefixlen = prefixlen, + }; } return 0; @@ -703,12 +704,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; case SD_DHCP_OPTION_STATIC_ROUTE: - r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size); + r = lease_parse_static_routes(lease, option, len); if (r < 0) log_debug_errno(r, "Failed to parse static routes, ignoring: %m"); break; - case SD_DHCP_OPTION_INTERFACE_MTU: + case SD_DHCP_OPTION_MTU_INTERFACE: r = lease_parse_u16(option, len, &lease->mtu, 68); if (r < 0) log_debug_errno(r, "Failed to parse MTU, ignoring: %m"); @@ -728,7 +729,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; - case SD_DHCP_OPTION_DOMAIN_SEARCH_LIST: + case SD_DHCP_OPTION_DOMAIN_SEARCH: r = dhcp_lease_parse_search_domains(option, len, &lease->search_domains); if (r < 0) log_debug_errno(r, "Failed to parse Domain Search List, ignoring: %m"); @@ -749,28 +750,25 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void log_debug_errno(r, "Failed to parse root path, ignoring: %m"); break; - case SD_DHCP_OPTION_RENEWAL_T1_TIME: + case SD_DHCP_OPTION_RENEWAL_TIME: r = lease_parse_u32(option, len, &lease->t1, 1); if (r < 0) log_debug_errno(r, "Failed to parse T1 time, ignoring: %m"); break; - case SD_DHCP_OPTION_REBINDING_T2_TIME: + case SD_DHCP_OPTION_REBINDING_TIME: r = lease_parse_u32(option, len, &lease->t2, 1); if (r < 0) log_debug_errno(r, "Failed to parse T2 time, ignoring: %m"); break; case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE: - r = lease_parse_classless_routes( - option, len, - &lease->static_route, - &lease->static_route_size); + r = lease_parse_classless_routes(lease, option, len); if (r < 0) log_debug_errno(r, "Failed to parse classless routes, ignoring: %m"); break; - case SD_DHCP_OPTION_NEW_TZDB_TIMEZONE: { + case SD_DHCP_OPTION_TZDB_TIMEZONE: { _cleanup_free_ char *tz = NULL; r = lease_parse_string(option, len, &tz); @@ -918,13 +916,15 @@ int dhcp_lease_parse_search_domains(const uint8_t *option, size_t len, char ***d } int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const void *data, uint8_t len) { - struct sd_dhcp_raw_option *cur, *option; + struct sd_dhcp_raw_option *option, *before = NULL; assert(lease); LIST_FOREACH(options, cur, lease->private_options) { - if (tag < cur->tag) + if (tag < cur->tag) { + before = cur; break; + } if (tag == cur->tag) { log_debug("Ignoring duplicate option, tagged %i.", tag); return 0; @@ -943,7 +943,7 @@ int dhcp_lease_insert_private_option(sd_dhcp_lease *lease, uint8_t tag, const vo return -ENOMEM; } - LIST_INSERT_BEFORE(options, lease->private_options, cur, option); + LIST_INSERT_BEFORE(options, lease->private_options, before, option); return 0; } @@ -963,7 +963,6 @@ int dhcp_lease_new(sd_dhcp_lease **ret) { int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { _cleanup_(unlink_and_freep) char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; - struct sd_dhcp_raw_option *option; struct in_addr address; const struct in_addr *addresses; const void *client_id, *data; @@ -972,7 +971,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { const char *string; uint16_t mtu; _cleanup_free_ sd_dhcp_route **routes = NULL; - char **search_domains = NULL; + char **search_domains; uint32_t t1, t2, lifetime; int r; @@ -1071,9 +1070,14 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r >= 0) fprintf(f, "ROOT_PATH=%s\n", string); - r = sd_dhcp_lease_get_routes(lease, &routes); + r = sd_dhcp_lease_get_static_routes(lease, &routes); if (r > 0) - serialize_dhcp_routes(f, "ROUTES", routes, r); + serialize_dhcp_routes(f, "STATIC_ROUTES", routes, r); + + routes = mfree(routes); + r = sd_dhcp_lease_get_classless_routes(lease, &routes); + if (r > 0) + serialize_dhcp_routes(f, "CLASSLESS_ROUTES", routes, r); r = sd_dhcp_lease_get_timezone(lease, &string); if (r >= 0) @@ -1149,7 +1153,8 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { *smtp = NULL, *lpr = NULL, *mtu = NULL, - *routes = NULL, + *static_routes = NULL, + *classless_routes = NULL, *domains = NULL, *client_id_hex = NULL, *vendor_specific_hex = NULL, @@ -1189,7 +1194,8 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "HOSTNAME", &lease->hostname, "DOMAIN_SEARCH_LIST", &domains, "ROOT_PATH", &lease->root_path, - "ROUTES", &routes, + "STATIC_ROUTES", &static_routes, + "CLASSLESS_ROUTES", &classless_routes, "CLIENTID", &client_id_hex, "TIMEZONE", &lease->timezone, "VENDOR_SPECIFIC", &vendor_specific_hex, @@ -1336,13 +1342,22 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { lease->search_domains = TAKE_PTR(a); } - if (routes) { + if (static_routes) { r = deserialize_dhcp_routes( - &lease->static_route, - &lease->static_route_size, - routes); + &lease->static_routes, + &lease->n_static_routes, + static_routes); if (r < 0) - log_debug_errno(r, "Failed to parse DHCP routes %s, ignoring: %m", routes); + log_debug_errno(r, "Failed to parse DHCP static routes %s, ignoring: %m", static_routes); + } + + if (classless_routes) { + r = deserialize_dhcp_routes( + &lease->classless_routes, + &lease->n_classless_routes, + classless_routes); + if (r < 0) + log_debug_errno(r, "Failed to parse DHCP classless routes %s, ignoring: %m", classless_routes); } if (lifetime) { @@ -1489,9 +1504,3 @@ int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway) { *gateway = route->gw_addr; return 0; } - -int sd_dhcp_route_get_option(sd_dhcp_route *route) { - assert_return(route, -EINVAL); - - return route->option; -} diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 9088c09fc..64f5286ea 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -12,14 +12,17 @@ #include "alloc-util.h" #include "dhcp-internal.h" #include "dhcp-server-internal.h" +#include "dns-domain.h" #include "fd-util.h" #include "in-addr-util.h" #include "io-util.h" +#include "memory-util.h" #include "network-common.h" #include "ordered-set.h" #include "siphash24.h" #include "string-util.h" #include "unaligned.h" +#include "utf8.h" #define DHCP_DEFAULT_LEASE_TIME_USEC USEC_PER_HOUR #define DHCP_MAX_LEASE_TIME_USEC (USEC_PER_HOUR*12) @@ -124,7 +127,7 @@ int sd_dhcp_server_is_in_relay_mode(sd_dhcp_server *server) { void client_id_hash_func(const DHCPClientId *id, struct siphash *state) { assert(id); - assert(id->length); + assert(id->length > 0); assert(id->data); siphash24_compress(&id->length, sizeof(id->length), state); @@ -134,8 +137,10 @@ void client_id_hash_func(const DHCPClientId *id, struct siphash *state) { int client_id_compare_func(const DHCPClientId *a, const DHCPClientId *b) { int r; - assert(!a->length || a->data); - assert(!b->length || b->data); + assert(a->length > 0); + assert(a->data); + assert(b->length > 0); + assert(b->data); r = CMP(a->length, b->length); if (r != 0) @@ -155,12 +160,12 @@ DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR( static sd_dhcp_server *dhcp_server_free(sd_dhcp_server *server) { assert(server); - log_dhcp_server(server, "UNREF"); - sd_dhcp_server_stop(server); sd_event_unref(server->event); + free(server->boot_server_name); + free(server->boot_filename); free(server->timezone); for (sd_dhcp_lease_server_type_t i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++) @@ -269,10 +274,50 @@ sd_event *sd_dhcp_server_get_event(sd_dhcp_server *server) { return server->event; } +int sd_dhcp_server_set_boot_server_address(sd_dhcp_server *server, const struct in_addr *address) { + assert_return(server, -EINVAL); + + if (address) + server->boot_server_address = *address; + else + server->boot_server_address = (struct in_addr) {}; + + return 0; +} + +int sd_dhcp_server_set_boot_server_name(sd_dhcp_server *server, const char *name) { + int r; + + assert_return(server, -EINVAL); + + if (name) { + r = dns_name_is_valid(name); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; + } + + return free_and_strdup(&server->boot_server_name, name); +} + +int sd_dhcp_server_set_boot_filename(sd_dhcp_server *server, const char *filename) { + assert_return(server, -EINVAL); + + if (filename && (!string_is_safe(filename) || !ascii_is_valid(filename))) + return -EINVAL; + + return free_and_strdup(&server->boot_filename, filename); +} + int sd_dhcp_server_stop(sd_dhcp_server *server) { + bool running; + if (!server) return 0; + running = sd_dhcp_server_is_running(server); + server->receive_message = sd_event_source_disable_unref(server->receive_message); server->receive_broadcast = sd_event_source_disable_unref(server->receive_broadcast); @@ -280,27 +325,35 @@ int sd_dhcp_server_stop(sd_dhcp_server *server) { server->fd = safe_close(server->fd); server->fd_broadcast = safe_close(server->fd_broadcast); - log_dhcp_server(server, "STOPPED"); + if (running) + log_dhcp_server(server, "STOPPED"); return 0; } -static int dhcp_server_send_unicast_raw(sd_dhcp_server *server, - DHCPPacket *packet, size_t len) { +static int dhcp_server_send_unicast_raw( + sd_dhcp_server *server, + uint8_t hlen, + const uint8_t *chaddr, + DHCPPacket *packet, + size_t len) { + union sockaddr_union link = { .ll.sll_family = AF_PACKET, .ll.sll_protocol = htobe16(ETH_P_IP), .ll.sll_ifindex = server->ifindex, - .ll.sll_halen = ETH_ALEN, + .ll.sll_halen = hlen, }; assert(server); assert(server->ifindex > 0); - assert(server->address); + assert(server->address != 0); + assert(hlen > 0); + assert(chaddr); assert(packet); assert(len > sizeof(DHCPPacket)); - memcpy(&link.ll.sll_addr, &packet->dhcp.chaddr, ETH_ALEN); + memcpy(link.ll.sll_addr, chaddr, hlen); if (len > UINT16_MAX) return -EOVERFLOW; @@ -372,8 +425,16 @@ static bool requested_broadcast(DHCPMessage *message) { return message->flags & htobe16(0x8000); } -static int dhcp_server_send(sd_dhcp_server *server, be32_t destination, uint16_t destination_port, - DHCPPacket *packet, size_t optoffset, bool l2_broadcast) { +static int dhcp_server_send( + sd_dhcp_server *server, + uint8_t hlen, + const uint8_t *chaddr, + be32_t destination, + uint16_t destination_port, + DHCPPacket *packet, + size_t optoffset, + bool l2_broadcast) { + if (destination != INADDR_ANY) return dhcp_server_send_udp(server, destination, destination_port, &packet->dhcp, @@ -386,7 +447,7 @@ static int dhcp_server_send(sd_dhcp_server *server, be32_t destination, uint16_t /* we cannot send UDP packet to specific MAC address when the address is not yet configured, so must fall back to raw packets */ - return dhcp_server_send_unicast_raw(server, packet, + return dhcp_server_send_unicast_raw(server, hlen, chaddr, packet, sizeof(DHCPPacket) + optoffset); } @@ -399,7 +460,7 @@ int dhcp_server_send_packet(sd_dhcp_server *server, assert(server); assert(req); - assert(req->max_optlen); + assert(req->max_optlen > 0); assert(req->message); assert(optoffset <= req->max_optlen); assert(packet); @@ -447,45 +508,51 @@ int dhcp_server_send_packet(sd_dhcp_server *server, client, because the client may not have a correct network address or subnet mask, and the client may not be answering ARP requests. */ - if (req->message->giaddr) { + if (req->message->giaddr != 0) { destination = req->message->giaddr; destination_port = DHCP_PORT_SERVER; if (type == DHCP_NAK) packet->dhcp.flags = htobe16(0x8000); - } else if (req->message->ciaddr && type != DHCP_NAK) + } else if (req->message->ciaddr != 0 && type != DHCP_NAK) destination = req->message->ciaddr; bool l2_broadcast = requested_broadcast(req->message) || type == DHCP_NAK; - return dhcp_server_send(server, destination, destination_port, packet, optoffset, l2_broadcast); + return dhcp_server_send(server, req->message->hlen, req->message->chaddr, + destination, destination_port, packet, optoffset, l2_broadcast); } -static int server_message_init(sd_dhcp_server *server, DHCPPacket **ret, - uint8_t type, size_t *_optoffset, - DHCPRequest *req) { +static int server_message_init( + sd_dhcp_server *server, + DHCPPacket **ret, + uint8_t type, + size_t *ret_optoffset, + DHCPRequest *req) { + _cleanup_free_ DHCPPacket *packet = NULL; size_t optoffset = 0; int r; assert(server); assert(ret); - assert(_optoffset); + assert(ret_optoffset); assert(IN_SET(type, DHCP_OFFER, DHCP_ACK, DHCP_NAK)); + assert(req); packet = malloc0(sizeof(DHCPPacket) + req->max_optlen); if (!packet) return -ENOMEM; r = dhcp_message_init(&packet->dhcp, BOOTREPLY, - be32toh(req->message->xid), type, ARPHRD_ETHER, + be32toh(req->message->xid), type, + req->message->htype, req->message->hlen, req->message->chaddr, req->max_optlen, &optoffset); if (r < 0) return r; packet->dhcp.flags = req->message->flags; packet->dhcp.giaddr = req->message->giaddr; - memcpy(&packet->dhcp.chaddr, &req->message->chaddr, ETH_ALEN); - *_optoffset = optoffset; + *ret_optoffset = optoffset; *ret = TAKE_PTR(packet); return 0; @@ -512,6 +579,7 @@ static int server_send_offer_or_ack( return r; packet->dhcp.yiaddr = address; + packet->dhcp.siaddr = server->boot_server_address.s_addr; lease_time = htobe32(req->lifetime); r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, @@ -535,6 +603,22 @@ static int server_send_offer_or_ack( return r; } + if (server->boot_server_name) { + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, + SD_DHCP_OPTION_BOOT_SERVER_NAME, + strlen(server->boot_server_name), server->boot_server_name); + if (r < 0) + return r; + } + + if (server->boot_filename) { + r = dhcp_option_append(&packet->dhcp, req->max_optlen, &offset, 0, + SD_DHCP_OPTION_BOOT_FILENAME, + strlen(server->boot_filename), server->boot_filename); + if (r < 0) + return r; + } + if (type == DHCP_ACK) { static const uint8_t option_map[_SD_DHCP_LEASE_SERVER_TYPE_MAX] = { [SD_DHCP_LEASE_DNS] = SD_DHCP_OPTION_DOMAIN_NAME_SERVER, @@ -562,7 +646,7 @@ static int server_send_offer_or_ack( if (server->timezone) { r = dhcp_option_append( &packet->dhcp, req->max_optlen, &offset, 0, - SD_DHCP_OPTION_NEW_TZDB_TIMEZONE, + SD_DHCP_OPTION_TZDB_TIMEZONE, strlen(server->timezone), server->timezone); if (r < 0) return r; @@ -585,11 +669,7 @@ static int server_send_offer_or_ack( return r; } - r = dhcp_server_send_packet(server, req, packet, type, offset); - if (r < 0) - return r; - - return 0; + return dhcp_server_send_packet(server, req, packet, type, offset); } static int server_send_nak_or_ignore(sd_dhcp_server *server, bool init_reboot, DHCPRequest *req) { @@ -616,8 +696,14 @@ static int server_send_nak_or_ignore(sd_dhcp_server *server, bool init_reboot, D return DHCP_NAK; } -static int server_send_forcerenew(sd_dhcp_server *server, be32_t address, - be32_t gateway, const uint8_t chaddr[]) { +static int server_send_forcerenew( + sd_dhcp_server *server, + be32_t address, + be32_t gateway, + uint8_t htype, + uint8_t hlen, + const uint8_t *chaddr) { + _cleanup_free_ DHCPPacket *packet = NULL; size_t optoffset = 0; int r; @@ -631,7 +717,7 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address, return -ENOMEM; r = dhcp_message_init(&packet->dhcp, BOOTREPLY, 0, - DHCP_FORCERENEW, ARPHRD_ETHER, + DHCP_FORCERENEW, htype, hlen, chaddr, DHCP_MIN_OPTIONS_SIZE, &optoffset); if (r < 0) return r; @@ -641,15 +727,9 @@ static int server_send_forcerenew(sd_dhcp_server *server, be32_t address, if (r < 0) return r; - memcpy(&packet->dhcp.chaddr, chaddr, ETH_ALEN); - - r = dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT, - &packet->dhcp, - sizeof(DHCPMessage) + optoffset); - if (r < 0) - return r; - - return 0; + return dhcp_server_send_udp(server, address, DHCP_PORT_CLIENT, + &packet->dhcp, + sizeof(DHCPMessage) + optoffset); } static int parse_request(uint8_t code, uint8_t len, const void *option, void *userdata) { @@ -681,8 +761,7 @@ static int parse_request(uint8_t code, uint8_t len, const void *option, void *us if (!data) return -ENOMEM; - free(req->client_id.data); - req->client_id.data = data; + free_and_replace(req->client_id.data, data); req->client_id.length = len; } @@ -718,22 +797,45 @@ static int ensure_sane_request(sd_dhcp_server *server, DHCPRequest *req, DHCPMes req->message = message; - /* set client id based on MAC address if client did not send an explicit - one */ - if (!req->client_id.data) { - void *data; + if (message->hlen > sizeof(message->chaddr)) + return -EBADMSG; - data = malloc0(ETH_ALEN + 1); + /* set client id based on MAC address if client did not send an explicit one */ + if (!req->client_id.data) { + uint8_t *data; + + if (message->hlen == 0) + return -EBADMSG; + + data = new0(uint8_t, message->hlen + 1); if (!data) return -ENOMEM; - ((uint8_t*) data)[0] = 0x01; - memcpy((uint8_t*) data + 1, &message->chaddr, ETH_ALEN); + data[0] = 0x01; + memcpy(data + 1, message->chaddr, message->hlen); - req->client_id.length = ETH_ALEN + 1; + req->client_id.length = message->hlen + 1; req->client_id.data = data; } + if (message->hlen == 0 || memeqzero(message->chaddr, message->hlen)) { + /* See RFC2131 section 4.1.1. + * hlen and chaddr may not be set for non-ethernet interface. + * Let's try to retrieve it from the client ID. */ + + if (!req->client_id.data) + return -EBADMSG; + + if (req->client_id.length <= 1 || req->client_id.length > sizeof(message->chaddr) + 1) + return -EBADMSG; + + if (req->client_id.data[0] != 0x01) + return -EBADMSG; + + message->hlen = req->client_id.length - 1; + memcpy(message->chaddr, req->client_id.data + 1, message->hlen); + } + if (req->max_optlen < DHCP_MIN_OPTIONS_SIZE) req->max_optlen = DHCP_MIN_OPTIONS_SIZE; @@ -794,6 +896,10 @@ static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *messag assert(message); assert(sd_dhcp_server_is_in_relay_mode(server)); + if (message->hlen == 0 || message->hlen > sizeof(message->chaddr) || memeqzero(message->chaddr, message->hlen)) + return log_dhcp_server_errno(server, SYNTHETIC_ERRNO(EBADMSG), + "(relay agent) received message without/invalid hardware address, discarding."); + if (message->op == BOOTREQUEST) { log_dhcp_server(server, "(relay agent) BOOTREQUEST (0x%x)", be32toh(message->xid)); if (message->hops >= 16) @@ -833,7 +939,7 @@ static int dhcp_server_relay_message(sd_dhcp_server *server, DHCPMessage *messag bool l2_broadcast = requested_broadcast(message) || message_type == DHCP_NAK; const be32_t destination = message_type == DHCP_NAK ? INADDR_ANY : message->ciaddr; - return dhcp_server_send(server, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast); + return dhcp_server_send(server, message->hlen, message->chaddr, destination, DHCP_PORT_CLIENT, packet, opt_length, l2_broadcast); } return -EBADMSG; } @@ -842,7 +948,9 @@ static int prepare_new_lease( DHCPLease **ret_lease, be32_t address, const DHCPClientId *client_id, - const uint8_t chaddr[static ETH_ALEN], + uint8_t htype, + uint8_t hlen, + const uint8_t *chaddr, be32_t gateway, usec_t expiration) { @@ -855,6 +963,8 @@ static int prepare_new_lease( *lease = (DHCPLease) { .address = address, .client_id.length = client_id->length, + .htype = htype, + .hlen = hlen, .gateway = gateway, .expiration = expiration, }; @@ -862,13 +972,66 @@ static int prepare_new_lease( if (!lease->client_id.data) return -ENOMEM; - memcpy(&lease->chaddr, chaddr, ETH_ALEN); + memcpy(lease->chaddr, chaddr, hlen); *ret_lease = TAKE_PTR(lease); return 0; } +static int server_ack_request(sd_dhcp_server *server, DHCPRequest *req, DHCPLease *existing_lease, be32_t address) { + usec_t time_now, expiration; + int r; + + assert(server); + assert(req); + assert(address != 0); + + r = sd_event_now(server->event, CLOCK_BOOTTIME, &time_now); + if (r < 0) + return r; + + expiration = usec_add(req->lifetime * USEC_PER_SEC, time_now); + + if (existing_lease) { + assert(existing_lease->server); + assert(existing_lease->address == address); + existing_lease->expiration = expiration; + + } else { + _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL; + + r = prepare_new_lease(&lease, address, &req->client_id, + req->message->htype, req->message->hlen, + req->message->chaddr, req->message->giaddr, expiration); + if (r < 0) + return log_dhcp_server_errno(server, r, "Failed to create new lease: %m"); + + lease->server = server; /* This must be set just before hashmap_put(). */ + + r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease); + if (r < 0) + return log_dhcp_server_errno(server, r, "Could not save lease: %m"); + + r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease); + if (r < 0) + return log_dhcp_server_errno(server, r, "Could not save lease: %m"); + + TAKE_PTR(lease); + } + + r = server_send_offer_or_ack(server, req, address, DHCP_ACK); + if (r < 0) + return log_dhcp_server_errno(server, r, "Could not send ACK: %m"); + + log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid)); + + if (server->callback) + server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata); + + return DHCP_ACK; +} + static int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) { DHCPLease *lease; usec_t time_now; @@ -876,16 +1039,15 @@ static int dhcp_server_cleanup_expired_leases(sd_dhcp_server *server) { assert(server); - r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(server->event, CLOCK_BOOTTIME, &time_now); if (r < 0) return r; - HASHMAP_FOREACH(lease, server->bound_leases_by_client_id) { + HASHMAP_FOREACH(lease, server->bound_leases_by_client_id) if (lease->expiration < time_now) { log_dhcp_server(server, "CLEAN (0x%x)", be32toh(lease->address)); dhcp_lease_free(lease); } - } return 0; } @@ -912,9 +1074,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz assert(server); assert(message); - if (message->op != BOOTREQUEST || - message->htype != ARPHRD_ETHER || - message->hlen != ETHER_ADDR_LEN) + if (message->op != BOOTREQUEST) return 0; req = new0(DHCPRequest, 1); @@ -927,7 +1087,6 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz r = ensure_sane_request(server, req, message); if (r < 0) - /* this only fails on critical errors */ return r; r = dhcp_server_cleanup_expired_leases(server); @@ -944,7 +1103,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz log_dhcp_server(server, "DISCOVER (0x%x)", be32toh(req->message->xid)); - if (!server->pool_size) + if (server->pool_size == 0) /* no pool allocated */ return 0; @@ -1001,7 +1160,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz /* see RFC 2131, section 4.3.2 */ - if (req->server_id) { + if (req->server_id != 0) { log_dhcp_server(server, "REQUEST (selecting) (0x%x)", be32toh(req->message->xid)); @@ -1010,22 +1169,22 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz /* client did not pick us */ return 0; - if (req->message->ciaddr) + if (req->message->ciaddr != 0) /* this MUST be zero */ return 0; - if (!req->requested_ip) + if (req->requested_ip == 0) /* this must be filled in with the yiaddr from the chosen OFFER */ return 0; address = req->requested_ip; - } else if (req->requested_ip) { + } else if (req->requested_ip != 0) { log_dhcp_server(server, "REQUEST (init-reboot) (0x%x)", be32toh(req->message->xid)); /* INIT-REBOOT */ - if (req->message->ciaddr) + if (req->message->ciaddr != 0) /* this MUST be zero */ return 0; @@ -1037,7 +1196,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz be32toh(req->message->xid)); /* REBINDING / RENEWING */ - if (!req->message->ciaddr) + if (req->message->ciaddr == 0) /* this MUST be filled in with clients IP address */ return 0; @@ -1055,45 +1214,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz /* The client requested an address which is different from the static lease. Refuse. */ return server_send_nak_or_ignore(server, init_reboot, req); - _cleanup_(dhcp_lease_freep) DHCPLease *lease = NULL; - usec_t time_now, expiration; - - r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - - expiration = usec_add(req->lifetime * USEC_PER_SEC, time_now); - - r = prepare_new_lease(&lease, static_lease->address, &req->client_id, - req->message->chaddr, req->message->giaddr, expiration); - if (r < 0) - return r; - - r = server_send_offer_or_ack(server, req, address, DHCP_ACK); - if (r < 0) - /* this only fails on critical errors */ - return log_dhcp_server_errno(server, r, "Could not send ack: %m"); - - log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid)); - - dhcp_lease_free(hashmap_get(server->bound_leases_by_client_id, &lease->client_id)); - - lease->server = server; /* This must be set just before hashmap_put(). */ - - r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease); - if (r < 0) - return log_dhcp_server_errno(server, r, "Could not save lease: %m"); - - r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease); - if (r < 0) - return log_dhcp_server_errno(server, r, "Could not save lease: %m"); - - TAKE_PTR(lease); - - if (server->callback) - server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata); - - return DHCP_ACK; + return server_ack_request(server, req, existing_lease, address); } if (address_is_in_pool(server, address)) { @@ -1103,50 +1224,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, siz /* We previously assigned an address, but the client requested another one. Refuse. */ return server_send_nak_or_ignore(server, init_reboot, req); - _cleanup_(dhcp_lease_freep) DHCPLease *new_lease = NULL; - usec_t time_now, expiration; - DHCPLease *lease; - - r = sd_event_now(server->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - - expiration = usec_add(req->lifetime * USEC_PER_SEC, time_now); - - if (!existing_lease) { - r = prepare_new_lease(&new_lease, address, &req->client_id, - req->message->chaddr, req->message->giaddr, expiration); - if (r < 0) - return r; - - lease = new_lease; - } else { - existing_lease->expiration = expiration; - lease = existing_lease; - } - - r = server_send_offer_or_ack(server, req, address, DHCP_ACK); - if (r < 0) - /* this only fails on critical errors */ - return log_dhcp_server_errno(server, r, "Could not send ack: %m"); - - log_dhcp_server(server, "ACK (0x%x)", be32toh(req->message->xid)); - - lease->server = server; /* This must be set just before hashmap_put(). */ - - r = hashmap_ensure_put(&server->bound_leases_by_client_id, &dhcp_lease_hash_ops, &lease->client_id, lease); - if (r < 0) - return log_dhcp_server_errno(server, r, "Could not save lease: %m"); - r = hashmap_ensure_put(&server->bound_leases_by_address, NULL, UINT32_TO_PTR(lease->address), lease); - if (r < 0) - return log_dhcp_server_errno(server, r, "Could not save lease: %m"); - - TAKE_PTR(new_lease); - - if (server->callback) - server->callback(server, SD_DHCP_SERVER_EVENT_LEASE_CHANGED, server->callback_userdata); - - return DHCP_ACK; + return server_ack_request(server, req, existing_lease, address); } return server_send_nak_or_ignore(server, init_reboot, req); @@ -1232,7 +1310,7 @@ static int server_receive_message(sd_event_source *s, int fd, if ((size_t) len < sizeof(DHCPMessage)) return 0; - CMSG_FOREACH(cmsg, &msg) { + CMSG_FOREACH(cmsg, &msg) if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO && cmsg->cmsg_len == CMSG_LEN(sizeof(struct in_pktinfo))) { @@ -1245,7 +1323,6 @@ static int server_receive_message(sd_event_source *s, int fd, break; } - } if (sd_dhcp_server_is_in_relay_mode(server)) { r = dhcp_server_relay_message(server, message, len - sizeof(DHCPMessage), buflen); @@ -1259,6 +1336,18 @@ static int server_receive_message(sd_event_source *s, int fd, return 0; } +static void dhcp_server_update_lease_servers(sd_dhcp_server *server) { + assert(server); + assert(server->address != 0); + + /* Convert null address -> server address */ + + for (sd_dhcp_lease_server_type_t k = 0; k < _SD_DHCP_LEASE_SERVER_TYPE_MAX; k++) + for (size_t i = 0; i < server->servers[k].size; i++) + if (in4_addr_is_null(&server->servers[k].addr[i])) + server->servers[k].addr[i].s_addr = server->address; +} + int sd_dhcp_server_start(sd_dhcp_server *server) { int r; @@ -1273,6 +1362,8 @@ int sd_dhcp_server_start(sd_dhcp_server *server) { assert_return(server->fd < 0, -EBUSY); assert_return(server->address != htobe32(INADDR_ANY), -EUNATCH); + dhcp_server_update_lease_servers(server); + r = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); if (r < 0) { r = -errno; @@ -1323,8 +1414,8 @@ int sd_dhcp_server_start(sd_dhcp_server *server) { return 0; on_error: - sd_dhcp_server_stop(server); - return r; + sd_dhcp_server_stop(server); + return r; } int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { @@ -1336,7 +1427,8 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { log_dhcp_server(server, "FORCERENEW"); HASHMAP_FOREACH(lease, server->bound_leases_by_client_id) { - k = server_send_forcerenew(server, lease->address, lease->gateway, lease->chaddr); + k = server_send_forcerenew(server, lease->address, lease->gateway, + lease->htype, lease->hlen, lease->chaddr); if (k < 0) r = k; } @@ -1401,6 +1493,7 @@ int sd_dhcp_server_set_servers( struct in_addr *c = NULL; assert_return(server, -EINVAL); + assert_return(!sd_dhcp_server_is_running(server), -EBUSY); assert_return(addresses || n_addresses == 0, -EINVAL); assert_return(what >= 0, -EINVAL); assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL); @@ -1415,8 +1508,7 @@ int sd_dhcp_server_set_servers( return -ENOMEM; } - free(server->servers[what].addr); - server->servers[what].addr = c; + free_and_replace(server->servers[what].addr, c); server->servers[what].size = n_addresses; return 1; } @@ -1542,22 +1634,17 @@ int sd_dhcp_server_set_static_lease( assert_return(server, -EINVAL); assert_return(client_id, -EINVAL); - assert_return(client_id_size == ETH_ALEN + 1, -EINVAL); + assert_return(client_id_size > 0, -EINVAL); assert_return(!sd_dhcp_server_is_running(server), -EBUSY); /* Static lease with an empty or omitted address is a valid entry, - * the server removes any static lease with the specified mac address. */ + * the server removes any static lease with the specified mac address. */ if (!address || address->s_addr == 0) { - _cleanup_free_ void *data = NULL; DHCPClientId c; - data = memdup(client_id, client_id_size); - if (!data) - return -ENOMEM; - c = (DHCPClientId) { .length = client_id_size, - .data = data, + .data = client_id, }; dhcp_lease_free(hashmap_get(server->static_leases_by_client_id, &c)); @@ -1571,8 +1658,6 @@ int sd_dhcp_server_set_static_lease( *lease = (DHCPLease) { .address = address->s_addr, .client_id.length = client_id_size, - .gateway = 0, - .expiration = 0, }; lease->client_id.data = memdup(client_id, client_id_size); if (!lease->client_id.data) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 84bc739bb..27864a0a4 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -14,7 +14,6 @@ #include "dhcp-identifier.h" #include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" -#include "dhcp6-protocol.h" #include "dns-domain.h" #include "event-util.h" #include "fd-util.h" @@ -22,74 +21,11 @@ #include "hostname-util.h" #include "in-addr-util.h" #include "io-util.h" -#include "network-common.h" #include "random-util.h" #include "socket-util.h" -#include "string-table.h" #include "strv.h" -#include "util.h" #include "web-util.h" -#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN - -#define IRT_DEFAULT (1 * USEC_PER_DAY) -#define IRT_MINIMUM (600 * USEC_PER_SEC) - -/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */ -typedef enum DHCP6RequestIA { - DHCP6_REQUEST_IA_NA = 1 << 0, - DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */ - DHCP6_REQUEST_IA_PD = 1 << 2, -} DHCP6RequestIA; - -struct sd_dhcp6_client { - unsigned n_ref; - - DHCP6State state; - sd_event *event; - int event_priority; - int ifindex; - char *ifname; - DHCP6Address hint_pd_prefix; - struct in6_addr local_address; - uint8_t mac_addr[MAX_MAC_ADDR_LEN]; - size_t mac_addr_len; - uint16_t arp_type; - DHCP6IA ia_na; - DHCP6IA ia_pd; - sd_event_source *timeout_t1; - sd_event_source *timeout_t2; - DHCP6RequestIA request_ia; - be32_t transaction_id; - usec_t transaction_start; - struct sd_dhcp6_lease *lease; - int fd; - bool information_request; - bool iaid_set; - be16_t *req_opts; - size_t req_opts_len; - char *fqdn; - char *mudurl; - char **user_class; - char **vendor_class; - sd_event_source *receive_message; - usec_t retransmit_time; - uint8_t retransmit_count; - sd_event_source *timeout_resend; - sd_event_source *timeout_resend_expire; - sd_dhcp6_client_callback_t callback; - void *userdata; - struct duid duid; - size_t duid_len; - usec_t information_request_time_usec; - usec_t information_refresh_time_usec; - OrderedHashmap *extra_options; - OrderedHashmap *vendor_options; - - /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */ - bool test_mode; -}; - static const uint16_t default_req_opts[] = { SD_DHCP6_OPTION_DNS_SERVERS, SD_DHCP6_OPTION_DOMAIN_LIST, @@ -97,78 +33,10 @@ static const uint16_t default_req_opts[] = { SD_DHCP6_OPTION_SNTP_SERVERS, }; -const char * dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = { - [DHCP6_MESSAGE_SOLICIT] = "Solicit", - [DHCP6_MESSAGE_ADVERTISE] = "Advertise", - [DHCP6_MESSAGE_REQUEST] = "Request", - [DHCP6_MESSAGE_CONFIRM] = "Confirm", - [DHCP6_MESSAGE_RENEW] = "Renew", - [DHCP6_MESSAGE_REBIND] = "Rebind", - [DHCP6_MESSAGE_REPLY] = "Reply", - [DHCP6_MESSAGE_RELEASE] = "Release", - [DHCP6_MESSAGE_DECLINE] = "Decline", - [DHCP6_MESSAGE_RECONFIGURE] = "Reconfigure", - [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request", - [DHCP6_MESSAGE_RELAY_FORWARD] = "Relay Forward", - [DHCP6_MESSAGE_RELAY_REPLY] = "Relay Reply", - [DHCP6_MESSAGE_LEASE_QUERY] = "Lease Query", - [DHCP6_MESSAGE_LEASE_QUERY_REPLY] = "Lease Query Reply", - [DHCP6_MESSAGE_LEASE_QUERY_DONE] = "Lease Query Done", - [DHCP6_MESSAGE_LEASE_QUERY_DATA] = "Lease Query Data", - [DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request", - [DHCP6_MESSAGE_RECONFIGURE_REPLY] = "Reconfigure Reply", - [DHCP6_MESSAGE_DHCPV4_QUERY] = "DHCPv4 Query", - [DHCP6_MESSAGE_DHCPV4_RESPONSE] = "DHCPv4 Response", - [DHCP6_MESSAGE_ACTIVE_LEASE_QUERY] = "Active Lease Query", - [DHCP6_MESSAGE_START_TLS] = "Start TLS", - [DHCP6_MESSAGE_BINDING_UPDATE] = "Binding Update", - [DHCP6_MESSAGE_BINDING_REPLY] = "Binding Reply", - [DHCP6_MESSAGE_POOL_REQUEST] = "Pool Request", - [DHCP6_MESSAGE_POOL_RESPONSE] = "Pool Response", - [DHCP6_MESSAGE_UPDATE_REQUEST] = "Update Request", - [DHCP6_MESSAGE_UPDATE_REQUEST_ALL] = "Update Request All", - [DHCP6_MESSAGE_UPDATE_DONE] = "Update Done", - [DHCP6_MESSAGE_CONNECT] = "Connect", - [DHCP6_MESSAGE_CONNECT_REPLY] = "Connect Reply", - [DHCP6_MESSAGE_DISCONNECT] = "Disconnect", - [DHCP6_MESSAGE_STATE] = "State", - [DHCP6_MESSAGE_CONTACT] = "Contact", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int); - -const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = { - [DHCP6_STATUS_SUCCESS] = "Success", - [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure", - [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available", - [DHCP6_STATUS_NO_BINDING] = "Binding unavailable", - [DHCP6_STATUS_NOT_ON_LINK] = "Not on link", - [DHCP6_STATUS_USE_MULTICAST] = "Use multicast", - [DHCP6_STATUS_NO_PREFIX_AVAIL] = "No prefix available", - [DHCP6_STATUS_UNKNOWN_QUERY_TYPE] = "Unknown query type", - [DHCP6_STATUS_MALFORMED_QUERY] = "Malformed query", - [DHCP6_STATUS_NOT_CONFIGURED] = "Not configured", - [DHCP6_STATUS_NOT_ALLOWED] = "Not allowed", - [DHCP6_STATUS_QUERY_TERMINATED] = "Query terminated", - [DHCP6_STATUS_DATA_MISSING] = "Data missing", - [DHCP6_STATUS_CATCHUP_COMPLETE] = "Catch up complete", - [DHCP6_STATUS_NOT_SUPPORTED] = "Not supported", - [DHCP6_STATUS_TLS_CONNECTION_REFUSED] = "TLS connection refused", - [DHCP6_STATUS_ADDRESS_IN_USE] = "Address in use", - [DHCP6_STATUS_CONFIGURATION_CONFLICT] = "Configuration conflict", - [DHCP6_STATUS_MISSING_BINDING_INFORMATION] = "Missing binding information", - [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information", - [DHCP6_STATUS_SERVER_SHUTTING_DOWN] = "Server shutting down", - [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED] = "DNS update not supported", - [DHCP6_STATUS_EXCESSIVE_TIME_SKEW] = "Excessive time skew", -}; - -DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int); - #define DHCP6_CLIENT_DONT_DESTROY(client) \ _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client) -static int client_start(sd_dhcp6_client *client, DHCP6State state); +static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state); int sd_dhcp6_client_set_callback( sd_dhcp6_client *client, @@ -185,8 +53,8 @@ int sd_dhcp6_client_set_callback( int sd_dhcp6_client_set_ifindex(sd_dhcp6_client *client, int ifindex) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(ifindex > 0, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->ifindex = ifindex; return 0; @@ -222,9 +90,9 @@ int sd_dhcp6_client_set_local_address( const struct in6_addr *local_address) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(local_address, -EINVAL); assert_return(in6_addr_is_link_local(local_address) > 0, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); client->local_address = *local_address; @@ -233,13 +101,16 @@ int sd_dhcp6_client_set_local_address( int sd_dhcp6_client_set_mac( sd_dhcp6_client *client, - const uint8_t *addr, size_t addr_len, + const uint8_t *addr, + size_t addr_len, uint16_t arp_type) { assert_return(client, -EINVAL); assert_return(addr, -EINVAL); - assert_return(addr_len <= MAX_MAC_ADDR_LEN, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(addr_len <= sizeof(client->hw_addr.bytes), -EINVAL); + + /* Unlike the other setters, it is OK to set a new MAC address while the client is running, + * as the MAC address is used only when setting DUID or IAID. */ if (arp_type == ARPHRD_ETHER) assert_return(addr_len == ETH_ALEN, -EINVAL); @@ -247,12 +118,12 @@ int sd_dhcp6_client_set_mac( assert_return(addr_len == INFINIBAND_ALEN, -EINVAL); else { client->arp_type = ARPHRD_NONE; - client->mac_addr_len = 0; + client->hw_addr.length = 0; return 0; } - memcpy(&client->mac_addr, addr, addr_len); - client->mac_addr_len = addr_len; + memcpy(client->hw_addr.bytes, addr, addr_len); + client->hw_addr.length = addr_len; client->arp_type = arp_type; return 0; @@ -261,25 +132,47 @@ int sd_dhcp6_client_set_mac( int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen, - const struct in6_addr *pd_address) { + const struct in6_addr *pd_prefix) { + + _cleanup_free_ DHCP6Address *prefix = NULL; assert_return(client, -EINVAL); - assert_return(pd_address, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - client->hint_pd_prefix.iapdprefix.address = *pd_address; - client->hint_pd_prefix.iapdprefix.prefixlen = prefixlen; + if (!pd_prefix) { + /* clear previous assignments. */ + dhcp6_ia_clear_addresses(&client->ia_pd); + return 0; + } - return 0; + assert_return(prefixlen > 0 && prefixlen <= 128, -EINVAL); + + prefix = new(DHCP6Address, 1); + if (!prefix) + return -ENOMEM; + + *prefix = (DHCP6Address) { + .iapdprefix.address = *pd_prefix, + .iapdprefix.prefixlen = prefixlen, + }; + + LIST_PREPEND(addresses, client->ia_pd.addresses, TAKE_PTR(prefix)); + return 1; } int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { int r; assert_return(client, -EINVAL); - assert_return(v, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - r = ordered_hashmap_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v, v); + if (!v) { + /* Clear the previous assignments. */ + ordered_set_clear(client->vendor_options); + return 0; + } + + r = ordered_set_ensure_put(&client->vendor_options, &dhcp6_option_hash_ops, v); if (r < 0) return r; @@ -289,10 +182,12 @@ int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option * } static int client_ensure_duid(sd_dhcp6_client *client) { + assert(client); + if (client->duid_len != 0) return 0; - return dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); + return dhcp_identifier_set_duid_en(client->test_mode, &client->duid, &client->duid_len); } /** @@ -302,15 +197,15 @@ static int client_ensure_duid(sd_dhcp6_client *client) { */ static int dhcp6_client_set_duid_internal( sd_dhcp6_client *client, - uint16_t duid_type, + DUIDType duid_type, const void *duid, size_t duid_len, usec_t llt_time) { int r; assert_return(client, -EINVAL); - assert_return(duid_len == 0 || duid != NULL, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + assert_return(duid_len == 0 || duid, -EINVAL); if (duid) { r = dhcp_validate_duid_len(duid_type, duid_len, true); @@ -325,37 +220,19 @@ static int dhcp6_client_set_duid_internal( client->duid.type = htobe16(duid_type); memcpy(&client->duid.raw.data, duid, duid_len); client->duid_len = sizeof(client->duid.type) + duid_len; - } else - switch (duid_type) { - case DUID_TYPE_LLT: - if (client->mac_addr_len == 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LLT, MAC address is not set."); - r = dhcp_identifier_set_duid_llt(&client->duid, llt_time, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-LLT: %m"); - break; - case DUID_TYPE_EN: - r = dhcp_identifier_set_duid_en(&client->duid, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-EN: %m"); - break; - case DUID_TYPE_LL: - if (client->mac_addr_len == 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EOPNOTSUPP), "Failed to set DUID-LL, MAC address is not set."); - - r = dhcp_identifier_set_duid_ll(&client->duid, client->mac_addr, client->mac_addr_len, client->arp_type, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-LL: %m"); - break; - case DUID_TYPE_UUID: - r = dhcp_identifier_set_duid_uuid(&client->duid, &client->duid_len); - if (r < 0) - return log_dhcp6_client_errno(client, r, "Failed to set DUID-UUID: %m"); - break; - default: - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "Invalid DUID type"); - } + } else { + r = dhcp_identifier_set_duid(duid_type, client->hw_addr.bytes, client->hw_addr.length, + client->arp_type, llt_time, client->test_mode, &client->duid, &client->duid_len); + if (r == -EOPNOTSUPP) + return log_dhcp6_client_errno(client, r, + "Failed to set %s. MAC address is not set or " + "interface type is not supported.", + duid_type_to_string(duid_type)); + if (r < 0) + return log_dhcp6_client_errno(client, r, "Failed to set %s: %m", + duid_type_to_string(duid_type)); + } return 0; } @@ -374,14 +251,6 @@ int sd_dhcp6_client_set_duid_llt( return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time); } -static const char* const dhcp6_duid_type_table[_DUID_TYPE_MAX] = { - [DUID_TYPE_LLT] = "DUID-LLT", - [DUID_TYPE_EN] = "DUID-EN/Vendor", - [DUID_TYPE_LL] = "DUID-LL", - [DUID_TYPE_UUID] = "UUID", -}; -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_duid_type, DUIDType); - int sd_dhcp6_client_duid_as_string( sd_dhcp6_client *client, char **duid) { @@ -393,7 +262,7 @@ int sd_dhcp6_client_duid_as_string( assert_return(client->duid_len > 0, -ENODATA); assert_return(duid, -EINVAL); - v = dhcp6_duid_type_to_string(be16toh(client->duid.type)); + v = duid_type_to_string(be16toh(client->duid.type)); if (v) { s = strdup(v); if (!s) @@ -419,38 +288,62 @@ int sd_dhcp6_client_duid_as_string( int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); - client->ia_na.ia_na.id = htobe32(iaid); - client->ia_pd.ia_pd.id = htobe32(iaid); + client->ia_na.header.id = htobe32(iaid); + client->ia_pd.header.id = htobe32(iaid); client->iaid_set = true; return 0; } +static int client_ensure_iaid(sd_dhcp6_client *client) { + int r; + uint32_t iaid; + + assert(client); + + if (client->iaid_set) + return 0; + + r = dhcp_identifier_set_iaid(client->ifindex, client->hw_addr.bytes, client->hw_addr.length, + /* legacy_unstable_byteorder = */ true, + /* use_mac = */ client->test_mode, + &iaid); + if (r < 0) + return r; + + client->ia_na.header.id = iaid; + client->ia_pd.header.id = iaid; + client->iaid_set = true; + + return 0; +} + +int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) { + assert_return(client, -EINVAL); + assert_return(iaid, -EINVAL); + + if (!client->iaid_set) + return -ENODATA; + + *iaid = be32toh(client->ia_na.header.id); + + return 0; +} + void dhcp6_client_set_test_mode(sd_dhcp6_client *client, bool test_mode) { assert(client); client->test_mode = test_mode; } -int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) { - assert_return(client, -EINVAL); - assert_return(iaid, -EINVAL); - - if (!client->iaid_set) - return -ENODATA; - - *iaid = be32toh(client->ia_na.ia_na.id); - - return 0; -} - int sd_dhcp6_client_set_fqdn( sd_dhcp6_client *client, const char *fqdn) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); /* Make sure FQDN qualifies as DNS and as Linux hostname */ if (fqdn && @@ -462,7 +355,7 @@ int sd_dhcp6_client_set_fqdn( int sd_dhcp6_client_set_information_request(sd_dhcp6_client *client, int enabled) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); client->information_request = enabled; @@ -482,7 +375,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) size_t t; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); if (!dhcp6_option_can_request(option)) return -EINVAL; @@ -501,7 +394,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) { assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(mudurl, -EINVAL); assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL); assert_return(http_url_is_valid(mudurl), -EINVAL); @@ -510,11 +403,10 @@ int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mud } int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const *user_class) { - char * const *p; char **s; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(!strv_isempty(user_class), -EINVAL); STRV_FOREACH(p, user_class) { @@ -532,11 +424,10 @@ int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char * const } int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char * const *vendor_class) { - char * const *p; char **s; assert_return(client, -EINVAL); - assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); assert_return(!strv_isempty(vendor_class), -EINVAL); STRV_FOREACH(p, vendor_class) { @@ -564,6 +455,7 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation); @@ -581,16 +473,20 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) { int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request); return 0; } -int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) { - assert_return(client, -EINVAL); +int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id) { + assert(client); + assert(client->test_mode); - client->transaction_id = transaction_id; + /* This is for tests or fuzzers. */ + + client->transaction_id = transaction_id & htobe32(0x00ffffff); return 0; } @@ -621,6 +517,18 @@ int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) { return 0; } +static void client_set_state(sd_dhcp6_client *client, DHCP6State state) { + assert(client); + + if (client->state == state) + return; + + log_dhcp6_client(client, "State changed: %s -> %s", + dhcp6_state_to_string(client->state), dhcp6_state_to_string(state)); + + client->state = state; +} + static void client_notify(sd_dhcp6_client *client, int event) { assert(client); @@ -628,30 +536,6 @@ static void client_notify(sd_dhcp6_client *client, int event) { client->callback(client, event, client->userdata); } -static int client_reset(sd_dhcp6_client *client) { - assert(client); - - client->lease = sd_dhcp6_lease_unref(client->lease); - - client->receive_message = - sd_event_source_unref(client->receive_message); - - client->transaction_id = 0; - client->transaction_start = 0; - - client->retransmit_time = 0; - client->retransmit_count = 0; - - (void) event_source_disable(client->timeout_resend); - (void) event_source_disable(client->timeout_resend_expire); - (void) event_source_disable(client->timeout_t1); - (void) event_source_disable(client->timeout_t2); - - client->state = DHCP6_STATE_STOPPED; - - return 0; -} - static void client_stop(sd_dhcp6_client *client, int error) { DHCP6_CLIENT_DONT_DESTROY(client); @@ -659,21 +543,115 @@ static void client_stop(sd_dhcp6_client *client, int error) { client_notify(client, error); - client_reset(client); + client->lease = sd_dhcp6_lease_unref(client->lease); + + /* Reset IRT here. Otherwise, we cannot restart the client in the information requesting mode, + * even though the lease is freed below. */ + client->information_request_time_usec = 0; + client->information_refresh_time_usec = 0; + + (void) event_source_disable(client->receive_message); + (void) event_source_disable(client->timeout_resend); + (void) event_source_disable(client->timeout_expire); + (void) event_source_disable(client->timeout_t1); + (void) event_source_disable(client->timeout_t2); + + client_set_state(client, DHCP6_STATE_STOPPED); } -static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { +static int client_append_common_options_in_managed_mode( + sd_dhcp6_client *client, + uint8_t **opt, + size_t *optlen, + const DHCP6IA *ia_na, + const DHCP6IA *ia_pd) { + + int r; + + assert(client); + assert(IN_SET(client->state, + DHCP6_STATE_SOLICITATION, + DHCP6_STATE_REQUEST, + DHCP6_STATE_RENEW, + DHCP6_STATE_REBIND)); + assert(opt); + assert(optlen); + + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && ia_na) { + r = dhcp6_option_append_ia(opt, optlen, ia_na); + if (r < 0) + return r; + } + + if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && ia_pd) { + r = dhcp6_option_append_ia(opt, optlen, ia_pd); + if (r < 0) + return r; + } + + if (client->fqdn) { + r = dhcp6_option_append_fqdn(opt, optlen, client->fqdn); + if (r < 0) + return r; + } + + if (client->user_class) { + r = dhcp6_option_append_user_class(opt, optlen, client->user_class); + if (r < 0) + return r; + } + + if (client->vendor_class) { + r = dhcp6_option_append_vendor_class(opt, optlen, client->vendor_class); + if (r < 0) + return r; + } + + if (!ordered_set_isempty(client->vendor_options)) { + r = dhcp6_option_append_vendor_option(opt, optlen, client->vendor_options); + if (r < 0) + return r; + } + + return 0; +} + +static DHCP6MessageType client_message_type_from_state(sd_dhcp6_client *client) { + assert(client); + + switch (client->state) { + case DHCP6_STATE_INFORMATION_REQUEST: + return DHCP6_MESSAGE_INFORMATION_REQUEST; + case DHCP6_STATE_SOLICITATION: + return DHCP6_MESSAGE_SOLICIT; + case DHCP6_STATE_REQUEST: + return DHCP6_MESSAGE_REQUEST; + case DHCP6_STATE_RENEW: + return DHCP6_MESSAGE_RENEW; + case DHCP6_STATE_REBIND: + return DHCP6_MESSAGE_REBIND; + default: + assert_not_reached(); + } +} + +int dhcp6_client_send_message(sd_dhcp6_client *client) { _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr all_servers = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; struct sd_dhcp6_option *j; size_t len, optlen = 512; uint8_t *opt; - int r; - usec_t elapsed_usec; + usec_t elapsed_usec, time_now; be16_t elapsed_time; + int r; assert(client); + assert(client->event); + + r = sd_event_now(client->event, CLOCK_BOOTTIME, &time_now); + if (r < 0) + return r; len = sizeof(DHCP6Message) + optlen; @@ -684,194 +662,56 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { opt = (uint8_t *)(message + 1); message->transaction_id = client->transaction_id; + message->type = client_message_type_from_state(client); - switch(client->state) { + switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: - message->type = DHCP6_MESSAGE_INFORMATION_REQUEST; - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - break; case DHCP6_STATE_SOLICITATION: - message->type = DHCP6_MESSAGE_SOLICIT; - - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL); if (r < 0) return r; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) { - r = dhcp6_option_append_ia(&opt, &optlen, - &client->ia_na); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, - client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix); - if (r < 0) - return r; - } - + r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + &client->ia_na, &client->ia_pd); + if (r < 0) + return r; break; case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: - if (client->state == DHCP6_STATE_REQUEST) - message->type = DHCP6_MESSAGE_REQUEST; - else - message->type = DHCP6_MESSAGE_RENEW; - r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID, client->lease->serverid_len, client->lease->serverid); if (r < 0) return r; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { - r = dhcp6_option_append_ia(&opt, &optlen, - &client->lease->ia); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); - if (r < 0) - return r; - } - - break; - + _fallthrough_; case DHCP6_STATE_REBIND: - message->type = DHCP6_MESSAGE_REBIND; - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) { - r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia); - if (r < 0) - return r; - } - - if (client->fqdn) { - r = dhcp6_option_append_fqdn(&opt, &optlen, client->fqdn); - if (r < 0) - return r; - } - - if (client->mudurl) { - r = dhcp6_option_append(&opt, &optlen, - SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl), - client->mudurl); - if (r < 0) - return r; - } - - if (client->user_class) { - r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class); - if (r < 0) - return r; - } - - if (client->vendor_class) { - r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class); - if (r < 0) - return r; - } - - if (!ordered_hashmap_isempty(client->vendor_options)) { - r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options); - if (r < 0) - return r; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL); - if (r < 0) - return r; - } + assert(client->lease); + r = client_append_common_options_in_managed_mode(client, &opt, &optlen, + client->lease->ia_na, client->lease->ia_pd); + if (r < 0) + return r; break; case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: - return -EINVAL; default: assert_not_reached(); } + if (client->mudurl) { + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_MUD_URL_V6, + strlen(client->mudurl), client->mudurl); + if (r < 0) + return r; + } + r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO, client->req_opts_len * sizeof(be16_t), client->req_opts); @@ -884,6 +724,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; + ORDERED_HASHMAP_FOREACH(j, client->extra_options) { + r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data); + if (r < 0) + return r; + } + /* RFC 8415 Section 21.9. * A client MUST include an Elapsed Time option in messages to indicate how long the client has * been trying to complete a DHCP message exchange. */ @@ -893,12 +739,6 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { if (r < 0) return r; - ORDERED_HASHMAP_FOREACH(j, client->extra_options) { - r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data); - if (r < 0) - return r; - } - r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message, len - optlen); if (r < 0) @@ -910,100 +750,43 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return 0; } -static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - - assert(s); - assert(client); - assert(client->lease); - - (void) event_source_disable(client->timeout_t2); - - log_dhcp6_client(client, "Timeout T2"); - - client_start(client, DHCP6_STATE_REBIND); - - return 0; -} - -static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - - assert(s); - assert(client); - assert(client->lease); - - (void) event_source_disable(client->timeout_t1); - - log_dhcp6_client(client, "Timeout T1"); - - client_start(client, DHCP6_STATE_RENEW); - - return 0; -} - -static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) { - sd_dhcp6_client *client = userdata; - DHCP6_CLIENT_DONT_DESTROY(client); - DHCP6State state; - - assert(s); - assert(client); - assert(client->event); - - state = client->state; - - client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE); - - /* RFC 3315, section 18.1.4., says that "...the client may choose to - use a Solicit message to locate a new DHCP server..." */ - if (state == DHCP6_STATE_REBIND) - client_start(client, DHCP6_STATE_SOLICITATION); - - return 0; -} - static usec_t client_timeout_compute_random(usec_t val) { - return val - (random_u32() % USEC_PER_SEC) * val / 10 / USEC_PER_SEC; + return usec_sub_unsigned(val, random_u64_range(val / 10)); } static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userdata) { - int r = 0; - sd_dhcp6_client *client = userdata; - usec_t time_now, init_retransmit_time = 0, max_retransmit_time = 0; - usec_t max_retransmit_duration = 0; - uint8_t max_retransmit_count = 0; + sd_dhcp6_client *client = ASSERT_PTR(userdata); + usec_t init_retransmit_time, max_retransmit_time; + int r; - assert(s); - assert(client); assert(client->event); - (void) event_source_disable(client->timeout_resend); - switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: init_retransmit_time = DHCP6_INF_TIMEOUT; max_retransmit_time = DHCP6_INF_MAX_RT; - break; case DHCP6_STATE_SOLICITATION: if (client->retransmit_count > 0 && client->lease) { - client_start(client, DHCP6_STATE_REQUEST); + (void) client_start_transaction(client, DHCP6_STATE_REQUEST); return 0; } init_retransmit_time = DHCP6_SOL_TIMEOUT; max_retransmit_time = DHCP6_SOL_MAX_RT; - break; case DHCP6_STATE_REQUEST: + + if (client->retransmit_count >= DHCP6_REQ_MAX_RC) { + client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); + return 0; + } + init_retransmit_time = DHCP6_REQ_TIMEOUT; max_retransmit_time = DHCP6_REQ_MAX_RT; - max_retransmit_count = DHCP6_REQ_MAX_RC; - break; case DHCP6_STATE_RENEW: @@ -1013,422 +796,365 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda /* RFC 3315, section 18.1.3. says max retransmit duration will be the remaining time until T2. Instead of setting MRD, wait for T2 to trigger with the same end result */ - break; case DHCP6_STATE_REBIND: init_retransmit_time = DHCP6_REB_TIMEOUT; max_retransmit_time = DHCP6_REB_MAX_RT; - if (event_source_is_enabled(client->timeout_resend_expire) <= 0) { - uint32_t expire = 0; - - r = dhcp6_lease_ia_rebind_expire(&client->lease->ia, &expire); - if (r < 0) { - client_stop(client, r); - return 0; - } - max_retransmit_duration = expire * USEC_PER_SEC; - } - + /* Also, instead of setting MRD, the expire timer is already set in client_enter_bound_state(). */ break; case DHCP6_STATE_STOPPED: case DHCP6_STATE_BOUND: - return 0; default: assert_not_reached(); } - if (max_retransmit_count > 0 && - client->retransmit_count >= max_retransmit_count) { - client_stop(client, SD_DHCP6_CLIENT_EVENT_RETRANS_MAX); - return 0; - } - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - goto error; - - r = client_send_message(client, time_now); + r = dhcp6_client_send_message(client); if (r >= 0) client->retransmit_count++; if (client->retransmit_time == 0) { - client->retransmit_time = - client_timeout_compute_random(init_retransmit_time); + client->retransmit_time = client_timeout_compute_random(init_retransmit_time); if (client->state == DHCP6_STATE_SOLICITATION) client->retransmit_time += init_retransmit_time / 10; - } else { - assert(max_retransmit_time > 0); - if (client->retransmit_time > max_retransmit_time / 2) - client->retransmit_time = client_timeout_compute_random(max_retransmit_time); - else - client->retransmit_time += client_timeout_compute_random(client->retransmit_time); - } + } else if (client->retransmit_time > max_retransmit_time / 2) + client->retransmit_time = client_timeout_compute_random(max_retransmit_time); + else + client->retransmit_time += client_timeout_compute_random(client->retransmit_time); log_dhcp6_client(client, "Next retransmission in %s", FORMAT_TIMESPAN(client->retransmit_time, USEC_PER_SEC)); - r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), - time_now + client->retransmit_time, 10 * USEC_PER_MSEC, - client_timeout_resend, client, - client->event_priority, "dhcp6-resend-timer", true); - if (r < 0) - goto error; - - if (max_retransmit_duration > 0 && event_source_is_enabled(client->timeout_resend_expire) <= 0) { - - log_dhcp6_client(client, "Max retransmission duration %"PRIu64" secs", - max_retransmit_duration / USEC_PER_SEC); - - r = event_reset_time(client->event, &client->timeout_resend_expire, - clock_boottime_or_monotonic(), - time_now + max_retransmit_duration, USEC_PER_SEC, - client_timeout_resend_expire, client, - client->event_priority, "dhcp6-resend-expire-timer", true); - if (r < 0) - goto error; - } - -error: + r = event_reset_time_relative(client->event, &client->timeout_resend, + CLOCK_BOOTTIME, + client->retransmit_time, 10 * USEC_PER_MSEC, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timer", true); if (r < 0) client_stop(client, r); return 0; } -static int client_ensure_iaid(sd_dhcp6_client *client) { +static int client_start_transaction(sd_dhcp6_client *client, DHCP6State state) { int r; - uint32_t iaid; assert(client); + assert(client->event); - if (client->iaid_set) - return 0; + switch (state) { + case DHCP6_STATE_INFORMATION_REQUEST: + case DHCP6_STATE_SOLICITATION: + assert(client->state == DHCP6_STATE_STOPPED); + break; + case DHCP6_STATE_REQUEST: + assert(client->state == DHCP6_STATE_SOLICITATION); + break; + case DHCP6_STATE_RENEW: + assert(client->state == DHCP6_STATE_BOUND); + break; + case DHCP6_STATE_REBIND: + assert(IN_SET(client->state, DHCP6_STATE_BOUND, DHCP6_STATE_RENEW)); + break; + case DHCP6_STATE_STOPPED: + case DHCP6_STATE_BOUND: + default: + assert_not_reached(); + } - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, - /* legacy_unstable_byteorder = */ true, - /* use_mac = */ client->test_mode, - &iaid); + client_set_state(client, state); + + client->retransmit_time = 0; + client->retransmit_count = 0; + client->transaction_id = random_u32() & htobe32(0x00ffffff); + + r = sd_event_now(client->event, CLOCK_BOOTTIME, &client->transaction_start); if (r < 0) - return r; + goto error; - client->ia_na.ia_na.id = iaid; - client->ia_pd.ia_pd.id = iaid; - client->iaid_set = true; + r = event_reset_time(client->event, &client->timeout_resend, + CLOCK_BOOTTIME, + 0, 0, + client_timeout_resend, client, + client->event_priority, "dhcp6-resend-timeout", true); + if (r < 0) + goto error; + + r = sd_event_source_set_enabled(client->receive_message, SD_EVENT_ON); + if (r < 0) + goto error; + + return 0; + +error: + client_stop(client, r); + return r; +} + +static int client_timeout_expire(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = ASSERT_PTR(userdata); + DHCP6_CLIENT_DONT_DESTROY(client); + DHCP6State state; + + (void) event_source_disable(client->timeout_expire); + (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_t1); + + state = client->state; + + client_stop(client, SD_DHCP6_CLIENT_EVENT_RESEND_EXPIRE); + + /* RFC 3315, section 18.1.4., says that "...the client may choose to + use a Solicit message to locate a new DHCP server..." */ + if (state == DHCP6_STATE_REBIND) + (void) client_start_transaction(client, DHCP6_STATE_SOLICITATION); return 0; } -int client_parse_message( +static int client_timeout_t2(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = ASSERT_PTR(userdata); + + (void) event_source_disable(client->timeout_t2); + (void) event_source_disable(client->timeout_t1); + + log_dhcp6_client(client, "Timeout T2"); + + (void) client_start_transaction(client, DHCP6_STATE_REBIND); + + return 0; +} + +static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata) { + sd_dhcp6_client *client = ASSERT_PTR(userdata); + + (void) event_source_disable(client->timeout_t1); + + log_dhcp6_client(client, "Timeout T1"); + + (void) client_start_transaction(client, DHCP6_STATE_RENEW); + + return 0; +} + +static int client_enter_bound_state(sd_dhcp6_client *client) { + usec_t lifetime_t1, lifetime_t2, lifetime_valid; + int r; + + assert(client); + assert(client->lease); + assert(IN_SET(client->state, + DHCP6_STATE_SOLICITATION, + DHCP6_STATE_REQUEST, + DHCP6_STATE_RENEW, + DHCP6_STATE_REBIND)); + + (void) event_source_disable(client->receive_message); + (void) event_source_disable(client->timeout_resend); + + r = dhcp6_lease_get_lifetime(client->lease, &lifetime_t1, &lifetime_t2, &lifetime_valid); + if (r < 0) + goto error; + + lifetime_t2 = client_timeout_compute_random(lifetime_t2); + lifetime_t1 = client_timeout_compute_random(MIN(lifetime_t1, lifetime_t2)); + + if (lifetime_t1 == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite T1"); + event_source_disable(client->timeout_t1); + } else { + log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(lifetime_t1, USEC_PER_SEC)); + r = event_reset_time_relative(client->event, &client->timeout_t1, + CLOCK_BOOTTIME, + lifetime_t1, 10 * USEC_PER_SEC, + client_timeout_t1, client, + client->event_priority, "dhcp6-t1-timeout", true); + if (r < 0) + goto error; + } + + if (lifetime_t2 == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite T2"); + event_source_disable(client->timeout_t2); + } else { + log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(lifetime_t2, USEC_PER_SEC)); + r = event_reset_time_relative(client->event, &client->timeout_t2, + CLOCK_BOOTTIME, + lifetime_t2, 10 * USEC_PER_SEC, + client_timeout_t2, client, + client->event_priority, "dhcp6-t2-timeout", true); + if (r < 0) + goto error; + } + + if (lifetime_valid == USEC_INFINITY) { + log_dhcp6_client(client, "Infinite valid lifetime"); + event_source_disable(client->timeout_expire); + } else { + log_dhcp6_client(client, "Valid lifetime expires in %s", FORMAT_TIMESPAN(lifetime_valid, USEC_PER_SEC)); + + r = event_reset_time_relative(client->event, &client->timeout_expire, + CLOCK_BOOTTIME, + lifetime_valid, USEC_PER_SEC, + client_timeout_expire, client, + client->event_priority, "dhcp6-lease-expire", true); + if (r < 0) + goto error; + } + + client_set_state(client, DHCP6_STATE_BOUND); + client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); + return 0; + +error: + client_stop(client, r); + return r; +} + +static int log_invalid_message_type(sd_dhcp6_client *client, const DHCP6Message *message) { + const char *type_str; + + assert(client); + assert(message); + + type_str = dhcp6_message_type_to_string(message->type); + if (type_str) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received unexpected %s message, ignoring.", type_str); + else + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received unsupported message type %u, ignoring.", message->type); +} + +static int client_process_information( sd_dhcp6_client *client, DHCP6Message *message, size_t len, - sd_dhcp6_lease *lease) { + const triple_timestamp *timestamp, + const struct in6_addr *server_address) { - uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX; - usec_t irt = IRT_DEFAULT; + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; int r; assert(client); assert(message); - assert(len >= sizeof(DHCP6Message)); - assert(lease); - len -= sizeof(DHCP6Message); - for (size_t offset = 0; offset < len;) { - uint16_t optcode; - size_t optlen; - const uint8_t *optval; + if (message->type != DHCP6_MESSAGE_REPLY) + return log_invalid_message_type(client, message); - r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); - if (r < 0) - return r; + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); + if (r < 0) + return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); - switch (optcode) { - case SD_DHCP6_OPTION_CLIENTID: - if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs", - dhcp6_message_type_to_string(message->type)); + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); - r = dhcp6_lease_set_clientid(lease, optval, optlen); - if (r < 0) - return r; + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); - break; - - case SD_DHCP6_OPTION_SERVERID: - if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs", - dhcp6_message_type_to_string(message->type)); - - r = dhcp6_lease_set_serverid(lease, optval, optlen); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_PREFERENCE: - if (optlen != 1) - return -EINVAL; - - r = dhcp6_lease_set_preference(lease, optval[0]); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_STATUS_CODE: { - _cleanup_free_ char *msg = NULL; - - r = dhcp6_option_parse_status(optval, optlen, &msg); - if (r < 0) - return r; - - if (r > 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Received %s message with non-zero status: %s%s%s", - dhcp6_message_type_to_string(message->type), - strempty(msg), isempty(msg) ? "" : ": ", - dhcp6_message_status_to_string(r)); - break; - } - case SD_DHCP6_OPTION_IA_NA: { - _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; - - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode."); - break; - } - - r = dhcp6_option_parse_ia(client, client->ia_pd.ia_na.id, optcode, optlen, optval, &ia); - if (r == -ENOMEM) - return r; - if (r < 0) - continue; - - if (lease->ia.addresses) { - log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); - continue; - } - - lease->ia = ia; - ia = (DHCP6IA) {}; - - lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2)); - - break; - } - case SD_DHCP6_OPTION_IA_PD: { - _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; - - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode."); - break; - } - - r = dhcp6_option_parse_ia(client, client->ia_pd.ia_pd.id, optcode, optlen, optval, &ia); - if (r == -ENOMEM) - return r; - if (r < 0) - continue; - - if (lease->pd.addresses) { - log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); - continue; - } - - lease->pd = ia; - ia = (DHCP6IA) {}; - - lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2)); - - break; - } - case SD_DHCP6_OPTION_RAPID_COMMIT: - r = dhcp6_lease_set_rapid_commit(lease); - if (r < 0) - return r; - - break; - - case SD_DHCP6_OPTION_DNS_SERVERS: - r = dhcp6_lease_add_dns(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_DOMAIN_LIST: - r = dhcp6_lease_add_domains(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_NTP_SERVER: - r = dhcp6_lease_add_ntp(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_SNTP_SERVERS: - r = dhcp6_lease_add_sntp(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_CLIENT_FQDN: - r = dhcp6_lease_set_fqdn(lease, optval, optlen); - if (r < 0) - log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m"); - - break; - - case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME: - if (optlen != 4) - return -EINVAL; - - irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; - break; - } - } - - uint8_t *clientid; - size_t clientid_len; - if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s message does not contain client ID. Ignoring.", - dhcp6_message_type_to_string(message->type)); - - if (clientid_len != client->duid_len || - memcmp(clientid, &client->duid, clientid_len) != 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.", - dhcp6_message_type_to_string(message->type)); - - if (client->state != DHCP6_STATE_INFORMATION_REQUEST) { - r = dhcp6_lease_get_serverid(lease, NULL, NULL); - if (r < 0) - return log_dhcp6_client_errno(client, r, "%s has no server id", - dhcp6_message_type_to_string(message->type)); - - if (!lease->ia.addresses && !lease->pd.addresses) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); - - if (lease->ia.addresses) { - lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1); - lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2); - } - - if (lease->pd.addresses) { - lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1); - lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2); - } - } - - client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); + /* Do not call client_stop() here, as it frees the acquired lease. */ + (void) event_source_disable(client->receive_message); + (void) event_source_disable(client->timeout_resend); + client_set_state(client, DHCP6_STATE_STOPPED); + client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); return 0; } -static int client_receive_reply( +static int client_process_reply( sd_dhcp6_client *client, - DHCP6Message *reply, + DHCP6Message *message, size_t len, - const triple_timestamp *t, + const triple_timestamp *timestamp, const struct in6_addr *server_address) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - bool rapid_commit; int r; assert(client); - assert(reply); - assert(t); + assert(message); - if (reply->type != DHCP6_MESSAGE_REPLY) - return 0; + if (message->type != DHCP6_MESSAGE_REPLY) + return log_invalid_message_type(client, message); - r = dhcp6_lease_new(&lease); + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); if (r < 0) - return -ENOMEM; + return log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); - lease->timestamp = *t; - if (server_address) - lease->server_address = *server_address; + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); - r = client_parse_message(client, reply, len, lease); + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); + + return client_enter_bound_state(client); +} + +static int client_process_advertise_or_rapid_commit_reply( + sd_dhcp6_client *client, + DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address) { + + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + uint8_t pref_advertise, pref_lease = 0; + int r; + + assert(client); + assert(message); + + if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY)) + return log_invalid_message_type(client, message); + + r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease); if (r < 0) - return r; + return log_dhcp6_client_errno(client, r, "Failed to process received %s message, ignoring: %m", + dhcp6_message_type_to_string(message->type)); + + if (message->type == DHCP6_MESSAGE_REPLY) { + bool rapid_commit; - if (client->state == DHCP6_STATE_SOLICITATION) { r = dhcp6_lease_get_rapid_commit(lease, &rapid_commit); if (r < 0) return r; if (!rapid_commit) - return 0; + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received reply message without rapid commit flag, ignoring."); + + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); + + sd_dhcp6_lease_unref(client->lease); + client->lease = TAKE_PTR(lease); + + return client_enter_bound_state(client); } - sd_dhcp6_lease_unref(client->lease); - client->lease = TAKE_PTR(lease); - - return DHCP6_STATE_BOUND; -} - -static int client_receive_advertise( - sd_dhcp6_client *client, - DHCP6Message *advertise, - size_t len, - const triple_timestamp *t, - const struct in6_addr *server_address) { - - _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t pref_advertise = 0, pref_lease = 0; - int r; - - assert(client); - assert(advertise); - assert(t); - - if (advertise->type != DHCP6_MESSAGE_ADVERTISE) - return 0; - - r = dhcp6_lease_new(&lease); - if (r < 0) - return r; - - lease->timestamp = *t; - if (server_address) - lease->server_address = *server_address; - - r = client_parse_message(client, advertise, len, lease); - if (r < 0) - return r; - r = dhcp6_lease_get_preference(lease, &pref_advertise); if (r < 0) return r; - r = dhcp6_lease_get_preference(client->lease, &pref_lease); + if (client->lease) { + r = dhcp6_lease_get_preference(client->lease, &pref_lease); + if (r < 0) + return r; + } - if (r < 0 || pref_advertise > pref_lease) { + log_dhcp6_client(client, "Processed %s message", dhcp6_message_type_to_string(message->type)); + + if (!client->lease || pref_advertise > pref_lease) { + /* If this is the first advertise message or has higher preference, then save the lease. */ sd_dhcp6_lease_unref(client->lease); client->lease = TAKE_PTR(lease); - r = 0; } if (pref_advertise == 255 || client->retransmit_count > 1) - r = DHCP6_STATE_REQUEST; + (void) client_start_transaction(client, DHCP6_STATE_REQUEST); - return r; + return 0; } static int client_receive_message( @@ -1437,7 +1163,7 @@ static int client_receive_message( revents, void *userdata) { - sd_dhcp6_client *client = userdata; + sd_dhcp6_client *client = ASSERT_PTR(userdata); DHCP6_CLIENT_DONT_DESTROY(client); /* This needs to be initialized with zero. See #20741. */ CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {}; @@ -1456,11 +1182,6 @@ static int client_receive_message( _cleanup_free_ DHCP6Message *message = NULL; struct in6_addr *server_address = NULL; ssize_t buflen, len; - int r = 0; - - assert(s); - assert(client); - assert(client->event); buflen = next_datagram_size_fd(fd); if (buflen < 0) { @@ -1507,236 +1228,43 @@ static int client_receive_message( triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg))); } - if (!triple_timestamp_is_set(&t)) - triple_timestamp_get(&t); - - if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY, DHCP6_MESSAGE_RECONFIGURE)) { - const char *type_str = dhcp6_message_type_to_string(message->type); - if (type_str) - log_dhcp6_client(client, "Received unexpected %s message, ignoring.", type_str); - else - log_dhcp6_client(client, "Received unsupported message type %u, ignoring.", message->type); - return 0; - } - if (client->transaction_id != (message->transaction_id & htobe32(0x00ffffff))) return 0; switch (client->state) { case DHCP6_STATE_INFORMATION_REQUEST: - r = client_receive_reply(client, message, len, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); + if (client_process_information(client, message, len, &t, server_address) < 0) return 0; - } - - client_notify(client, SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); - - client_start(client, DHCP6_STATE_STOPPED); - break; case DHCP6_STATE_SOLICITATION: - r = client_receive_advertise(client, message, len, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m"); + if (client_process_advertise_or_rapid_commit_reply(client, message, len, &t, server_address) < 0) return 0; - } + break; - if (r == DHCP6_STATE_REQUEST) { - client_start(client, r); - break; - } - - _fallthrough_; /* for Solicitation Rapid Commit option check */ case DHCP6_STATE_REQUEST: case DHCP6_STATE_RENEW: case DHCP6_STATE_REBIND: - - r = client_receive_reply(client, message, len, &t, server_address); - if (r < 0) { - log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m"); + if (client_process_reply(client, message, len, &t, server_address) < 0) return 0; - } - - if (r == DHCP6_STATE_BOUND) { - r = client_start(client, DHCP6_STATE_BOUND); - if (r < 0) { - client_stop(client, r); - return 0; - } - - client_notify(client, SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); - } - break; case DHCP6_STATE_BOUND: - - break; - case DHCP6_STATE_STOPPED: - return 0; default: assert_not_reached(); } - log_dhcp6_client(client, "Recv %s", - dhcp6_message_type_to_string(message->type)); - return 0; } -static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1, - uint32_t *lifetime_t2) { - assert_return(client, -EINVAL); - assert_return(client->lease, -EINVAL); - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) { - *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2); - - return 0; - } - - if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) { - *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1); - *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2); - - return 0; - } - - return -ENOMSG; -} - -static int client_start(sd_dhcp6_client *client, DHCP6State state) { - int r; - usec_t timeout, time_now; - uint32_t lifetime_t1, lifetime_t2; - - assert_return(client, -EINVAL); - assert_return(client->event, -EINVAL); - assert_return(client->ifindex > 0, -EINVAL); - assert_return(client->state != state, -EINVAL); - - (void) event_source_disable(client->timeout_resend_expire); - (void) event_source_disable(client->timeout_resend); - client->retransmit_time = 0; - client->retransmit_count = 0; - - r = sd_event_now(client->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - - if (!client->receive_message) { - r = sd_event_add_io(client->event, &client->receive_message, - client->fd, EPOLLIN, client_receive_message, - client); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(client->receive_message, - client->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(client->receive_message, - "dhcp6-receive-message"); - if (r < 0) - goto error; - } - - switch (state) { - case DHCP6_STATE_STOPPED: - if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { - client->state = DHCP6_STATE_STOPPED; - - return 0; - } - - _fallthrough_; - case DHCP6_STATE_SOLICITATION: - client->state = DHCP6_STATE_SOLICITATION; - - break; - - case DHCP6_STATE_INFORMATION_REQUEST: - case DHCP6_STATE_REQUEST: - case DHCP6_STATE_RENEW: - case DHCP6_STATE_REBIND: - - client->state = state; - - break; - - case DHCP6_STATE_BOUND: - - r = client_get_lifetime(client, &lifetime_t1, &lifetime_t2); - if (r < 0) - goto error; - - if (lifetime_t1 == 0xffffffff || lifetime_t2 == 0xffffffff) { - log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", - lifetime_t1, lifetime_t2); - - return 0; - } - - timeout = client_timeout_compute_random(lifetime_t1 * USEC_PER_SEC); - - log_dhcp6_client(client, "T1 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - - r = event_reset_time(client->event, &client->timeout_t1, - clock_boottime_or_monotonic(), - time_now + timeout, 10 * USEC_PER_SEC, - client_timeout_t1, client, - client->event_priority, "dhcp6-t1-timeout", true); - if (r < 0) - goto error; - - timeout = client_timeout_compute_random(lifetime_t2 * USEC_PER_SEC); - - log_dhcp6_client(client, "T2 expires in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); - - r = event_reset_time(client->event, &client->timeout_t2, - clock_boottime_or_monotonic(), - time_now + timeout, 10 * USEC_PER_SEC, - client_timeout_t2, client, - client->event_priority, "dhcp6-t2-timeout", true); - if (r < 0) - goto error; - - client->state = state; - - return 0; - default: - assert_not_reached(); - } - - client->transaction_id = random_u32() & htobe32(0x00ffffff); - client->transaction_start = time_now; - - r = event_reset_time(client->event, &client->timeout_resend, - clock_boottime_or_monotonic(), - 0, 0, - client_timeout_resend, client, - client->event_priority, "dhcp6-resend-timeout", true); - if (r < 0) - goto error; - - return 0; - - error: - client_reset(client); - return r; -} - int sd_dhcp6_client_stop(sd_dhcp6_client *client) { if (!client) return 0; client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP); + client->receive_message = sd_event_source_unref(client->receive_message); client->fd = safe_close(client->fd); return 0; @@ -1756,16 +1284,12 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { assert_return(client->event, -EINVAL); assert_return(client->ifindex > 0, -EINVAL); assert_return(in6_addr_is_link_local(&client->local_address) > 0, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); + assert_return(client->information_request || client->request_ia != 0, -EINVAL); - if (client->state != DHCP6_STATE_STOPPED) - return -EBUSY; - - if (!client->information_request && client->request_ia == 0) - return -EINVAL; - - r = client_reset(client); - if (r < 0) - return r; + /* Even if the client is in the STOPPED state, the lease acquired in the previous information + * request may be stored. */ + client->lease = sd_dhcp6_lease_unref(client->lease); r = client_ensure_iaid(client); if (r < 0) @@ -1780,7 +1304,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { if (r < 0) { _cleanup_free_ char *p = NULL; - (void) in_addr_to_string(AF_INET6, (const union in_addr_union*) &client->local_address, &p); + (void) in6_addr_to_string(&client->local_address, &p); return log_dhcp6_client_errno(client, r, "Failed to bind to UDP socket at address %s: %m", strna(p)); } @@ -1788,6 +1312,24 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { client->fd = r; } + if (!client->receive_message) { + _cleanup_(sd_event_source_disable_unrefp) sd_event_source *s = NULL; + + r = sd_event_add_io(client->event, &s, client->fd, EPOLLIN, client_receive_message, client); + if (r < 0) + return r; + + r = sd_event_source_set_priority(s, client->event_priority); + if (r < 0) + return r; + + r = sd_event_source_set_description(s, "dhcp6-receive-message"); + if (r < 0) + return r; + + client->receive_message = TAKE_PTR(s); + } + if (client->information_request) { usec_t t = now(CLOCK_MONOTONIC); @@ -1798,10 +1340,10 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) { state = DHCP6_STATE_INFORMATION_REQUEST; } - log_dhcp6_client(client, "Started in %s mode", + log_dhcp6_client(client, "Starting in %s mode", client->information_request ? "Information request" : "Managed"); - return client_start(client, state); + return client_start_transaction(client, state); } int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64_t priority) { @@ -1809,6 +1351,7 @@ int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64 assert_return(client, -EINVAL); assert_return(!client->event, -EBUSY); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); if (event) client->event = sd_event_ref(event); @@ -1825,6 +1368,7 @@ int sd_dhcp6_client_attach_event(sd_dhcp6_client *client, sd_event *event, int64 int sd_dhcp6_client_detach_event(sd_dhcp6_client *client) { assert_return(client, -EINVAL); + assert_return(!sd_dhcp6_client_is_running(client), -EBUSY); client->event = sd_event_unref(client->event); @@ -1838,24 +1382,26 @@ sd_event *sd_dhcp6_client_get_event(sd_dhcp6_client *client) { } static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) { - assert(client); + if (!client) + return NULL; - client->timeout_resend = sd_event_source_unref(client->timeout_resend); - client->timeout_resend_expire = sd_event_source_unref(client->timeout_resend_expire); - client->timeout_t1 = sd_event_source_unref(client->timeout_t1); - client->timeout_t2 = sd_event_source_unref(client->timeout_t2); + sd_dhcp6_lease_unref(client->lease); - client_reset(client); + sd_event_source_disable_unref(client->receive_message); + sd_event_source_disable_unref(client->timeout_resend); + sd_event_source_disable_unref(client->timeout_expire); + sd_event_source_disable_unref(client->timeout_t1); + sd_event_source_disable_unref(client->timeout_t2); + sd_event_unref(client->event); client->fd = safe_close(client->fd); - sd_dhcp6_client_detach_event(client); - free(client->req_opts); free(client->fqdn); free(client->mudurl); - + dhcp6_ia_clear_addresses(&client->ia_pd); ordered_hashmap_free(client->extra_options); + ordered_set_free(client->vendor_options); strv_free(client->user_class); strv_free(client->vendor_class); free(client->ifname); @@ -1891,8 +1437,6 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD, .fd = -1, .req_opts_len = ELEMENTSOF(default_req_opts), - .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1, - .hint_pd_prefix.iapdprefix.lifetime_valid = (be32_t) -1, .req_opts = TAKE_PTR(req_opts), }; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index a47e6a019..8734e5a82 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -6,10 +6,21 @@ #include #include "alloc-util.h" +#include "dhcp6-internal.h" #include "dhcp6-lease-internal.h" -#include "dhcp6-protocol.h" #include "strv.h" -#include "util.h" + +#define IRT_DEFAULT (1 * USEC_PER_DAY) +#define IRT_MINIMUM (600 * USEC_PER_SEC) + +static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timestamp *timestamp) { + assert(lease); + + if (timestamp && triple_timestamp_is_set(timestamp)) + lease->timestamp = *timestamp; + else + triple_timestamp_get(&lease->timestamp); +} int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) { assert_return(lease, -EINVAL); @@ -24,6 +35,68 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_ return 0; } +static usec_t sec2usec(uint32_t sec) { + return sec == UINT32_MAX ? USEC_INFINITY : sec * USEC_PER_SEC; +} + +static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) { + uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX; + + assert(lease); + assert(lease->ia_na || lease->ia_pd); + + if (lease->ia_na) { + t1 = MIN(t1, be32toh(lease->ia_na->header.lifetime_t1)); + t2 = MIN(t2, be32toh(lease->ia_na->header.lifetime_t2)); + + LIST_FOREACH(addresses, a, lease->ia_na->addresses) + min_valid_lt = MIN(min_valid_lt, be32toh(a->iaaddr.lifetime_valid)); + } + + if (lease->ia_pd) { + t1 = MIN(t1, be32toh(lease->ia_pd->header.lifetime_t1)); + t2 = MIN(t2, be32toh(lease->ia_pd->header.lifetime_t2)); + + LIST_FOREACH(addresses, a, lease->ia_pd->addresses) + min_valid_lt = MIN(min_valid_lt, be32toh(a->iapdprefix.lifetime_valid)); + } + + if (t2 == 0 || t2 > min_valid_lt) { + /* If T2 is zero or longer than the minimum valid lifetime of the addresses or prefixes, + * then adjust lifetime with it. */ + t1 = min_valid_lt / 2; + t2 = min_valid_lt / 10 * 8; + } + + lease->lifetime_valid = sec2usec(min_valid_lt); + lease->lifetime_t1 = sec2usec(t1); + lease->lifetime_t2 = sec2usec(t2); +} + +int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2, usec_t *ret_valid) { + assert(lease); + + if (!lease->ia_na && !lease->ia_pd) + return -ENODATA; + + if (ret_t1) + *ret_t1 = lease->lifetime_t1; + if (ret_t2) + *ret_t2 = lease->lifetime_t2; + if (ret_valid) + *ret_valid = lease->lifetime_valid; + return 0; +} + +static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) { + assert(lease); + + if (server_address) + lease->server_address = *server_address; + else + lease->server_address = (struct in6_addr) {}; +} + int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) { assert_return(lease, -EINVAL); assert_return(ret, -EINVAL); @@ -32,55 +105,35 @@ int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *re return 0; } -int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { - DHCP6Address *addr; - uint32_t valid = 0, t; +void dhcp6_ia_clear_addresses(DHCP6IA *ia) { + assert(ia); - assert_return(ia, -EINVAL); - assert_return(expire, -EINVAL); + LIST_FOREACH(addresses, a, ia->addresses) + free(a); - LIST_FOREACH(addresses, addr, ia->addresses) { - t = be32toh(addr->iaaddr.lifetime_valid); - if (valid < t) - valid = t; - } - - t = be32toh(ia->ia_na.lifetime_t2); - if (t > valid) - return -EINVAL; - - *expire = valid - t; - - return 0; + ia->addresses = NULL; } -DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) { - DHCP6Address *address; - +DHCP6IA *dhcp6_ia_free(DHCP6IA *ia) { if (!ia) return NULL; - while (ia->addresses) { - address = ia->addresses; + dhcp6_ia_clear_addresses(ia); - LIST_REMOVE(addresses, ia->addresses, address); - - free(address); - } - - return NULL; + return mfree(ia); } int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { - uint8_t *clientid; + uint8_t *clientid = NULL; - assert_return(lease, -EINVAL); - assert_return(id, -EINVAL); - assert_return(len > 0, -EINVAL); + assert(lease); + assert(id || len == 0); - clientid = memdup(id, len); - if (!clientid) - return -ENOMEM; + if (len > 0) { + clientid = memdup(id, len); + if (!clientid) + return -ENOMEM; + } free_and_replace(lease->clientid, clientid); lease->clientid_len = len; @@ -89,7 +142,7 @@ int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le } int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) { - assert_return(lease, -EINVAL); + assert(lease); if (!lease->clientid) return -ENODATA; @@ -103,15 +156,16 @@ int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re } int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) { - uint8_t *serverid; + uint8_t *serverid = NULL; - assert_return(lease, -EINVAL); - assert_return(id, -EINVAL); - assert_return(len > 0, -EINVAL); + assert(lease); + assert(id || len == 0); - serverid = memdup(id, len); - if (!serverid) - return -ENOMEM; + if (len > 0) { + serverid = memdup(id, len); + if (!serverid) + return -ENOMEM; + } free_and_replace(lease->serverid, serverid); lease->serverid_len = len; @@ -120,7 +174,7 @@ int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t le } int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) { - assert_return(lease, -EINVAL); + assert(lease); if (!lease->serverid) return -ENODATA; @@ -129,107 +183,99 @@ int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *re *ret_id = lease->serverid; if (ret_len) *ret_len = lease->serverid_len; - return 0; } int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference) { - assert_return(lease, -EINVAL); + assert(lease); lease->preference = preference; - return 0; } -int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference) { - assert_return(preference, -EINVAL); - - if (!lease) - return -EINVAL; - - *preference = lease->preference; +int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *ret) { + assert(lease); + assert(ret); + *ret = lease->preference; return 0; } int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease) { - assert_return(lease, -EINVAL); + assert(lease); lease->rapid_commit = true; - return 0; } -int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) { - assert_return(lease, -EINVAL); - assert_return(rapid_commit, -EINVAL); - - *rapid_commit = lease->rapid_commit; +int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *ret) { + assert(lease); + assert(ret); + *ret = lease->rapid_commit; return 0; } -int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid) { +int sd_dhcp6_lease_get_address( + sd_dhcp6_lease *lease, + struct in6_addr *ret_addr, + uint32_t *ret_lifetime_preferred, + uint32_t *ret_lifetime_valid) { + assert_return(lease, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(lifetime_preferred, -EINVAL); - assert_return(lifetime_valid, -EINVAL); if (!lease->addr_iter) - return -ENOMSG; + return -ENODATA; - memcpy(addr, &lease->addr_iter->iaaddr.address, - sizeof(struct in6_addr)); - *lifetime_preferred = - be32toh(lease->addr_iter->iaaddr.lifetime_preferred); - *lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid); + if (ret_addr) + *ret_addr = lease->addr_iter->iaaddr.address; + if (ret_lifetime_preferred) + *ret_lifetime_preferred = be32toh(lease->addr_iter->iaaddr.lifetime_preferred); + if (ret_lifetime_valid) + *ret_lifetime_valid = be32toh(lease->addr_iter->iaaddr.lifetime_valid); lease->addr_iter = lease->addr_iter->addresses_next; - return 0; } void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { if (lease) - lease->addr_iter = lease->ia.addresses; + lease->addr_iter = lease->ia_na ? lease->ia_na->addresses : NULL; } -int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, - uint8_t *prefix_len, - uint32_t *lifetime_preferred, - uint32_t *lifetime_valid) { +int sd_dhcp6_lease_get_pd( + sd_dhcp6_lease *lease, + struct in6_addr *ret_prefix, + uint8_t *ret_prefix_len, + uint32_t *ret_lifetime_preferred, + uint32_t *ret_lifetime_valid) { + assert_return(lease, -EINVAL); - assert_return(prefix, -EINVAL); - assert_return(prefix_len, -EINVAL); - assert_return(lifetime_preferred, -EINVAL); - assert_return(lifetime_valid, -EINVAL); if (!lease->prefix_iter) - return -ENOMSG; + return -ENODATA; - memcpy(prefix, &lease->prefix_iter->iapdprefix.address, - sizeof(struct in6_addr)); - *prefix_len = lease->prefix_iter->iapdprefix.prefixlen; - *lifetime_preferred = - be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); - *lifetime_valid = - be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); + if (ret_prefix) + *ret_prefix = lease->prefix_iter->iapdprefix.address; + if (ret_prefix_len) + *ret_prefix_len = lease->prefix_iter->iapdprefix.prefixlen; + if (ret_lifetime_preferred) + *ret_lifetime_preferred = be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); + if (ret_lifetime_valid) + *ret_lifetime_valid = be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); lease->prefix_iter = lease->prefix_iter->addresses_next; - return 0; } void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { if (lease) - lease->prefix_iter = lease->pd.addresses; + lease->prefix_iter = lease->ia_pd ? lease->ia_pd->addresses : NULL; } int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -241,7 +287,7 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) { assert_return(lease, -EINVAL); if (!lease->dns) - return -ENOENT; + return -ENODATA; if (ret) *ret = lease->dns; @@ -253,8 +299,8 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t _cleanup_strv_free_ char **domains = NULL; int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -271,7 +317,7 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { assert_return(ret, -EINVAL); if (!lease->domains) - return -ENOENT; + return -ENODATA; *ret = lease->domains; return strv_length(lease->domains); @@ -280,8 +326,8 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); for (size_t offset = 0; offset < optlen;) { const uint8_t *subval; @@ -296,7 +342,7 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt case DHCP6_NTP_SUBOPTION_SRV_ADDR: case DHCP6_NTP_SUBOPTION_MC_ADDR: if (sublen != 16) - return 0; + return -EINVAL; r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count); if (r < 0) @@ -326,8 +372,8 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt } int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); if (optlen == 0) return 0; @@ -352,14 +398,14 @@ int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr ** return lease->sntp_count; } - return -ENOENT; + return -ENODATA; } int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { assert_return(lease, -EINVAL); if (!lease->ntp_fqdn) - return -ENOENT; + return -ENODATA; if (ret) *ret = lease->ntp_fqdn; @@ -367,11 +413,14 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { } int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { - int r; char *fqdn; + int r; - assert_return(lease, -EINVAL); - assert_return(optval, -EINVAL); + assert(lease); + assert(optval || optlen == 0); + + if (optlen == 0) + return 0; if (optlen < 2) return -ENODATA; @@ -390,20 +439,222 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) { assert_return(ret, -EINVAL); if (!lease->fqdn) - return -ENOENT; + return -ENODATA; *ret = lease->fqdn; return 0; } +static int dhcp6_lease_parse_message( + sd_dhcp6_client *client, + sd_dhcp6_lease *lease, + const DHCP6Message *message, + size_t len) { + + usec_t irt = IRT_DEFAULT; + int r; + + assert(client); + assert(lease); + assert(message); + assert(len >= sizeof(DHCP6Message)); + + len -= sizeof(DHCP6Message); + for (size_t offset = 0; offset < len;) { + uint16_t optcode; + size_t optlen; + const uint8_t *optval; + + r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); + if (r < 0) + return r; + + switch (optcode) { + case SD_DHCP6_OPTION_CLIENTID: + if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs", + dhcp6_message_type_to_string(message->type)); + + r = dhcp6_lease_set_clientid(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_SERVERID: + if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs", + dhcp6_message_type_to_string(message->type)); + + r = dhcp6_lease_set_serverid(lease, optval, optlen); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_PREFERENCE: + if (optlen != 1) + return -EINVAL; + + r = dhcp6_lease_set_preference(lease, optval[0]); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_STATUS_CODE: { + _cleanup_free_ char *msg = NULL; + + r = dhcp6_option_parse_status(optval, optlen, &msg); + if (r < 0) + return r; + + if (r > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received %s message with non-zero status: %s%s%s", + dhcp6_message_type_to_string(message->type), + strempty(msg), isempty(msg) ? "" : ": ", + dhcp6_message_status_to_string(r)); + break; + } + case SD_DHCP6_OPTION_IA_NA: { + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; + + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode."); + break; + } + + r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + + if (lease->ia_na) { + log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); + continue; + } + + dhcp6_ia_free(lease->ia_na); + lease->ia_na = TAKE_PTR(ia); + break; + } + case SD_DHCP6_OPTION_IA_PD: { + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; + + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode."); + break; + } + + r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + + if (lease->ia_pd) { + log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); + continue; + } + + dhcp6_ia_free(lease->ia_pd); + lease->ia_pd = TAKE_PTR(ia); + break; + } + case SD_DHCP6_OPTION_RAPID_COMMIT: + r = dhcp6_lease_set_rapid_commit(lease); + if (r < 0) + return r; + + break; + + case SD_DHCP6_OPTION_DNS_SERVERS: + r = dhcp6_lease_add_dns(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_DOMAIN_LIST: + r = dhcp6_lease_add_domains(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_NTP_SERVER: + r = dhcp6_lease_add_ntp(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_SNTP_SERVERS: + r = dhcp6_lease_add_sntp(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_CLIENT_FQDN: + r = dhcp6_lease_set_fqdn(lease, optval, optlen); + if (r < 0) + log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m"); + + break; + + case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME: + if (optlen != 4) + return -EINVAL; + + irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; + break; + } + } + + uint8_t *clientid; + size_t clientid_len; + if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "%s message does not contain client ID. Ignoring.", + dhcp6_message_type_to_string(message->type)); + + if (memcmp_nn(clientid, clientid_len, &client->duid, client->duid_len) != 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "The client ID in %s message does not match. Ignoring.", + dhcp6_message_type_to_string(message->type)); + + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); + log_dhcp6_client(client, "New information request will be refused in %s.", + FORMAT_TIMESPAN(client->information_refresh_time_usec, USEC_PER_SEC)); + + } else { + r = dhcp6_lease_get_serverid(lease, NULL, NULL); + if (r < 0) + return log_dhcp6_client_errno(client, r, "%s has no server id", + dhcp6_message_type_to_string(message->type)); + + if (!lease->ia_na && !lease->ia_pd) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "No IA_PD prefix or IA_NA address received. Ignoring."); + + dhcp6_lease_set_lifetime(lease); + } + + return 0; +} + static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { if (!lease) return NULL; free(lease->clientid); free(lease->serverid); - dhcp6_lease_free_ia(&lease->ia); - dhcp6_lease_free_ia(&lease->pd); + dhcp6_ia_free(lease->ia_na); + dhcp6_ia_free(lease->ia_pd); free(lease->dns); free(lease->fqdn); strv_free(lease->domains); @@ -419,14 +670,47 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_lease, sd_dhcp6_lease, dhcp6_lease_free); int dhcp6_lease_new(sd_dhcp6_lease **ret) { sd_dhcp6_lease *lease; - lease = new0(sd_dhcp6_lease, 1); + assert(ret); + + lease = new(sd_dhcp6_lease, 1); if (!lease) return -ENOMEM; - lease->n_ref = 1; - - LIST_HEAD_INIT(lease->ia.addresses); + *lease = (sd_dhcp6_lease) { + .n_ref = 1, + }; *ret = lease; return 0; } + +int dhcp6_lease_new_from_message( + sd_dhcp6_client *client, + const DHCP6Message *message, + size_t len, + const triple_timestamp *timestamp, + const struct in6_addr *server_address, + sd_dhcp6_lease **ret) { + + _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; + int r; + + assert(client); + assert(message); + assert(len >= sizeof(DHCP6Message)); + assert(ret); + + r = dhcp6_lease_new(&lease); + if (r < 0) + return r; + + dhcp6_lease_set_timestamp(lease, timestamp); + dhcp6_lease_set_server_address(lease, server_address); + + r = dhcp6_lease_parse_message(client, lease, message, len); + if (r < 0) + return r; + + *ret = TAKE_PTR(lease); + return 0; +} diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index 232b3b033..8303e7fb3 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -200,10 +200,10 @@ static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_u if (random_usec > 0) next_timeout += (usec_t) random_u64() % random_usec; - assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0); + assert_se(sd_event_now(acd->event, CLOCK_BOOTTIME, &time_now) >= 0); return event_reset_time(acd->event, &acd->timer_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, time_now + next_timeout, 0, ipv4acd_on_timeout, acd, acd->event_priority, "ipv4acd-timer", true); @@ -381,7 +381,7 @@ static int ipv4acd_on_packet( if (ipv4acd_arp_conflict(acd, &packet, true)) { usec_t ts; - assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0); + assert_se(sd_event_now(acd->event, CLOCK_BOOTTIME, &ts) >= 0); /* Defend address */ if (ts > acd->defend_window) { diff --git a/src/libsystemd-network/sd-lldp-rx.c b/src/libsystemd-network/sd-lldp-rx.c index 34bdcb644..d4762bf09 100644 --- a/src/libsystemd-network/sd-lldp-rx.c +++ b/src/libsystemd-network/sd-lldp-rx.c @@ -69,7 +69,7 @@ static int lldp_rx_make_space(sd_lldp_rx *lldp_rx, size_t extra) { goto remove_one; if (t == USEC_INFINITY) - t = now(clock_boottime_or_monotonic()); + t = now(CLOCK_BOOTTIME); if (n->until > t) break; @@ -448,7 +448,7 @@ static int lldp_rx_start_timer(sd_lldp_rx *lldp_rx, sd_lldp_neighbor *neighbor) return event_source_disable(lldp_rx->timer_event_source); return event_reset_time(lldp_rx->event, &lldp_rx->timer_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, n->until, 0, on_timer_event, lldp_rx, lldp_rx->event_priority, "lldp-rx-timer", true); diff --git a/src/libsystemd-network/sd-lldp-tx.c b/src/libsystemd-network/sd-lldp-tx.c index a5f60346e..136059fa7 100644 --- a/src/libsystemd-network/sd-lldp-tx.c +++ b/src/libsystemd-network/sd-lldp-tx.c @@ -609,7 +609,7 @@ int sd_lldp_tx_start(sd_lldp_tx *lldp_tx) { delay = lldp_tx_get_delay(lldp_tx); r = sd_event_add_time_relative(lldp_tx->event, &lldp_tx->timer_event_source, - clock_boottime_or_monotonic(), delay, 0, + CLOCK_BOOTTIME, delay, 0, on_timer_event, lldp_tx); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index 7e16f5129..ecd552ca0 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -269,7 +269,7 @@ static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) { assert(nd); assert(nd->event); - assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0); + assert_se(sd_event_now(nd->event, CLOCK_BOOTTIME, &time_now) >= 0); if (!nd->retransmit_time) nd->retransmit_time = ndisc_timeout_compute_random(NDISC_ROUTER_SOLICITATION_INTERVAL); @@ -281,7 +281,7 @@ static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) { } r = event_reset_time(nd->event, &nd->timeout_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, time_now + nd->retransmit_time, 10 * USEC_PER_MSEC, ndisc_timeout, nd, nd->event_priority, "ndisc-timeout-no-ra", true); @@ -344,7 +344,7 @@ int sd_ndisc_start(sd_ndisc *nd) { assert(!nd->recv_event_source); - r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(nd->event, CLOCK_BOOTTIME, &time_now); if (r < 0) goto fail; @@ -363,7 +363,7 @@ int sd_ndisc_start(sd_ndisc *nd) { (void) sd_event_source_set_description(nd->recv_event_source, "ndisc-receive-message"); r = event_reset_time(nd->event, &nd->timeout_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, time_now + USEC_PER_SEC / 2, 1 * USEC_PER_SEC, /* See RFC 8415 sec. 18.2.1 */ ndisc_timeout, nd, nd->event_priority, "ndisc-timeout", true); @@ -371,7 +371,7 @@ int sd_ndisc_start(sd_ndisc *nd) { goto fail; r = event_reset_time(nd->event, &nd->timeout_no_ra, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, time_now + NDISC_TIMEOUT_NO_RA_USEC, 10 * USEC_PER_MSEC, ndisc_timeout_no_ra, nd, nd->event_priority, "ndisc-timeout-no-ra", true); diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index e332f6a2a..7b5c12e48 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -148,8 +148,6 @@ static be32_t usec_to_be32_sec(usec_t usec) { } static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_usec) { - sd_radv_route_prefix *rt; - sd_radv_prefix *p; struct sockaddr_in6 dst_addr = { .sin6_family = AF_INET6, .sin6_addr = IN6ADDR_ALL_NODES_MULTICAST_INIT, @@ -183,7 +181,7 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, usec_t lifetime_us assert(ra); assert(router_lifetime_is_valid(lifetime_usec)); - r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now); if (r < 0) return r; @@ -323,7 +321,7 @@ static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) { assert(ra->event); assert(router_lifetime_is_valid(ra->lifetime_usec)); - r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); + r = sd_event_now(ra->event, CLOCK_BOOTTIME, &time_now); if (r < 0) goto fail; @@ -359,7 +357,7 @@ static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) { log_radv(ra, "Next Router Advertisement in %s", FORMAT_TIMESPAN(timeout, USEC_PER_SEC)); r = event_reset_time(ra->event, &ra->timeout_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, usec_add(time_now, timeout), MSEC_PER_SEC, radv_timeout, ra, ra->event_priority, "radv-timeout", true); @@ -411,7 +409,7 @@ int sd_radv_start(sd_radv *ra) { return 0; r = event_reset_time(ra->event, &ra->timeout_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, 0, 0, radv_timeout, ra, ra->event_priority, "radv-timeout", true); @@ -578,8 +576,7 @@ int sd_radv_set_preference(sd_radv *ra, unsigned preference) { int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { _cleanup_free_ char *addr_p = NULL; - sd_radv_prefix *cur; - bool update = false; + sd_radv_prefix *found = NULL; int r; assert_return(ra, -EINVAL); @@ -604,7 +601,7 @@ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { continue; if (cur->opt.prefixlen == p->opt.prefixlen) { - update = true; + found = cur; break; } @@ -615,15 +612,13 @@ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { strna(addr_p), strna(addr_cur)); } - if (update) { - assert(cur); - + if (found) { /* p and cur may be equivalent. First increment the reference counter. */ sd_radv_prefix_ref(p); /* Then, remove the old entry. */ - LIST_REMOVE(prefix, ra->prefixes, cur); - sd_radv_prefix_unref(cur); + LIST_REMOVE(prefix, ra->prefixes, found); + sd_radv_prefix_unref(found); /* Finally, add the new entry. */ LIST_APPEND(prefix, ra->prefixes, p); @@ -659,15 +654,18 @@ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { return 0; } -sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, - const struct in6_addr *prefix, - unsigned char prefixlen) { - sd_radv_prefix *cur, *next; +void sd_radv_remove_prefix( + sd_radv *ra, + const struct in6_addr *prefix, + unsigned char prefixlen) { - assert_return(ra, NULL); - assert_return(prefix, NULL); + if (!ra) + return; - LIST_FOREACH_SAFE(prefix, cur, next, ra->prefixes) { + if (!prefix) + return; + + LIST_FOREACH(prefix, cur, ra->prefixes) { if (prefixlen != cur->opt.prefixlen) continue; @@ -677,17 +675,13 @@ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, LIST_REMOVE(prefix, ra->prefixes, cur); ra->n_prefixes--; sd_radv_prefix_unref(cur); - - break; + return; } - - return cur; } int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) { _cleanup_free_ char *addr_p = NULL; - sd_radv_route_prefix *cur; - bool update = false; + sd_radv_route_prefix *found = NULL; int r; assert_return(ra, -EINVAL); @@ -708,7 +702,7 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) { continue; if (cur->opt.prefixlen == p->opt.prefixlen) { - update = true; + found = cur; break; } @@ -719,15 +713,13 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) { strna(addr_p), strna(addr_cur)); } - if (update) { - assert(cur); - + if (found) { /* p and cur may be equivalent. First increment the reference counter. */ sd_radv_route_prefix_ref(p); /* Then, remove the old entry. */ - LIST_REMOVE(prefix, ra->route_prefixes, cur); - sd_radv_route_prefix_unref(cur); + LIST_REMOVE(prefix, ra->route_prefixes, found); + sd_radv_route_prefix_unref(found); /* Finally, add the new entry. */ LIST_APPEND(prefix, ra->route_prefixes, p); @@ -762,8 +754,12 @@ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p) { return 0; } -int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, - const struct in6_addr *dns, size_t n_dns) { +int sd_radv_set_rdnss( + sd_radv *ra, + uint32_t lifetime, + const struct in6_addr *dns, + size_t n_dns) { + _cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL; size_t len; @@ -796,11 +792,13 @@ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, return 0; } -int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime, - char **search_list) { +int sd_radv_set_dnssl( + sd_radv *ra, + uint32_t lifetime, + char **search_list) { + _cleanup_free_ struct sd_radv_opt_dns *opt_dnssl = NULL; size_t len = 0; - char **s; uint8_t *p; assert_return(ra, -EINVAL); @@ -876,8 +874,11 @@ int sd_radv_prefix_new(sd_radv_prefix **ret) { DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_prefix, sd_radv_prefix, mfree); -int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr *in6_addr, - unsigned char prefixlen) { +int sd_radv_prefix_set_prefix( + sd_radv_prefix *p, + const struct in6_addr *in6_addr, + unsigned char prefixlen) { + assert_return(p, -EINVAL); assert_return(in6_addr, -EINVAL); @@ -894,8 +895,11 @@ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, const struct in6_addr *in6_addr return 0; } -int sd_radv_prefix_get_prefix(sd_radv_prefix *p, struct in6_addr *ret_in6_addr, - unsigned char *ret_prefixlen) { +int sd_radv_prefix_get_prefix( + sd_radv_prefix *p, + struct in6_addr *ret_in6_addr, + unsigned char *ret_prefixlen) { + assert_return(p, -EINVAL); assert_return(ret_in6_addr, -EINVAL); assert_return(ret_prefixlen, -EINVAL); @@ -914,8 +918,7 @@ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) { return 0; } -int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, - int address_autoconfiguration) { +int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p, int address_autoconfiguration) { assert_return(p, -EINVAL); SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, address_autoconfiguration); @@ -967,8 +970,11 @@ int sd_radv_route_prefix_new(sd_radv_route_prefix **ret) { DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_route_prefix, sd_radv_route_prefix, mfree); -int sd_radv_route_prefix_set_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr, - unsigned char prefixlen) { +int sd_radv_route_prefix_set_prefix( + sd_radv_route_prefix *p, + const struct in6_addr *in6_addr, + unsigned char prefixlen) { + assert_return(p, -EINVAL); assert_return(in6_addr, -EINVAL); diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 4b11cdbc1..7497c3ef3 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -31,13 +31,6 @@ static bool verbose = true; static int test_fd[2]; static test_callback_recv_t callback_recv; static be32_t xid; -static sd_event_source *test_hangcheck; - -static int test_dhcp_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) { - assert_not_reached(); - - return 0; -} static void test_request_basic(sd_event *e) { int r; @@ -116,7 +109,7 @@ static void test_request_anonymize(sd_event *e) { r = sd_dhcp_client_attach_event(client, e, 0); assert_se(r >= 0); - assert_se(sd_dhcp_client_set_request_option(client, SD_DHCP_OPTION_NETBIOS_NAMESERVER) == 0); + assert_se(sd_dhcp_client_set_request_option(client, SD_DHCP_OPTION_NETBIOS_NAME_SERVER) == 0); /* This PRL option is not set when using Anonymize */ assert_se(sd_dhcp_client_set_request_option(client, SD_DHCP_OPTION_HOST_NAME) == 1); assert_se(sd_dhcp_client_set_request_option(client, SD_DHCP_OPTION_PARAMETER_REQUEST_LIST) == -EINVAL); @@ -170,7 +163,7 @@ static int check_options(uint8_t code, uint8_t len, const void *option, void *us struct duid duid; size_t duid_len; - assert_se(dhcp_identifier_set_duid_en(&duid, &duid_len) >= 0); + assert_se(dhcp_identifier_set_duid_en(/* test_mode = */ true, &duid, &duid_len) >= 0); assert_se(dhcp_identifier_set_iaid(42, mac_addr, ETH_ALEN, true, /* use_mac = */ true, &iaid) >= 0); assert_se(len == sizeof(uint8_t) + sizeof(uint32_t) + duid_len); @@ -514,19 +507,15 @@ static void test_addr_acq(sd_event *e) { callback_recv = test_addr_acq_recv_discover; - assert_se(sd_event_add_time_relative( - e, &test_hangcheck, - clock_boottime_or_monotonic(), - 2 * USEC_PER_SEC, 0, - test_dhcp_hangcheck, NULL) >= 0); + assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, + 2 * USEC_PER_SEC, 0, + NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); res = sd_dhcp_client_start(client); assert_se(IN_SET(res, 0, -EINPROGRESS)); assert_se(sd_event_loop(e) >= 0); - test_hangcheck = sd_event_source_unref(test_hangcheck); - assert_se(sd_dhcp_client_set_callback(client, NULL, NULL) >= 0); assert_se(sd_dhcp_client_stop(client) >= 0); sd_dhcp_client_unref(client); diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index 800a6641e..b01b1f57a 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -89,7 +89,8 @@ static void test_message_init(void) { message = malloc0(len); assert_se(dhcp_message_init(message, BOOTREQUEST, 0x12345678, - DHCP_DISCOVER, ARPHRD_ETHER, optlen, &optoffset) >= 0); + DHCP_DISCOVER, ARPHRD_ETHER, ETH_ALEN, (uint8_t[16]){}, + optlen, &optoffset) >= 0); assert_se(message->xid == htobe32(0x12345678)); assert_se(message->op == BOOTREQUEST); diff --git a/src/libsystemd-network/test-dhcp-server.c b/src/libsystemd-network/test-dhcp-server.c index a5c9bfeaf..a19735b42 100644 --- a/src/libsystemd-network/test-dhcp-server.c +++ b/src/libsystemd-network/test-dhcp-server.c @@ -20,8 +20,9 @@ static void test_pool(struct in_addr *address, unsigned size, int ret) { assert_se(sd_dhcp_server_configure_pool(server, address, 8, 0, size) == ret); } -static int test_basic(sd_event *event, bool bind_to_interface) { +static int test_basic(bool bind_to_interface) { _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; struct in_addr address_lo = { .s_addr = htobe32(INADDR_LOOPBACK), }; @@ -30,6 +31,10 @@ static int test_basic(sd_event *event, bool bind_to_interface) { }; int r; + log_debug("/* %s(bind_to_interface=%s) */", __func__, yes_no(bind_to_interface)); + + assert_se(sd_event_new(&event) >= 0); + /* attach to loopback interface */ assert_se(sd_dhcp_server_new(&server, 1) >= 0); assert_se(server); @@ -58,7 +63,7 @@ static int test_basic(sd_event *event, bool bind_to_interface) { r = sd_dhcp_server_start(server); if (r == -EPERM) - return log_info_errno(r, "sd_dhcp_server_start failed: %m"); + return r; assert_se(r >= 0); assert_se(sd_dhcp_server_start(server) >= 0); @@ -108,9 +113,17 @@ static void test_message_handler(void) { struct in_addr address_lo = { .s_addr = htobe32(INADDR_LOOPBACK), }; + struct in_addr static_lease_address = { + .s_addr = htobe32(INADDR_LOOPBACK + 42), + }; + static uint8_t static_lease_client_id[7] = {0x01, 'A', 'B', 'C', 'D', 'E', 'G' }; + + log_debug("/* %s */", __func__); assert_se(sd_dhcp_server_new(&server, 1) >= 0); assert_se(sd_dhcp_server_configure_pool(server, &address_lo, 8, 0, 0) >= 0); + assert_se(sd_dhcp_server_set_static_lease(server, &static_lease_address, static_lease_client_id, + ELEMENTSOF(static_lease_client_id)) >= 0); assert_se(sd_dhcp_server_attach_event(server, NULL, 0) >= 0); assert_se(sd_dhcp_server_start(server) >= 0); @@ -137,12 +150,12 @@ static void test_message_handler(void) { assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); test.message.htype = 0; - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); test.message.htype = ARPHRD_ETHER; assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); test.message.hlen = 0; - assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == -EBADMSG); test.message.hlen = ETHER_ADDR_LEN; assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_OFFER); @@ -180,6 +193,26 @@ static void test_message_handler(void) { test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 30); assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); + + /* request address reserved for static lease (unmatching client ID) */ + test.option_client_id.id[6] = 'H'; + test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42); + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); + + /* request unmatching address */ + test.option_client_id.id[6] = 'G'; + test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 41); + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == 0); + + /* request matching address */ + test.option_client_id.id[6] = 'G'; + test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42); + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK); + + /* try again */ + test.option_client_id.id[6] = 'G'; + test.option_requested_ip.address = htobe32(INADDR_LOOPBACK + 42); + assert_se(dhcp_server_handle_message(server, (DHCPMessage*)&test, sizeof(test)) == DHCP_ACK); } static uint64_t client_id_hash_helper(DHCPClientId *id, uint8_t key[HASH_KEY_SIZE]) { @@ -202,6 +235,8 @@ static void test_client_id_hash(void) { '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', }; + log_debug("/* %s */", __func__); + a.data = (uint8_t*)strdup("abcd"); b.data = (uint8_t*)strdup("abcd"); @@ -227,24 +262,61 @@ static void test_client_id_hash(void) { free(b.data); } +static void test_static_lease(void) { + _cleanup_(sd_dhcp_server_unrefp) sd_dhcp_server *server = NULL; + + log_debug("/* %s */", __func__); + + assert_se(sd_dhcp_server_new(&server, 1) >= 0); + + assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020304 }, + (uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t)) >= 0); + /* Duplicated entry. */ + assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020304 }, + (uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t)) == -EEXIST); + /* Address is conflicted. */ + assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020304 }, + (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) == -EEXIST); + /* Client ID is conflicted. */ + assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020305 }, + (uint8_t*) &(uint32_t) { 0x01020304 }, sizeof(uint32_t)) == -EEXIST); + + assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020305 }, + (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) >= 0); + /* Remove the previous entry. */ + assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 }, + (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) >= 0); + /* Then, set a different address. */ + assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x01020306 }, + (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) >= 0); + /* Remove again. */ + assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 }, + (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) >= 0); + /* Try to remove non-existent entry. */ + assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 }, + (uint8_t*) &(uint32_t) { 0x01020305 }, sizeof(uint32_t)) >= 0); + /* Try to remove non-existent entry. */ + assert_se(sd_dhcp_server_set_static_lease(server, &(struct in_addr) { .s_addr = 0x00000000 }, + (uint8_t*) &(uint32_t) { 0x01020306 }, sizeof(uint32_t)) >= 0); +} + int main(int argc, char *argv[]) { - _cleanup_(sd_event_unrefp) sd_event *e; int r; test_setup_logging(LOG_DEBUG); - assert_se(sd_event_new(&e) >= 0); + test_client_id_hash(); + test_static_lease(); - r = test_basic(e, true); - if (r != 0) - return log_tests_skipped("cannot start dhcp server(bound to interface)"); + r = test_basic(true); + if (r < 0) + return log_tests_skipped_errno(r, "cannot start dhcp server(bound to interface)"); - r = test_basic(e, false); - if (r != 0) - return log_tests_skipped("cannot start dhcp server(non-bound to interface)"); + r = test_basic(false); + if (r < 0) + return log_tests_skipped_errno(r, "cannot start dhcp server(non-bound to interface)"); test_message_handler(); - test_client_id_hash(); return 0; } diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index bcd0134a8..8278a3875 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -26,36 +26,69 @@ #include "tests.h" #include "time-util.h" -static struct ether_addr mac_addr = { - .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} +#define DHCP6_CLIENT_EVENT_TEST_ADVERTISED 77 +#define IA_ID_BYTES \ + 0x0e, 0xcf, 0xa3, 0x7d +#define IA_NA_ADDRESS1_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c, 0x55, 0xad +#define IA_NA_ADDRESS2_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c, 0x55, 0xae +#define IA_PD_PREFIX1_BYTES \ + 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +#define IA_PD_PREFIX2_BYTES \ + 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +#define DNS1_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 +#define DNS2_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 +#define SNTP1_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 +#define SNTP2_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04 +#define NTP1_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05 +#define NTP2_BYTES \ + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06 +#define CLIENT_ID_BYTES \ + 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2 +#define SERVER_ID_BYTES \ + 0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53 + +static const struct in6_addr local_address = + { { { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, } } }; +static const struct in6_addr mcast_address = + IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; +static const struct in6_addr ia_na_address1 = { { { IA_NA_ADDRESS1_BYTES } } }; +static const struct in6_addr ia_na_address2 = { { { IA_NA_ADDRESS2_BYTES } } }; +static const struct in6_addr ia_pd_prefix1 = { { { IA_PD_PREFIX1_BYTES } } }; +static const struct in6_addr ia_pd_prefix2 = { { { IA_PD_PREFIX2_BYTES } } }; +static const struct in6_addr dns1 = { { { DNS1_BYTES } } }; +static const struct in6_addr dns2 = { { { DNS2_BYTES } } }; +static const struct in6_addr sntp1 = { { { SNTP1_BYTES } } }; +static const struct in6_addr sntp2 = { { { SNTP2_BYTES } } }; +static const struct in6_addr ntp1 = { { { NTP1_BYTES } } }; +static const struct in6_addr ntp2 = { { { NTP2_BYTES } } }; +static const uint8_t client_id[] = { CLIENT_ID_BYTES }; +static const uint8_t server_id[] = { SERVER_ID_BYTES }; +static const struct ether_addr mac = { + .ether_addr_octet = { 'A', 'B', 'C', '1', '2', '3' }, }; - -static sd_event_source *hangcheck; -static int test_dhcp_fd[2]; +static int test_fd[2] = { -1, -1, }; static int test_ifindex = 42; -static int test_client_message_num; -static be32_t test_iaid = 0; -static uint8_t test_duid[14] = { }; +static unsigned test_client_sent_message_count = 0; +static sd_dhcp6_client *client_ref = NULL; -static void test_client_basic(sd_event *e) { - sd_dhcp6_client *client; +TEST(client_basic) { + _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; int v; - log_debug("/* %s */", __func__); - assert_se(sd_dhcp6_client_new(&client) >= 0); assert_se(client); - assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); - assert_se(sd_dhcp6_client_set_ifindex(client, 15) == 0); - assert_se(sd_dhcp6_client_set_ifindex(client, -42) == -EINVAL); - assert_se(sd_dhcp6_client_set_ifindex(client, -1) == -EINVAL); assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0); - assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, - sizeof (mac_addr), - ARPHRD_ETHER) >= 0); + assert_se(sd_dhcp6_client_set_mac(client, mac.ether_addr_octet, sizeof(mac), ARPHRD_ETHER) >= 0); assert_se(sd_dhcp6_client_set_fqdn(client, "host") == 1); assert_se(sd_dhcp6_client_set_fqdn(client, "host.domain") == 1); @@ -107,54 +140,45 @@ static void test_client_basic(sd_event *e) { assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0); assert_se(sd_dhcp6_client_detach_event(client) >= 0); - assert_se(!sd_dhcp6_client_unref(client)); } -static void test_parse_domain(void) { +TEST(parse_domain) { + _cleanup_free_ char *domain = NULL; + _cleanup_strv_free_ char **list = NULL; uint8_t *data; - char *domain; - char **list; - int r; - - log_debug("/* %s */", __func__); data = (uint8_t []) { 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0 }; - r = dhcp6_option_parse_domainname(data, 13, &domain); - assert_se(r == 0); + assert_se(dhcp6_option_parse_domainname(data, 13, &domain) >= 0); assert_se(domain); assert_se(streq(domain, "example.com")); - free(domain); + domain = mfree(domain); data = (uint8_t []) { 4, 't', 'e', 's', 't' }; - r = dhcp6_option_parse_domainname(data, 5, &domain); - assert_se(r == 0); + assert_se(dhcp6_option_parse_domainname(data, 5, &domain) >= 0); assert_se(domain); assert_se(streq(domain, "test")); - free(domain); + domain = mfree(domain); data = (uint8_t []) { 0 }; - r = dhcp6_option_parse_domainname(data, 1, &domain); - assert_se(r < 0); + assert_se(dhcp6_option_parse_domainname(data, 1, &domain) < 0); data = (uint8_t []) { 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 6, 'f', 'o', 'o', 'b', 'a', 'r', 0 }; - r = dhcp6_option_parse_domainname_list(data, 21, &list); - assert_se(r == 0); + assert_se(dhcp6_option_parse_domainname_list(data, 21, &list) >= 0); assert_se(list); assert_se(streq(list[0], "example.com")); assert_se(streq(list[1], "foobar")); - strv_free(list); + assert_se(!list[2]); + list = strv_free(list); data = (uint8_t []) { 1, 'a', 0, 20, 'b', 'c' }; - r = dhcp6_option_parse_domainname_list(data, 6, &list); - assert_se(r < 0); + assert_se(dhcp6_option_parse_domainname_list(data, 6, &list) < 0); data = (uint8_t []) { 0 , 0 }; - r = dhcp6_option_parse_domainname_list(data, 2, &list); - assert_se(r < 0); + assert_se(dhcp6_option_parse_domainname_list(data, 2, &list) < 0); } -static void test_option(void) { +TEST(option) { uint8_t packet[] = { 'F', 'O', 'O', 'H', 'O', 'G', 'E', 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07, @@ -176,8 +200,6 @@ static void test_option(void) { uint16_t optcode; uint8_t *out; - log_debug("/* %s */", __func__); - assert_se(sizeof(packet) == sizeof(result)); offset = 0; @@ -230,7 +252,7 @@ static void test_option(void) { assert_se(memcmp(packet, result, sizeof(packet)) == 0); } -static void test_option_status(void) { +TEST(option_status) { uint8_t option1[] = { /* IA NA */ 0x00, 0x03, 0x00, 0x12, 0x1a, 0x1d, 0x1a, 0x1d, @@ -297,16 +319,13 @@ static void test_option_status(void) { /* PD prefix status option */ 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, }; + _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL; DHCP6Option *option; - DHCP6IA ia, pd; be32_t iaid; - int r = 0; - - log_debug("/* %s */", __func__); + int r; memcpy(&iaid, option1 + 4, sizeof(iaid)); - zero(ia); option = (DHCP6Option*) option1; assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len)); @@ -315,89 +334,101 @@ static void test_option_status(void) { r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r == -EINVAL); - assert_se(!ia.addresses); option->len = htobe16(17); r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r == -EBADMSG); - assert_se(!ia.addresses); option->len = htobe16(sizeof(DHCP6Option)); r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r == -EBADMSG); - assert_se(!ia.addresses); - zero(ia); option = (DHCP6Option*) option2; assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r == -ENODATA); - assert_se(!ia.addresses); - zero(ia); option = (DHCP6Option*) option3; assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r >= 0); - assert_se(ia.addresses); - dhcp6_lease_free_ia(&ia); + assert_se(ia); + assert_se(ia->addresses); + ia = dhcp6_ia_free(ia); - zero(pd); option = (DHCP6Option*) option4; assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len)); - - r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &pd); + r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r >= 0); - assert_se(pd.addresses); - assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0); - assert_se(memcmp(&pd.ia_pd.lifetime_t1, &option4[8], 4) == 0); - assert_se(memcmp(&pd.ia_pd.lifetime_t2, &option4[12], 4) == 0); - dhcp6_lease_free_ia(&pd); + assert_se(ia); + assert_se(ia->addresses); + assert_se(memcmp(&ia->header.id, &option4[4], 4) == 0); + assert_se(memcmp(&ia->header.lifetime_t1, &option4[8], 4) == 0); + assert_se(memcmp(&ia->header.lifetime_t2, &option4[12], 4) == 0); + ia = dhcp6_ia_free(ia); - zero(pd); option = (DHCP6Option*) option5; assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len)); - - r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &pd); + r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r >= 0); - assert_se(pd.addresses); - dhcp6_lease_free_ia(&pd); + assert_se(ia); + assert_se(ia->addresses); + ia = dhcp6_ia_free(ia); } -static void test_client_parse_message_issue_22099(void) { +TEST(client_parse_message_issue_22099) { static const uint8_t msg[] = { - /* xid */ - 0x07, 0x7c, 0x4c, 0x16, - /* status code (zero length) */ - 0x00, 0x0e, 0x00, 0x00, - /* NTP servers (broken sub option and sub option length) */ - 0x00, 0x38, 0x00, 0x14, 0x01, 0x00, 0x10, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e, - /* client ID */ - 0x00, 0x01, 0x00, 0x0e, 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x5c, 0x6b, 0x90, 0xec, 0xda, 0x95, - 0x15, 0x45, - /* server ID */ - 0x00, 0x02, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x01, 0xdc, 0x15, 0xc8, 0xef, 0x1e, 0x4e, + /* Message type */ + DHCP6_MESSAGE_REPLY, + /* Transaction ID */ + 0x7c, 0x4c, 0x16, + /* Rapid commit */ + 0x00, SD_DHCP6_OPTION_RAPID_COMMIT, 0x00, 0x00, + /* NTP servers */ + 0x00, SD_DHCP6_OPTION_NTP_SERVER, 0x00, 0x14, + /* NTP server (broken sub option and sub option length) */ + 0x01, 0x00, 0x10, 0x00, + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + 0x00, 0x02, /* DUID-EN */ + 0x00, 0x00, 0xab, 0x11, /* pen */ + 0x5c, 0x6b, 0x90, 0xec, 0xda, 0x95, 0x15, 0x45, /* id */ + /* Server ID */ + 0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0a, + 0x00, 0x03, /* DUID-LL */ + 0x00, 0x01, /* htype */ + 0xdc, 0x15, 0xc8, 0xef, 0x1e, 0x4e, /* haddr */ /* preference */ - 0x00, 0x07, 0x00, 0x01, 0x00, + 0x00, SD_DHCP6_OPTION_PREFERENCE, 0x00, 0x01, + 0x00, /* DNS servers */ - 0x00, 0x17, 0x00, 0x10, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x15, 0xc8, 0xff, - 0xfe, 0xef, 0x1e, 0x4e, + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, 0x00, 0x10, + 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e, /* v6 pcp server */ - 0x00, 0x56, 0x00, 0x10, 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0xde, 0x15, 0xc8, 0xff, - 0xfe, 0xef, 0x1e, 0x4e, + 0x00, SD_DHCP6_OPTION_V6_PCP_SERVER, 0x00, 0x10, + 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e, /* IA_NA */ - 0x00, 0x03, 0x00, 0x28, 0xcc, 0x59, 0x11, 0x7b, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x0b, 0x40, - /* IA_NA (iaaddr) */ - 0x00, 0x05, 0x00, 0x18, 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0x6a, 0x05, 0xca, 0xff, - 0xfe, 0xf1, 0x51, 0x53, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, 0x1c, 0x20, + 0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x28, + 0xcc, 0x59, 0x11, 0x7b, /* iaid */ + 0x00, 0x00, 0x07, 0x08, /* lifetime T1 */ + 0x00, 0x00, 0x0b, 0x40, /* lifetime T2 */ + /* IA_NA (iaaddr suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0x6a, 0x05, 0xca, 0xff, 0xfe, 0xf1, 0x51, 0x53, /* address */ + 0x00, 0x00, 0x0e, 0x10, /* preferred lifetime */ + 0x00, 0x00, 0x1c, 0x20, /* valid lifetime */ /* IA_PD */ - 0x00, 0x19, 0x00, 0x29, 0xcc, 0x59, 0x11, 0x7b, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x0b, 0x40, - /* IA_PD (iaprefix) */ - 0x00, 0x1a, 0x00, 0x19, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, 0x1c, 0x20, 0x3a, 0x2a, 0x02, 0x81, - 0x0d, 0x98, 0x80, 0x37, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x29, + 0xcc, 0x59, 0x11, 0x7b, /* iaid */ + 0x00, 0x00, 0x07, 0x08, /* lifetime T1 */ + 0x00, 0x00, 0x0b, 0x40, /* lifetime T2 */ + /* IA_PD (iaprefix suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x0e, 0x10, /* preferred lifetime */ + 0x00, 0x00, 0x1c, 0x20, /* valid lifetime */ + 0x3a, /* prefixlen */ + 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* prefix */ }; static const uint8_t duid[] = { 0x00, 0x00, 0xab, 0x11, 0x5c, 0x6b, 0x90, 0xec, 0xda, 0x95, 0x15, 0x45, @@ -405,626 +436,562 @@ static void test_client_parse_message_issue_22099(void) { _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - log_debug("/* %s */", __func__); - assert_se(sd_dhcp6_client_new(&client) >= 0); assert_se(sd_dhcp6_client_set_iaid(client, 0xcc59117b) >= 0); assert_se(sd_dhcp6_client_set_duid(client, 2, duid, sizeof(duid)) >= 0); - assert_se(dhcp6_lease_new(&lease) >= 0); - - assert_se(client_parse_message(client, (DHCP6Message*) msg, sizeof(msg), lease) >= 0); + assert_se(dhcp6_lease_new_from_message(client, (const DHCP6Message*) msg, sizeof(msg), NULL, NULL, &lease) >= 0); } -static uint8_t msg_advertise[198] = { - 0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e, - 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30, - 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03, - 0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00, - 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05, - 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, - 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c, - 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, - 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28, - 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65, - 0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65, - 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66, - 0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e, - 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68, - 0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, - 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, - 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, - 0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, - 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19, - 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, - 0x53, 0x00, 0x07, 0x00, 0x01, 0x00 +static const uint8_t msg_information_request[] = { + /* Message type */ + DHCP6_MESSAGE_INFORMATION_REQUEST, + /* Transaction ID */ + 0x0f, 0xb4, 0xe5, + /* MUD URL */ + /* ORO */ + 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x08, + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, + 0x00, SD_DHCP6_OPTION_DOMAIN_LIST, + 0x00, SD_DHCP6_OPTION_NTP_SERVER, + 0x00, SD_DHCP6_OPTION_SNTP_SERVERS, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + CLIENT_ID_BYTES, + /* Extra options */ + /* Elapsed time */ + 0x00, SD_DHCP6_OPTION_ELAPSED_TIME, 0x00, 0x02, + 0x00, 0x00, }; -static uint8_t msg_reply[191] = { - 0x07, 0xf7, 0x4e, 0x57, 0x00, 0x02, 0x00, 0x0e, - 0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53, - 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, 0x00, 0x01, - 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, - 0xf3, 0x30, 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, - 0x00, 0x03, 0x00, 0x4a, 0x0e, 0xcf, 0xa3, 0x7d, - 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, - 0x00, 0x05, 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, - 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, - 0x09, 0x3c, 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, - 0x00, 0x00, 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x1e, - 0x00, 0x00, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, - 0x77, 0x65, 0x72, 0x65, 0x20, 0x61, 0x73, 0x73, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x00, 0x17, - 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, - 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, 0x03, 0x6c, - 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61, - 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, 0x01, 0x0d, - 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x27, 0x00, - 0x0e, 0x01, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61 +static const uint8_t msg_solicit[] = { + /* Message type */ + DHCP6_MESSAGE_SOLICIT, + /* Transaction ID */ + 0x0f, 0xb4, 0xe5, + /* Rapid commit */ + 0x00, SD_DHCP6_OPTION_RAPID_COMMIT, 0x00, 0x00, + /* IA_NA */ + 0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x0c, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x00, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x00, /* lifetime T2 */ + /* IA_PD */ + 0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x0c, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x00, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x00, /* lifetime T2 */ + /* Client FQDN */ + 0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x11, + DHCP6_FQDN_FLAG_S, + 0x04, 'h', 'o', 's', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* User Class */ + /* Vendor Class */ + /* Vendor Options */ + /* MUD URL */ + /* ORO */ + 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x08, + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, + 0x00, SD_DHCP6_OPTION_DOMAIN_LIST, + 0x00, SD_DHCP6_OPTION_NTP_SERVER, + 0x00, SD_DHCP6_OPTION_SNTP_SERVERS, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + CLIENT_ID_BYTES, + /* Extra options */ + /* Elapsed time */ + 0x00, SD_DHCP6_OPTION_ELAPSED_TIME, 0x00, 0x02, + 0x00, 0x00, }; -static uint8_t fqdn_wire[16] = { - 0x04, 'h', 'o', 's', 't', 0x03, 'l', 'a', 'b', - 0x05, 'i', 'n', 't', 'r', 'a', 0x00 +static const uint8_t msg_request[] = { + /* Message type */ + DHCP6_MESSAGE_REQUEST, + /* Transaction ID */ + 0x00, 0x00, 0x00, + /* Server ID */ + 0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0e, + SERVER_ID_BYTES, + /* IA_NA */ + 0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x44, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x00, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x00, /* lifetime T2 */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS1_BYTES, + 0x00, 0x00, 0x00, 0x00, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0x00, /* valid lifetime */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS2_BYTES, + 0x00, 0x00, 0x00, 0x00, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0x00, /* valid lifetime */ + /* IA_PD */ + 0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x46, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x00, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x00, /* lifetime T2 */ + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0x00, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX1_BYTES, + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0x00, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX2_BYTES, + /* Client FQDN */ + 0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x11, + DHCP6_FQDN_FLAG_S, + 0x04, 'h', 'o', 's', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* User Class */ + /* Vendor Class */ + /* Vendor Options */ + /* MUD URL */ + /* ORO */ + 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x08, + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, + 0x00, SD_DHCP6_OPTION_DOMAIN_LIST, + 0x00, SD_DHCP6_OPTION_NTP_SERVER, + 0x00, SD_DHCP6_OPTION_SNTP_SERVERS, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + CLIENT_ID_BYTES, + /* Extra options */ + /* Elapsed time */ + 0x00, SD_DHCP6_OPTION_ELAPSED_TIME, 0x00, 0x02, + 0x00, 0x00, }; -static void test_advertise_option(sd_event *e) { - _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - DHCP6Message *advertise = (DHCP6Message *)msg_advertise; - size_t len = sizeof(msg_advertise) - sizeof(DHCP6Message), pos = 0; - uint32_t lt_pref, lt_valid; - bool opt_clientid = false; - const struct in6_addr *addrs; - uint8_t preference = 255; - struct in6_addr addr; - char **domains; - uint8_t *opt; - int r; - be32_t val; +static const uint8_t msg_reply[] = { + /* Message type */ + DHCP6_MESSAGE_REPLY, + /* Transaction ID */ + 0x0f, 0xb4, 0xe5, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + CLIENT_ID_BYTES, + /* Server ID */ + 0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0e, + SERVER_ID_BYTES, + /* Rapid commit */ + 0x00, SD_DHCP6_OPTION_RAPID_COMMIT, 0x00, 0x01, + 0x00, + /* IA_NA */ + 0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x66, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x50, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x78, /* lifetime T2 */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS2_BYTES, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS1_BYTES, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + /* IA_NA (status code suboption) */ + 0x00, SD_DHCP6_OPTION_STATUS_CODE, 0x00, 0x1e, + 0x00, 0x00, /* status code */ + 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, 0x77, 0x65, + 0x72, 0x65, 0x20, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, /* status message */ + /* IA_PD */ + 0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x46, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x50, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x78, /* lifetime T2 */ + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX2_BYTES, + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX1_BYTES, + /* DNS servers */ + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, 0x00, 0x20, + DNS1_BYTES, + DNS2_BYTES, + /* SNTP servers */ + 0x00, SD_DHCP6_OPTION_SNTP_SERVERS, 0x00, 0x20, + SNTP1_BYTES, + SNTP2_BYTES, + /* NTP servers */ + 0x00, SD_DHCP6_OPTION_NTP_SERVER, 0x00, 0x37, + /* NTP server (address suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10, + NTP1_BYTES, + /* NTP server (address suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10, + NTP2_BYTES, + /* NTP server (fqdn suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_FQDN, 0x00, 0x0b, + 0x03, 'n', 't', 'p', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* Domain list */ + 0x00, SD_DHCP6_OPTION_DOMAIN_LIST, 0x00, 0x0b, + 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* Client FQDN */ + 0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x12, + 0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', +}; +static const uint8_t msg_advertise[] = { + /* Message type */ + DHCP6_MESSAGE_ADVERTISE, + /* Transaction ID */ + 0x0f, 0xb4, 0xe5, + /* Client ID */ + 0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e, + CLIENT_ID_BYTES, + /* Server ID */ + 0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0e, + SERVER_ID_BYTES, + /* Preference */ + 0x00, SD_DHCP6_OPTION_PREFERENCE, 0x00, 0x01, + 0xff, + /* IA_NA */ + 0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x7a, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x50, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x78, /* lifetime T2 */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS2_BYTES, /* address */ + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + /* IA_NA (IAADDR suboption) */ + 0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18, + IA_NA_ADDRESS1_BYTES, /* address */ + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + /* IA_NA (status code suboption) */ + 0x00, SD_DHCP6_OPTION_STATUS_CODE, 0x00, 0x32, + 0x00, 0x00, /* status code */ + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28, 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65, + 0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66, + 0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68, /* status message */ + /* IA_PD */ + 0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x46, + IA_ID_BYTES, + 0x00, 0x00, 0x00, 0x50, /* lifetime T1 */ + 0x00, 0x00, 0x00, 0x78, /* lifetime T2 */ + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX2_BYTES, + /* IA_PD (IA_PD_PREFIX suboption) */ + 0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x96, /* preferred lifetime */ + 0x00, 0x00, 0x00, 0xb4, /* valid lifetime */ + 0x40, /* prefixlen */ + IA_PD_PREFIX1_BYTES, + /* DNS servers */ + 0x00, SD_DHCP6_OPTION_DNS_SERVERS, 0x00, 0x20, + DNS1_BYTES, + DNS2_BYTES, + /* SNTP servers */ + 0x00, SD_DHCP6_OPTION_SNTP_SERVERS, 0x00, 0x20, + SNTP1_BYTES, + SNTP2_BYTES, + /* NTP servers */ + 0x00, SD_DHCP6_OPTION_NTP_SERVER, 0x00, 0x37, + /* NTP server (address suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10, + NTP1_BYTES, + /* NTP server (address suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10, + NTP2_BYTES, + /* NTP server (fqdn suboption) */ + 0x00, DHCP6_NTP_SUBOPTION_SRV_FQDN, 0x00, 0x0b, + 0x03, 'n', 't', 'p', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* Domain list */ + 0x00, SD_DHCP6_OPTION_DOMAIN_LIST, 0x00, 0x0b, + 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00, + /* Client FQDN */ + 0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x12, + 0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', +}; + +static void test_client_verify_information_request(const DHCP6Message *msg, size_t len) { log_debug("/* %s */", __func__); - assert_se(len >= sizeof(DHCP6Message)); + assert_se(len == sizeof(msg_information_request)); + /* The elapsed time value is not deterministic. Skip it. */ + assert_se(memcmp(msg, msg_information_request, len - sizeof(be16_t)) == 0); +} - assert_se(dhcp6_lease_new(&lease) >= 0); +static void test_client_verify_solicit(const DHCP6Message *msg, size_t len) { + log_debug("/* %s */", __func__); - assert_se(advertise->type == DHCP6_MESSAGE_ADVERTISE); - assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) == 0x0fb4e5); + assert_se(len == sizeof(msg_solicit)); + /* The elapsed time value is not deterministic. Skip it. */ + assert_se(memcmp(msg, msg_solicit, len - sizeof(be16_t)) == 0); +} - while (pos < len) { - DHCP6Option *option = (DHCP6Option *)&advertise->options[pos]; - const uint16_t optcode = be16toh(option->code); - const uint16_t optlen = be16toh(option->len); - uint8_t *optval = option->data; +static void test_client_verify_request(const DHCP6Message *msg, size_t len) { + log_debug("/* %s */", __func__); - switch(optcode) { - case SD_DHCP6_OPTION_CLIENTID: - assert_se(optlen == 14); + assert_se(len == sizeof(msg_request)); + assert_se(msg->type == DHCP6_MESSAGE_REQUEST); + /* The transaction ID and elapsed time value are not deterministic. Skip them. */ + assert_se(memcmp(msg->options, msg_request + offsetof(DHCP6Message, options), len - offsetof(DHCP6Message, options) - sizeof(be16_t)) == 0); +} - opt_clientid = true; +static void test_lease_common(sd_dhcp6_client *client) { + sd_dhcp6_lease *lease; + const struct in6_addr *addrs; + const char *str; + char **strv; + uint8_t *id; + size_t len; + + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + + assert_se(dhcp6_lease_get_clientid(lease, &id, &len) >= 0); + assert_se(memcmp_nn(id, len, client_id, sizeof(client_id)) == 0); + + assert_se(sd_dhcp6_lease_get_domains(lease, &strv) == 1); + assert_se(streq(strv[0], "lab.intra")); + assert_se(!strv[1]); + + assert_se(sd_dhcp6_lease_get_fqdn(lease, &str) >= 0); + assert_se(streq(str, "client.lab.intra")); + + assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 2); + assert_se(in6_addr_equal(&addrs[0], &dns1)); + assert_se(in6_addr_equal(&addrs[1], &dns2)); + + assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 2); + assert_se(in6_addr_equal(&addrs[0], &ntp1)); + assert_se(in6_addr_equal(&addrs[1], &ntp2)); + + assert_se(sd_dhcp6_lease_get_ntp_fqdn(lease, &strv) == 1); + assert_se(streq(strv[0], "ntp.intra")); + assert_se(!strv[1]); + + assert_se(lease->sntp_count == 2); + assert_se(in6_addr_equal(&lease->sntp[0], &sntp1)); + assert_se(in6_addr_equal(&lease->sntp[1], &sntp2)); +} + +static void test_lease_managed(sd_dhcp6_client *client) { + sd_dhcp6_lease *lease; + struct in6_addr addr; + uint32_t lt_pref, lt_valid; + uint8_t *id, prefixlen; + size_t len; + + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + + assert_se(dhcp6_lease_get_serverid(lease, &id, &len) >= 0); + assert_se(memcmp_nn(id, len, server_id, sizeof(server_id)) == 0); + + sd_dhcp6_lease_reset_address_iter(lease); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_na_address1)); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_na_address2)); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); + + sd_dhcp6_lease_reset_address_iter(lease); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_na_address1)); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_na_address2)); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); + + sd_dhcp6_lease_reset_pd_prefix_iter(lease); + assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_pd_prefix1)); + assert_se(prefixlen == 64); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_pd_prefix2)); + assert_se(prefixlen == 64); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); + + sd_dhcp6_lease_reset_pd_prefix_iter(lease); + assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_pd_prefix1)); + assert_se(prefixlen == 64); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, <_pref, <_valid) >= 0); + assert_se(in6_addr_equal(&addr, &ia_pd_prefix2)); + assert_se(prefixlen == 64); + assert_se(lt_pref == 150); + assert_se(lt_valid == 180); + assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENODATA); + + test_lease_common(client); +} + +static void test_client_callback(sd_dhcp6_client *client, int event, void *userdata) { + switch (event) { + case SD_DHCP6_CLIENT_EVENT_STOP: + log_debug("/* %s (event=stop) */", __func__); + return; + + case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST: + log_debug("/* %s (event=information-request) */", __func__); + + assert_se(test_client_sent_message_count == 1); + + test_lease_common(client); + + assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0); + assert_se(sd_dhcp6_client_start(client) >= 0); + assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_advertise)->transaction_id) >= 0); + break; + + case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE: + log_debug("/* %s (event=ip-acquire) */", __func__); + + assert_se(IN_SET(test_client_sent_message_count, 3, 4)); + + test_lease_managed(client); + + switch (test_client_sent_message_count) { + case 3: + assert_se(sd_dhcp6_client_stop(client) >= 0); + assert_se(sd_dhcp6_client_start(client) >= 0); + assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_reply)->transaction_id) >= 0); break; - case SD_DHCP6_OPTION_IA_NA: { - be32_t iaid = htobe32(0x0ecfa37d); - - assert_se(optlen == 94); - assert_se(optval == &msg_advertise[26]); - assert_se(!memcmp(optval, &msg_advertise[26], optlen)); - - assert_se(!memcmp(optval, &iaid, sizeof(val))); - - val = htobe32(80); - assert_se(!memcmp(optval + 4, &val, sizeof(val))); - - val = htobe32(120); - assert_se(!memcmp(optval + 8, &val, sizeof(val))); - - assert_se(dhcp6_option_parse_ia(NULL, iaid, optcode, optlen, optval, &lease->ia) >= 0); - - break; - } - case SD_DHCP6_OPTION_SERVERID: - assert_se(optlen == 14); - assert_se(optval == &msg_advertise[179]); - assert_se(!memcmp(optval, &msg_advertise[179], optlen)); - - assert_se(dhcp6_lease_set_serverid(lease, optval, optlen) >= 0); - break; - - case SD_DHCP6_OPTION_PREFERENCE: - assert_se(optlen == 1); - assert_se(!*optval); - - assert_se(dhcp6_lease_set_preference(lease, *optval) >= 0); - break; - - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(optlen == 2); - - break; - - case SD_DHCP6_OPTION_DNS_SERVERS: - assert_se(optlen == 16); - assert_se(dhcp6_lease_add_dns(lease, optval, optlen) >= 0); - break; - - case SD_DHCP6_OPTION_DOMAIN_LIST: - assert_se(optlen == 11); - assert_se(dhcp6_lease_add_domains(lease, optval, optlen) >= 0); - break; - - case SD_DHCP6_OPTION_SNTP_SERVERS: - assert_se(optlen == 16); - assert_se(dhcp6_lease_add_sntp(lease, optval, optlen) >= 0); + case 4: + assert_se(sd_event_exit(sd_dhcp6_client_get_event(client), 0) >= 0); break; default: - break; - } - - pos += sizeof(*option) + optlen; - } - - assert_se(pos == len); - assert_se(opt_clientid); - - sd_dhcp6_lease_reset_address_iter(lease); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); - assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); - assert_se(lt_pref == 150); - assert_se(lt_valid == 180); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENOMSG); - - sd_dhcp6_lease_reset_address_iter(lease); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); - assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENOMSG); - sd_dhcp6_lease_reset_address_iter(lease); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) >= 0); - assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr))); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENOMSG); - - assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0); - assert_se(len == 14); - assert_se(!memcmp(opt, &msg_advertise[179], len)); - - assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0); - assert_se(preference == 0); - - r = sd_dhcp6_lease_get_dns(lease, &addrs); - assert_se(r == 1); - assert_se(!memcmp(addrs, &msg_advertise[124], r * 16)); - - r = sd_dhcp6_lease_get_domains(lease, &domains); - assert_se(r == 1); - assert_se(!strcmp("lab.intra", domains[0])); - assert_se(domains[1] == NULL); - - r = sd_dhcp6_lease_get_ntp_addrs(lease, &addrs); - assert_se(r == 1); - assert_se(!memcmp(addrs, &msg_advertise[159], r * 16)); -} - -static int test_check_completed_in_2_seconds(sd_event_source *s, uint64_t usec, void *userdata) { - assert_not_reached(); -} - -static void test_client_solicit_cb(sd_dhcp6_client *client, int event, - void *userdata) { - sd_event *e = userdata; - sd_dhcp6_lease *lease; - const struct in6_addr *addrs; - char **domains; - - log_debug("/* %s */", __func__); - - assert_se(e); - assert_se(event == SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE); - - assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); - - assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); - assert_se(!strcmp("lab.intra", domains[0])); - assert_se(domains[1] == NULL); - - assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); - assert_se(!memcmp(addrs, &msg_advertise[124], 16)); - - assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); - assert_se(!memcmp(addrs, &msg_advertise[159], 16)); - - assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY); - - sd_event_exit(e, 0); -} - -static void test_client_send_reply(DHCP6Message *request) { - DHCP6Message reply; - - log_debug("/* %s */", __func__); - - reply.transaction_id = request->transaction_id; - reply.type = DHCP6_MESSAGE_REPLY; - - memcpy(msg_reply, &reply.transaction_id, 4); - - memcpy(&msg_reply[26], test_duid, sizeof(test_duid)); - - memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid)); - - assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply)); -} - -static void test_client_verify_request(DHCP6Message *request, size_t len) { - _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - bool found_clientid = false, found_iana = false, found_serverid = false, - found_elapsed_time = false, found_fqdn = false; - uint32_t lt_pref, lt_valid; - struct in6_addr addr; - size_t pos = 0; - be32_t val; - - log_debug("/* %s */", __func__); - - assert_se(request->type == DHCP6_MESSAGE_REQUEST); - assert_se(dhcp6_lease_new(&lease) >= 0); - - len -= sizeof(DHCP6Message); - - while (pos < len) { - DHCP6Option *option = (DHCP6Option *)&request->options[pos]; - uint16_t optcode = be16toh(option->code); - uint16_t optlen = be16toh(option->len); - uint8_t *optval = option->data; - - switch(optcode) { - case SD_DHCP6_OPTION_CLIENTID: - assert_se(!found_clientid); - found_clientid = true; - - assert_se(!memcmp(optval, &test_duid, - sizeof(test_duid))); - - break; - - case SD_DHCP6_OPTION_IA_NA: - assert_se(!found_iana); - found_iana = true; - - assert_se(optlen == 40); - assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid))); - - /* T1 and T2 should not be set. */ - val = 0; - assert_se(!memcmp(optval + 4, &val, sizeof(val))); - assert_se(!memcmp(optval + 8, &val, sizeof(val))); - - /* Then, this should refuse all addresses. */ - assert_se(dhcp6_option_parse_ia(NULL, test_iaid, optcode, optlen, optval, &lease->ia) == -ENODATA); - - break; - - case SD_DHCP6_OPTION_SERVERID: - assert_se(!found_serverid); - found_serverid = true; - - assert_se(optlen == 14); - assert_se(!memcmp(&msg_advertise[179], optval, optlen)); - - break; - - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(!found_elapsed_time); - found_elapsed_time = true; - - assert_se(optlen == 2); - - break; - case SD_DHCP6_OPTION_CLIENT_FQDN: - assert_se(!found_fqdn); - found_fqdn = true; - - assert_se(optlen == 17); - - assert_se(optval[0] == 0x01); - assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire))); - break; - } - - pos += sizeof(*option) + optlen; - } - - assert_se(found_clientid && found_iana && found_serverid && found_elapsed_time); - - sd_dhcp6_lease_reset_address_iter(lease); - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENOMSG); -} - -static void test_client_send_advertise(DHCP6Message *solicit) { - DHCP6Message advertise; - - log_debug("/* %s */", __func__); - - advertise.transaction_id = solicit->transaction_id; - advertise.type = DHCP6_MESSAGE_ADVERTISE; - - memcpy(msg_advertise, &advertise.transaction_id, 4); - - memcpy(&msg_advertise[8], test_duid, sizeof(test_duid)); - - memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid)); - - assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise)) == sizeof(msg_advertise)); -} - -static void test_client_verify_solicit(DHCP6Message *solicit, size_t len) { - bool found_clientid = false, found_iana = false, - found_elapsed_time = false, found_fqdn = false; - size_t pos = 0; - - log_debug("/* %s */", __func__); - - assert_se(solicit->type == DHCP6_MESSAGE_SOLICIT); - - len -= sizeof(DHCP6Message); - - while (pos < len) { - DHCP6Option *option = (DHCP6Option *)&solicit->options[pos]; - uint16_t optcode = be16toh(option->code); - uint16_t optlen = be16toh(option->len); - uint8_t *optval = option->data; - - switch(optcode) { - case SD_DHCP6_OPTION_CLIENTID: - assert_se(!found_clientid); - found_clientid = true; - - assert_se(optlen == sizeof(test_duid)); - memcpy(&test_duid, optval, sizeof(test_duid)); - - break; - - case SD_DHCP6_OPTION_IA_NA: - assert_se(!found_iana); - found_iana = true; - - assert_se(optlen == 12); - - memcpy(&test_iaid, optval, sizeof(test_iaid)); - - break; - - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(!found_elapsed_time); - found_elapsed_time = true; - - assert_se(optlen == 2); - - break; - - case SD_DHCP6_OPTION_CLIENT_FQDN: - assert_se(!found_fqdn); - found_fqdn = true; - - assert_se(optlen == 17); - - assert_se(optval[0] == 0x01); - assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire))); - - break; - } - - pos += sizeof(*option) + optlen; - } - - assert_se(pos == len); - assert_se(found_clientid && found_iana && found_elapsed_time); -} - -static void test_client_information_cb(sd_dhcp6_client *client, int event, void *userdata) { - sd_event *e = userdata; - sd_dhcp6_lease *lease; - const struct in6_addr *addrs; - struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; - char **domains; - const char *fqdn; - - log_debug("/* %s */", __func__); - - assert_se(e); - assert_se(event == SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST); - - assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); - - assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1); - assert_se(!strcmp("lab.intra", domains[0])); - assert_se(domains[1] == NULL); - - assert_se(sd_dhcp6_lease_get_fqdn(lease, &fqdn) >= 0); - assert_se(streq(fqdn, "client.intra")); - - assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1); - assert_se(!memcmp(addrs, &msg_advertise[124], 16)); - - assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1); - assert_se(!memcmp(addrs, &msg_advertise[159], 16)); - - assert_se(sd_dhcp6_client_set_information_request(client, false) == -EBUSY); - assert_se(sd_dhcp6_client_set_callback(client, NULL, e) >= 0); - assert_se(sd_dhcp6_client_stop(client) >= 0); - assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0); - - assert_se(sd_dhcp6_client_set_callback(client, test_client_solicit_cb, e) >= 0); - - assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); - - assert_se(sd_dhcp6_client_start(client) >= 0); -} - -static void test_client_verify_information_request(DHCP6Message *information_request, size_t len) { - _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - size_t pos = 0; - bool found_clientid = false, found_elapsed_time = false; - struct in6_addr addr; - uint32_t lt_pref, lt_valid; - - log_debug("/* %s */", __func__); - - assert_se(information_request->type == DHCP6_MESSAGE_INFORMATION_REQUEST); - assert_se(dhcp6_lease_new(&lease) >= 0); - - len -= sizeof(DHCP6Message); - - while (pos < len) { - DHCP6Option *option = (DHCP6Option *)&information_request->options[pos]; - uint16_t optcode = be16toh(option->code); - uint16_t optlen = be16toh(option->len); - uint8_t *optval = option->data; - - switch(optcode) { - case SD_DHCP6_OPTION_CLIENTID: - assert_se(!found_clientid); - found_clientid = true; - - assert_se(optlen == sizeof(test_duid)); - memcpy(&test_duid, optval, sizeof(test_duid)); - - break; - - case SD_DHCP6_OPTION_IA_NA: - case SD_DHCP6_OPTION_SERVERID: assert_not_reached(); - - case SD_DHCP6_OPTION_ELAPSED_TIME: - assert_se(!found_elapsed_time); - found_elapsed_time = true; - - assert_se(optlen == 2); - - break; } - pos += sizeof(*option) + optlen; + break; + + case DHCP6_CLIENT_EVENT_TEST_ADVERTISED: { + sd_dhcp6_lease *lease; + uint8_t preference; + + log_debug("/* %s (event=test-advertised) */", __func__); + + assert_se(test_client_sent_message_count == 2); + + test_lease_managed(client); + + assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0); + assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0); + assert_se(preference == 0xff); + + assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_reply)->transaction_id) >= 0); + break; + } + default: + assert_not_reached(); } - - assert_se(pos == len); - assert_se(found_clientid && found_elapsed_time); - - sd_dhcp6_lease_reset_address_iter(lease); - - assert_se(sd_dhcp6_lease_get_address(lease, &addr, <_pref, <_valid) == -ENOMSG); } -int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, - const void *packet, size_t len) { - struct in6_addr mcast = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; - DHCP6Message *message; +int dhcp6_network_send_udp_socket(int s, struct in6_addr *a, const void *packet, size_t len) { + log_debug("/* %s(count=%u) */", __func__, test_client_sent_message_count); - log_debug("/* %s */", __func__); - - assert_se(s == test_dhcp_fd[0]); - assert_se(server_address); + assert_se(a); + assert_se(in6_addr_equal(a, &mcast_address)); assert_se(packet); - assert_se(len > sizeof(DHCP6Message) + 4); - assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast)); + assert_se(len >= sizeof(DHCP6Message)); - message = (DHCP6Message *)packet; + switch (test_client_sent_message_count) { + case 0: + test_client_verify_information_request(packet, len); + assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply)); + break; - assert_se(message->transaction_id & 0x00ffffff); + case 1: + test_client_verify_solicit(packet, len); + assert_se(write(test_fd[1], msg_advertise, sizeof(msg_advertise)) == sizeof(msg_advertise)); + break; - if (test_client_message_num == 0) { - test_client_verify_information_request(message, len); - test_client_send_reply(message); - test_client_message_num++; - } else if (test_client_message_num == 1) { - test_client_verify_solicit(message, len); - test_client_send_advertise(message); - test_client_message_num++; - } else if (test_client_message_num == 2) { - test_client_verify_request(message, len); - test_client_send_reply(message); - test_client_message_num++; + case 2: + test_client_callback(client_ref, DHCP6_CLIENT_EVENT_TEST_ADVERTISED, NULL); + test_client_verify_request(packet, len); + assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply)); + break; + + case 3: + test_client_verify_solicit(packet, len); + assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply)); + break; + + default: + assert_not_reached(); } + test_client_sent_message_count++; return len; } -int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) { +int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *a) { assert_se(ifindex == test_ifindex); + assert_se(a); + assert_se(in6_addr_equal(a, &local_address)); - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) < 0) - return -errno; - - return test_dhcp_fd[0]; + assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0); + return TAKE_FD(test_fd[0]); } -static void test_client_solicit(sd_event *e) { - sd_dhcp6_client *client; - struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } }; - int val; +TEST(dhcp6_client) { + _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL; + _cleanup_(sd_event_unrefp) sd_event *e = NULL; - log_debug("/* %s */", __func__); + assert_se(sd_event_new(&e) >= 0); + assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, + 2 * USEC_PER_SEC, 0, + NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); assert_se(sd_dhcp6_client_new(&client) >= 0); - assert_se(client); - assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0); - assert_se(sd_dhcp6_client_set_ifindex(client, test_ifindex) == 0); - assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr, - sizeof (mac_addr), - ARPHRD_ETHER) >= 0); - assert_se(sd_dhcp6_client_set_fqdn(client, "host.lab.intra") == 1); + assert_se(sd_dhcp6_client_set_local_address(client, &local_address) >= 0); + assert_se(sd_dhcp6_client_set_fqdn(client, "host.lab.intra") >= 0); + assert_se(sd_dhcp6_client_set_iaid(client, unaligned_read_be32((uint8_t[]) { IA_ID_BYTES })) >= 0); dhcp6_client_set_test_mode(client, true); - assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); - assert_se(val == 0); - assert_se(sd_dhcp6_client_set_information_request(client, 42) >= 0); - assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0); - assert_se(val); - - assert_se(sd_dhcp6_client_set_callback(client, - test_client_information_cb, e) >= 0); - - assert_se(sd_event_add_time_relative(e, &hangcheck, clock_boottime_or_monotonic(), - 2 * USEC_PER_SEC, 0, - test_check_completed_in_2_seconds, NULL) >= 0); - - assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); + assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0); + assert_se(sd_dhcp6_client_set_callback(client, test_client_callback, NULL) >= 0); assert_se(sd_dhcp6_client_start(client) >= 0); - sd_event_loop(e); + assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_reply)->transaction_id) >= 0); - hangcheck = sd_event_source_unref(hangcheck); + assert_se(client_ref = sd_dhcp6_client_ref(client)); - assert_se(!sd_dhcp6_client_unref(client)); + assert_se(sd_event_loop(e) >= 0); - test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]); + assert_se(test_client_sent_message_count == 4); + + assert_se(!sd_dhcp6_client_unref(client_ref)); + test_fd[1] = safe_close(test_fd[1]); } -int main(int argc, char *argv[]) { - _cleanup_(sd_event_unrefp) sd_event *e; - - assert_se(sd_event_new(&e) >= 0); - - test_setup_logging(LOG_DEBUG); - - test_client_basic(e); - test_parse_domain(); - test_option(); - test_option_status(); - test_client_parse_message_issue_22099(); - test_advertise_option(e); - test_client_solicit(e); - - return 0; -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index 7b961f476..d4f8cf89b 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -51,7 +51,6 @@ static uint8_t advertisement[] = { 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; -static sd_event_source *test_hangcheck; static bool test_stopped; static int test_fd[2]; static sd_event_source *recv_router_advertisement; @@ -101,18 +100,9 @@ static const struct in6_addr test_rdnss = { { { 0x20, 0x01, 0x0d, 0xb8, static const char *test_dnssl[] = { "lab.intra", NULL }; -static int test_rs_hangcheck(sd_event_source *s, uint64_t usec, - void *userdata) { - assert_se(false); - - return 0; -} - -static void test_radv_prefix(void) { +TEST(radv_prefix) { sd_radv_prefix *p; - printf("* %s\n", __func__); - assert_se(sd_radv_prefix_new(&p) >= 0); assert_se(sd_radv_prefix_set_onlink(NULL, true) < 0); @@ -150,11 +140,9 @@ static void test_radv_prefix(void) { assert_se(!p); } -static void test_radv(void) { +TEST(radv) { sd_radv *ra; - printf("* %s\n", __func__); - assert_se(sd_radv_new(&ra) >= 0); assert_se(ra); @@ -292,13 +280,11 @@ static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdat return 0; } -static void test_ra(void) { +TEST(ra) { sd_event *e; sd_radv *ra; unsigned i; - printf("* %s\n", __func__); - assert_se(socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0); assert_se(sd_event_new(&e) >= 0); @@ -341,16 +327,13 @@ static void test_ra(void) { assert_se(sd_event_add_io(e, &recv_router_advertisement, test_fd[0], EPOLLIN, radv_recv, ra) >= 0); - assert_se(sd_event_add_time_relative( - e, &test_hangcheck, clock_boottime_or_monotonic(), - 2 *USEC_PER_SEC, 0, - test_rs_hangcheck, NULL) >= 0); + assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, + 2 * USEC_PER_SEC, 0, + NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); assert_se(sd_radv_start(ra) >= 0); - sd_event_loop(e); - - test_hangcheck = sd_event_source_unref(test_hangcheck); + assert_se(sd_event_loop(e) >= 0); ra = sd_radv_unref(ra); assert_se(!ra); @@ -360,14 +343,4 @@ static void test_ra(void) { sd_event_unref(e); } -int main(int argc, char *argv[]) { - - test_setup_logging(LOG_DEBUG); - - test_radv_prefix(); - test_radv(); - test_ra(); - - printf("* done\n"); - return 0; -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c index 1366b4411..13db49ea4 100644 --- a/src/libsystemd-network/test-ndisc-rs.c +++ b/src/libsystemd-network/test-ndisc-rs.c @@ -22,7 +22,6 @@ static struct ether_addr mac_addr = { }; static bool verbose = false; -static sd_event_source *test_hangcheck; static int test_fd[2]; static sd_ndisc *test_timeout_nd; @@ -166,13 +165,6 @@ static void router_dump(sd_ndisc_router *rt) { } } -static int test_rs_hangcheck(sd_event_source *s, uint64_t usec, - void *userdata) { - assert_se(false); - - return 0; -} - int icmp6_bind_router_solicitation(int ifindex) { assert_se(ifindex == 42); @@ -265,13 +257,10 @@ static void test_callback(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router sd_event_exit(e, 0); } -static void test_rs(void) { +TEST(rs) { sd_event *e; sd_ndisc *nd; - if (verbose) - printf("* %s\n", __func__); - send_ra_function = send_ra; assert_se(sd_event_new(&e) >= 0); @@ -285,10 +274,9 @@ static void test_rs(void) { assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); assert_se(sd_ndisc_set_callback(nd, test_callback, e) >= 0); - assert_se(sd_event_add_time_relative( - e, &test_hangcheck, clock_boottime_or_monotonic(), - 30 * USEC_PER_SEC, 0, - test_rs_hangcheck, NULL) >= 0); + assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, + 30 * USEC_PER_SEC, 0, + NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); assert_se(sd_ndisc_stop(nd) >= 0); assert_se(sd_ndisc_start(nd) >= 0); @@ -297,9 +285,7 @@ static void test_rs(void) { assert_se(sd_ndisc_start(nd) >= 0); - sd_event_loop(e); - - test_hangcheck = sd_event_source_unref(test_hangcheck); + assert_se(sd_event_loop(e) >= 0); nd = sd_ndisc_unref(nd); assert_se(!nd); @@ -358,13 +344,10 @@ static int test_timeout_value(uint8_t flags) { return 0; } -static void test_timeout(void) { +TEST(timeout) { sd_event *e; sd_ndisc *nd; - if (verbose) - printf("* %s\n", __func__); - send_ra_function = test_timeout_value; assert_se(sd_event_new(&e) >= 0); @@ -379,28 +362,17 @@ static void test_timeout(void) { assert_se(sd_ndisc_set_ifindex(nd, 42) >= 0); assert_se(sd_ndisc_set_mac(nd, &mac_addr) >= 0); - assert_se(sd_event_add_time_relative( - e, &test_hangcheck, clock_boottime_or_monotonic(), - 30 * USEC_PER_SEC, 0, - test_rs_hangcheck, NULL) >= 0); + assert_se(sd_event_add_time_relative(e, NULL, CLOCK_BOOTTIME, + 30 * USEC_PER_SEC, 0, + NULL, INT_TO_PTR(-ETIMEDOUT)) >= 0); assert_se(sd_ndisc_start(nd) >= 0); - sd_event_loop(e); - - test_hangcheck = sd_event_source_unref(test_hangcheck); + assert_se(sd_event_loop(e) >= 0); nd = sd_ndisc_unref(nd); sd_event_unref(e); } -int main(int argc, char *argv[]) { - - test_setup_logging(LOG_DEBUG); - - test_rs(); - test_timeout(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/libsystemd-network/test-sd-dhcp-lease.c b/src/libsystemd-network/test-sd-dhcp-lease.c index 9f1322688..3eea1b0d9 100644 --- a/src/libsystemd-network/test-sd-dhcp-lease.c +++ b/src/libsystemd-network/test-sd-dhcp-lease.c @@ -6,13 +6,14 @@ #include "macro.h" #include "string-util.h" #include "strv.h" +#include "tests.h" /* According to RFC1035 section 4.1.4, a domain name in a message can be either: * - a sequence of labels ending in a zero octet * - a pointer * - a sequence of labels ending with a pointer */ -static void test_dhcp_lease_parse_search_domains_basic(void) { +TEST(dhcp_lease_parse_search_domains_basic) { int r; _cleanup_strv_free_ char **domains = NULL; static const uint8_t optionbuf[] = { @@ -26,7 +27,7 @@ static void test_dhcp_lease_parse_search_domains_basic(void) { assert_se(streq(domains[1], "ABCD.EFG")); } -static void test_dhcp_lease_parse_search_domains_ptr(void) { +TEST(dhcp_lease_parse_search_domains_ptr) { int r; _cleanup_strv_free_ char **domains = NULL; static const uint8_t optionbuf[] = { @@ -39,7 +40,7 @@ static void test_dhcp_lease_parse_search_domains_ptr(void) { assert_se(streq(domains[1], "FOO")); } -static void test_dhcp_lease_parse_search_domains_labels_and_ptr(void) { +TEST(dhcp_lease_parse_search_domains_labels_and_ptr) { int r; _cleanup_strv_free_ char **domains = NULL; static const uint8_t optionbuf[] = { @@ -55,7 +56,7 @@ static void test_dhcp_lease_parse_search_domains_labels_and_ptr(void) { /* Tests for exceptions. */ -static void test_dhcp_lease_parse_search_domains_no_data(void) { +TEST(dhcp_lease_parse_search_domains_no_data) { _cleanup_strv_free_ char **domains = NULL; static const uint8_t optionbuf[3] = {0, 0, 0}; @@ -63,7 +64,7 @@ static void test_dhcp_lease_parse_search_domains_no_data(void) { assert_se(dhcp_lease_parse_search_domains(optionbuf, 0, &domains) == -ENODATA); } -static void test_dhcp_lease_parse_search_domains_loops(void) { +TEST(dhcp_lease_parse_search_domains_loops) { _cleanup_strv_free_ char **domains = NULL; static const uint8_t optionbuf[] = { 0x03, 'F', 'O', 'O', 0x00, 0x03, 'B', 'A', 'R', 0xC0, 0x06, @@ -72,7 +73,7 @@ static void test_dhcp_lease_parse_search_domains_loops(void) { assert_se(dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf), &domains) == -EBADMSG); } -static void test_dhcp_lease_parse_search_domains_wrong_len(void) { +TEST(dhcp_lease_parse_search_domains_wrong_len) { _cleanup_strv_free_ char **domains = NULL; static const uint8_t optionbuf[] = { 0x03, 'F', 'O', 'O', 0x03, 'B', 'A', 'R', 0x00, @@ -82,12 +83,4 @@ static void test_dhcp_lease_parse_search_domains_wrong_len(void) { assert_se(dhcp_lease_parse_search_domains(optionbuf, sizeof(optionbuf) - 5, &domains) == -EBADMSG); } -int main(int argc, char *argv[]) { - test_dhcp_lease_parse_search_domains_basic(); - test_dhcp_lease_parse_search_domains_ptr(); - test_dhcp_lease_parse_search_domains_labels_and_ptr(); - test_dhcp_lease_parse_search_domains_no_data(); - test_dhcp_lease_parse_search_domains_loops(); - test_dhcp_lease_parse_search_domains_wrong_len(); - return 0; -} +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/libsystemd/libsystemd.sym b/src/libsystemd/libsystemd.sym index 2178668d1..2773d28b4 100644 --- a/src/libsystemd/libsystemd.sym +++ b/src/libsystemd/libsystemd.sym @@ -769,3 +769,8 @@ global: sd_event_add_inotify_fd; sd_event_source_set_ratelimit_expire_callback; } LIBSYSTEMD_249; + +LIBSYSTEMD_251 { +global: + sd_id128_to_uuid_string; +} LIBSYSTEMD_250; diff --git a/src/libsystemd/meson.build b/src/libsystemd/meson.build index c21841258..67b439a5d 100644 --- a/src/libsystemd/meson.build +++ b/src/libsystemd/meson.build @@ -193,43 +193,43 @@ custom_target( ############################################################ tests += [ - [['src/libsystemd/sd-journal/test-journal-send.c']], + [files('sd-journal/test-journal-send.c')], - [['src/libsystemd/sd-journal/test-journal-match.c']], + [files('sd-journal/test-journal-match.c')], - [['src/libsystemd/sd-journal/test-journal-enum.c'], + [files('sd-journal/test-journal-enum.c'), [], [], [], '', 'timeout=360'], - [['src/libsystemd/sd-journal/test-journal-init.c']], + [files('sd-journal/test-journal-init.c')], - [['src/libsystemd/sd-journal/test-mmap-cache.c']], + [files('sd-journal/test-mmap-cache.c')], - [['src/libsystemd/sd-journal/test-catalog.c']], + [files('sd-journal/test-catalog.c')], - [['src/libsystemd/sd-journal/test-compress.c'], + [files('sd-journal/test-compress.c'), [], [liblz4, libzstd, libxz]], - [['src/libsystemd/sd-journal/test-compress-benchmark.c'], + [files('sd-journal/test-compress-benchmark.c'), [], [liblz4, libzstd, libxz], [], '', 'timeout=90'], - [['src/libsystemd/sd-journal/test-audit-type.c']], + [files('sd-journal/test-audit-type.c')], ] ############################################################ tests += [ - [['src/libsystemd/sd-bus/test-bus-address.c'], + [files('sd-bus/test-bus-address.c'), [], [threads]], - [['src/libsystemd/sd-bus/test-bus-marshal.c'], + [files('sd-bus/test-bus-marshal.c'), [], [threads, libglib, @@ -237,89 +237,89 @@ tests += [ libgio, libdbus]], - [['src/libsystemd/sd-bus/test-bus-signature.c'], + [files('sd-bus/test-bus-signature.c'), [], [threads]], - [['src/libsystemd/sd-bus/test-bus-queue-ref-cycle.c'], + [files('sd-bus/test-bus-queue-ref-cycle.c'), [], [threads]], - [['src/libsystemd/sd-bus/test-bus-watch-bind.c'], + [files('sd-bus/test-bus-watch-bind.c'), [], [threads], [], '', 'timeout=120'], - [['src/libsystemd/sd-bus/test-bus-chat.c'], + [files('sd-bus/test-bus-chat.c'), [], [threads]], - [['src/libsystemd/sd-bus/test-bus-cleanup.c'], + [files('sd-bus/test-bus-cleanup.c'), [], [threads, libseccomp]], - [['src/libsystemd/sd-bus/test-bus-track.c'], + [files('sd-bus/test-bus-track.c'), [], [libseccomp]], - [['src/libsystemd/sd-bus/test-bus-server.c'], + [files('sd-bus/test-bus-server.c'), [], [threads]], - [['src/libsystemd/sd-bus/test-bus-objects.c'], + [files('sd-bus/test-bus-objects.c'), [], [threads]], - [['src/libsystemd/sd-bus/test-bus-vtable.c', - 'src/libsystemd/sd-bus/test-vtable-data.h']], + [files('sd-bus/test-bus-vtable.c', + 'sd-bus/test-vtable-data.h')], - [['src/libsystemd/sd-bus/test-bus-gvariant.c'], + [files('sd-bus/test-bus-gvariant.c'), [], [libglib, libgobject, libgio]], - [['src/libsystemd/sd-bus/test-bus-creds.c']], + [files('sd-bus/test-bus-creds.c')], - [['src/libsystemd/sd-bus/test-bus-match.c']], + [files('sd-bus/test-bus-match.c')], - [['src/libsystemd/sd-bus/test-bus-benchmark.c'], + [files('sd-bus/test-bus-benchmark.c'), [], [threads], [], '', 'manual'], - [['src/libsystemd/sd-bus/test-bus-introspect.c', - 'src/libsystemd/sd-bus/test-vtable-data.h']], + [files('sd-bus/test-bus-introspect.c', + 'sd-bus/test-vtable-data.h')], - [['src/libsystemd/sd-event/test-event.c']], + [files('sd-event/test-event.c')], - [['src/libsystemd/sd-netlink/test-netlink.c']], + [files('sd-netlink/test-netlink.c')], - [['src/libsystemd/sd-resolve/test-resolve.c'], + [files('sd-resolve/test-resolve.c'), [], [threads], [], '', 'timeout=120'], - [['src/libsystemd/sd-login/test-login.c']], + [files('sd-login/test-login.c')], - [['src/libsystemd/sd-device/test-sd-device.c']], + [files('sd-device/test-sd-device.c')], - [['src/libsystemd/sd-device/test-device-util.c']], + [files('sd-device/test-device-util.c')], - [['src/libsystemd/sd-device/test-sd-device-monitor.c']], + [files('sd-device/test-sd-device-monitor.c')], ] if cxx_cmd != '' tests += [ - [['src/libsystemd/sd-bus/test-bus-vtable-cc.cc']], + [files('sd-bus/test-bus-vtable-cc.cc')], ] endif ############################################################ fuzzers += [ - [['src/libsystemd/sd-bus/fuzz-bus-message.c']], + [files('sd-bus/fuzz-bus-message.c')], - [['src/libsystemd/sd-bus/fuzz-bus-match.c']], + [files('sd-bus/fuzz-bus-match.c')], ] diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c index 0a4b3cbf6..050eec3a7 100644 --- a/src/libsystemd/sd-bus/bus-dump.c +++ b/src/libsystemd/sd-bus/bus-dump.c @@ -13,6 +13,7 @@ #include "format-util.h" #include "glyph-util.h" #include "macro.h" +#include "pcapng.h" #include "string-util.h" #include "strv.h" #include "terminal-util.h" @@ -412,8 +413,6 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { r = sd_bus_creds_get_cmdline(c, &cmdline); if (r >= 0) { - char **i; - fprintf(f, "%sCommandLine=%s", prefix, color); STRV_FOREACH(i, cmdline) { if (i != cmdline) @@ -478,8 +477,6 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { fprintf(f, "%sUniqueName=%s%s%s", prefix, color, c->unique_name, suffix); if (sd_bus_creds_get_well_known_names(c, &well_known) >= 0) { - char **i; - fprintf(f, "%sWellKnownNames=%s", prefix, color); STRV_FOREACH(i, well_known) { if (i != well_known) @@ -502,57 +499,99 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse) { return 0; } -/* - * For details about the file format, see: - * - * http://wiki.wireshark.org/Development/LibpcapFileFormat - */ +static uint16_t pcapng_optlen(size_t len) { + return ALIGN4(len + sizeof(struct pcapng_option)); +} -typedef struct _packed_ pcap_hdr_s { - uint32_t magic_number; /* magic number */ - uint16_t version_major; /* major version number */ - uint16_t version_minor; /* minor version number */ - int32_t thiszone; /* GMT to local correction */ - uint32_t sigfigs; /* accuracy of timestamps */ - uint32_t snaplen; /* max length of captured packets, in octets */ - uint32_t network; /* data link type */ -} pcap_hdr_t ; - -typedef struct _packed_ pcaprec_hdr_s { - uint32_t ts_sec; /* timestamp seconds */ - uint32_t ts_usec; /* timestamp microseconds */ - uint32_t incl_len; /* number of octets of packet saved in file */ - uint32_t orig_len; /* actual length of packet */ -} pcaprec_hdr_t; - -int bus_pcap_header(size_t snaplen, FILE *f) { - - pcap_hdr_t hdr = { - .magic_number = 0xa1b2c3d4U, - .version_major = 2, - .version_minor = 4, - .thiszone = 0, /* UTC */ - .sigfigs = 0, - .network = 231, /* D-Bus */ +static void pcapng_putopt(FILE *f, uint16_t code, const void *data, size_t len) { + struct pcapng_option opt = { + .code = code, + .length = len, }; - if (!f) - f = stdout; + assert(f); + assert((uint16_t) len == len); + assert(data || len == 0); + fwrite(&opt, 1, sizeof(opt), f); + if (len > 0) { + size_t pad = ALIGN4(len) - len; + + fwrite(data, 1, len, f); + + assert(pad < sizeof(uint32_t)); + while (pad-- > 0) + fputc('\0', f); + } +} + +static void pcapng_section_header(FILE *f, const char *os, const char *app) { + uint32_t len; + + assert(f); + + /* determine length of section header and options */ + len = sizeof(struct pcapng_section); + if (os) + len += pcapng_optlen(strlen(os)); + if (app) + len += pcapng_optlen(strlen(app)); + len += pcapng_optlen(0); /* OPT_END */ + len += sizeof(uint32_t); /* trailer length */ + + struct pcapng_section hdr = { + .block_type = PCAPNG_SECTION_BLOCK, + .block_length = len, + .byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC, + .major_version = PCAPNG_MAJOR_VERS, + .minor_version = PCAPNG_MINOR_VERS, + .section_length = UINT64_MAX, + }; + + fwrite(&hdr, 1, sizeof(hdr), f); + if (os) + pcapng_putopt(f, PCAPNG_SHB_OS, os, strlen(os)); + if (app) + pcapng_putopt(f, PCAPNG_SHB_USERAPPL, app, strlen(app)); + pcapng_putopt(f, PCAPNG_OPT_END, NULL, 0); + fwrite(&len, 1, sizeof(uint32_t), f); +} + +/* Only have a single instance of dbus pseudo interface */ +static void pcapng_interface_header(FILE *f, size_t snaplen) { + uint32_t len; + + assert(f); assert(snaplen > 0); assert((size_t) (uint32_t) snaplen == snaplen); - hdr.snaplen = (uint32_t) snaplen; + /* no options (yet) */ + len = sizeof(struct pcapng_interface_block) + sizeof(uint32_t); + struct pcapng_interface_block hdr = { + .block_type = PCAPNG_INTERFACE_BLOCK, + .block_length = len, + .link_type = 231, /* D-Bus */ + .snap_len = snaplen, + }; fwrite(&hdr, 1, sizeof(hdr), f); + fwrite(&len, 1, sizeof(uint32_t), f); +} +int bus_pcap_header(size_t snaplen, const char *os, const char *info, FILE *f) { + if (!f) + f = stdout; + + pcapng_section_header(f, os, info); + pcapng_interface_header(f, snaplen); return fflush_and_check(f); } int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) { struct bus_body_part *part; - pcaprec_hdr_t hdr = {}; - struct timeval tv; + size_t msglen, caplen, pad; + uint32_t length; + uint64_t ts; unsigned i; size_t w; @@ -563,18 +602,27 @@ int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) { assert(snaplen > 0); assert((size_t) (uint32_t) snaplen == snaplen); - if (m->realtime != 0) - timeval_store(&tv, m->realtime); - else - assert_se(gettimeofday(&tv, NULL) >= 0); + ts = m->realtime ?: now(CLOCK_REALTIME); + msglen = BUS_MESSAGE_SIZE(m); + caplen = MIN(msglen, snaplen); + pad = ALIGN4(caplen) - caplen; - hdr.ts_sec = tv.tv_sec; - hdr.ts_usec = tv.tv_usec; - hdr.orig_len = BUS_MESSAGE_SIZE(m); - hdr.incl_len = MIN(hdr.orig_len, snaplen); + /* packet block has no options */ + length = sizeof(struct pcapng_enhance_packet_block) + + caplen + pad + sizeof(uint32_t); - /* write the pcap header */ - fwrite(&hdr, 1, sizeof(hdr), f); + struct pcapng_enhance_packet_block epb = { + .block_type = PCAPNG_ENHANCED_PACKET_BLOCK, + .block_length = length, + .interface_id = 0, + .timestamp_hi = (uint32_t)(ts >> 32), + .timestamp_lo = (uint32_t)ts, + .original_length = msglen, + .capture_length = caplen, + }; + + /* write the pcapng enhanced packet block header */ + fwrite(&epb, 1, sizeof(epb), f); /* write the dbus header */ w = MIN(BUS_MESSAGE_BODY_BEGIN(m), snaplen); @@ -591,5 +639,11 @@ int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f) { snaplen -= w; } + while (pad-- > 0) + fputc('\0', f); + + /* trailing block length */ + fwrite(&length, 1, sizeof(uint32_t), f); + return fflush_and_check(f); } diff --git a/src/libsystemd/sd-bus/bus-dump.h b/src/libsystemd/sd-bus/bus-dump.h index aeb4616e9..e7470ba68 100644 --- a/src/libsystemd/sd-bus/bus-dump.h +++ b/src/libsystemd/sd-bus/bus-dump.h @@ -8,5 +8,5 @@ int bus_creds_dump(sd_bus_creds *c, FILE *f, bool terse); -int bus_pcap_header(size_t snaplen, FILE *f); +int bus_pcap_header(size_t snaplen, const char *os, const char *app, FILE *f); int bus_message_pcap_frame(sd_bus_message *m, size_t snaplen, FILE *f); diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c index b9ef6af63..eed0dae82 100644 --- a/src/libsystemd/sd-bus/bus-introspect.c +++ b/src/libsystemd/sd-bus/bus-introspect.c @@ -110,7 +110,7 @@ static int set_interface_name(struct introspect *intro, const char *interface_na return free_and_strdup(&intro->interface_name, interface_name); } -int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix) { +int introspect_write_child_nodes(struct introspect *i, OrderedSet *s, const char *prefix) { char *node; assert(i); @@ -118,7 +118,7 @@ int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefi assert_se(set_interface_name(i, NULL) >= 0); - while ((node = set_steal_first(s))) { + while ((node = ordered_set_steal_first(s))) { const char *e; e = object_path_startswith(node, prefix); diff --git a/src/libsystemd/sd-bus/bus-introspect.h b/src/libsystemd/sd-bus/bus-introspect.h index 34f32a4cf..19e3ef09e 100644 --- a/src/libsystemd/sd-bus/bus-introspect.h +++ b/src/libsystemd/sd-bus/bus-introspect.h @@ -5,7 +5,7 @@ #include "sd-bus.h" -#include "set.h" +#include "ordered-set.h" struct introspect { FILE *f; @@ -17,7 +17,7 @@ struct introspect { int introspect_begin(struct introspect *i, bool trusted); int introspect_write_default_interfaces(struct introspect *i, bool object_manager); -int introspect_write_child_nodes(struct introspect *i, Set *s, const char *prefix); +int introspect_write_child_nodes(struct introspect *i, OrderedSet *s, const char *prefix); int introspect_write_interface( struct introspect *i, const char *interface_name, diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c index 12a50845d..d4da60717 100644 --- a/src/libsystemd/sd-bus/bus-match.c +++ b/src/libsystemd/sd-bus/bus-match.c @@ -139,8 +139,6 @@ static bool value_node_test( return true; if (m->creds.mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) { - char **i; - /* on kdbus we have the well known names list * in the credentials, let's make use of that * for an accurate match */ @@ -174,8 +172,6 @@ static bool value_node_test( return false; case BUS_MATCH_ARG_HAS ... BUS_MATCH_ARG_HAS_LAST: { - char **i; - STRV_FOREACH(i, value_strv) if (streq_ptr(node->value.str, *i)) return true; @@ -386,8 +382,6 @@ int bus_match_run( if (test_str) found = hashmap_get(node->compare.children, test_str); else if (test_strv) { - char **i; - STRV_FOREACH(i, test_strv) { found = hashmap_get(node->compare.children, *i); if (found) { diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index 96529b422..b77372c3a 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -2815,7 +2815,6 @@ _public_ int sd_bus_message_append_string_memfd( } _public_ int sd_bus_message_append_strv(sd_bus_message *m, char **l) { - char **i; int r; assert_return(m, -EINVAL); diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 28d833671..e06a05db4 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -9,7 +9,6 @@ #include "bus-slot.h" #include "bus-type.h" #include "missing_capability.h" -#include "set.h" #include "string-util.h" #include "strv.h" @@ -99,10 +98,9 @@ static int add_enumerated_to_set( sd_bus *bus, const char *prefix, struct node_enumerator *first, - Set *s, + OrderedSet *s, sd_bus_error *error) { - struct node_enumerator *c; int r; assert(bus); @@ -110,7 +108,7 @@ static int add_enumerated_to_set( assert(s); LIST_FOREACH(enumerators, c, first) { - char **children = NULL, **k; + char **children = NULL; sd_bus_slot *slot; if (bus->nodes_modified) @@ -146,7 +144,7 @@ static int add_enumerated_to_set( continue; } - r = set_consume(s, *k); + r = ordered_set_consume(s, *k); if (r == -EEXIST) r = 0; } @@ -171,10 +169,9 @@ static int add_subtree_to_set( const char *prefix, struct node *n, unsigned flags, - Set *s, + OrderedSet *s, sd_bus_error *error) { - struct node *i; int r; assert(bus); @@ -198,7 +195,7 @@ static int add_subtree_to_set( if (!t) return -ENOMEM; - r = set_consume(s, t); + r = ordered_set_consume(s, t); if (r < 0 && r != -EEXIST) return r; @@ -220,28 +217,26 @@ static int get_child_nodes( const char *prefix, struct node *n, unsigned flags, - Set **_s, + OrderedSet **ret, sd_bus_error *error) { - Set *s = NULL; + _cleanup_ordered_set_free_free_ OrderedSet *s = NULL; int r; assert(bus); assert(prefix); assert(n); - assert(_s); + assert(ret); - s = set_new(&string_hash_ops); + s = ordered_set_new(&string_hash_ops); if (!s) return -ENOMEM; r = add_subtree_to_set(bus, prefix, n, flags, s, error); - if (r < 0) { - set_free_free(s); + if (r < 0) return r; - } - *_s = s; + *ret = TAKE_PTR(s); return 0; } @@ -252,7 +247,6 @@ static int node_callbacks_run( bool require_fallback, bool *found_object) { - struct node_callback *c; int r; assert(bus); @@ -808,7 +802,6 @@ static int property_get_all_callbacks_run( bool *found_object) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - struct node_vtable *c; bool found_interface; int r; @@ -888,8 +881,6 @@ static int bus_node_exists( const char *path, bool require_fallback) { - struct node_vtable *c; - struct node_callback *k; int r; assert(bus); @@ -935,9 +926,8 @@ int introspect_path( char **ret, sd_bus_error *error) { - _cleanup_set_free_free_ Set *s = NULL; + _cleanup_ordered_set_free_ OrderedSet *s = NULL; _cleanup_(introspect_free) struct introspect intro = {}; - struct node_vtable *c; bool empty; int r; @@ -961,7 +951,7 @@ int introspect_path( if (r < 0) return r; - empty = set_isempty(s); + empty = ordered_set_isempty(s); LIST_FOREACH(vtables, c, n->vtables) { if (require_fallback && !c->is_fallback) @@ -1058,7 +1048,6 @@ static int object_manager_serialize_path( const char *previous_interface = NULL; bool found_something = false; - struct node_vtable *i; struct node *n; int r; @@ -1231,7 +1220,7 @@ static int process_get_managed_objects( _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_set_free_free_ Set *s = NULL; + _cleanup_ordered_set_free_free_ OrderedSet *s = NULL; char *path; int r; @@ -1261,7 +1250,7 @@ static int process_get_managed_objects( if (r < 0) return r; - SET_FOREACH(path, s) { + ORDERED_SET_FOREACH(path, s) { r = object_manager_serialize_path_and_fallbacks(bus, reply, path, &error); if (r < 0) return bus_maybe_reply_error(m, r, &error); @@ -1482,7 +1471,7 @@ int bus_process_object(sd_bus *bus, sd_bus_message *m) { return 1; } -static struct node *bus_node_allocate(sd_bus *bus, const char *path) { +static struct node* bus_node_allocate(sd_bus *bus, const char *path) { struct node *n, *parent; const char *e; _cleanup_free_ char *s = NULL; @@ -1508,8 +1497,7 @@ static struct node *bus_node_allocate(sd_bus *bus, const char *path) { if (streq(path, "/")) parent = NULL; else { - e = strrchr(path, '/'); - assert(e); + assert_se(e = strrchr(path, '/')); p = strndupa_safe(path, MAX(1, e - path)); @@ -1788,7 +1776,7 @@ static int add_object_vtable_internal( void *userdata) { sd_bus_slot *s = NULL; - struct node_vtable *i, *existing = NULL; + struct node_vtable *existing = NULL; const sd_bus_vtable *v; struct node *n; int r; @@ -2069,9 +2057,7 @@ static int emit_properties_changed_on_interface( _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; bool has_invalidating = false, has_changing = false; struct vtable_member key = {}; - struct node_vtable *c; struct node *n; - char **property; void *u = NULL; int r; @@ -2350,13 +2336,12 @@ _public_ int sd_bus_emit_properties_changed( static int object_added_append_all_prefix( sd_bus *bus, sd_bus_message *m, - Set *s, + OrderedSet *s, const char *prefix, const char *path, bool require_fallback) { const char *previous_interface = NULL; - struct node_vtable *c; struct node *n; int r; @@ -2390,10 +2375,10 @@ static int object_added_append_all_prefix( * skip it on any of its parents. The child vtables * always fully override any conflicting vtables of * any parent node. */ - if (set_get(s, c->interface)) + if (ordered_set_get(s, c->interface)) continue; - r = set_put(s, c->interface); + r = ordered_set_put(s, c->interface); if (r < 0) return r; @@ -2439,7 +2424,7 @@ static int object_added_append_all_prefix( } static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { - _cleanup_set_free_ Set *s = NULL; + _cleanup_ordered_set_free_ OrderedSet *s = NULL; _cleanup_free_ char *prefix = NULL; size_t pl; int r; @@ -2463,7 +2448,7 @@ static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *p * a parent that were overwritten by a child. */ - s = set_new(&string_hash_ops); + s = ordered_set_new(&string_hash_ops); if (!s) return -ENOMEM; @@ -2570,13 +2555,12 @@ _public_ int sd_bus_emit_object_added(sd_bus *bus, const char *path) { static int object_removed_append_all_prefix( sd_bus *bus, sd_bus_message *m, - Set *s, + OrderedSet *s, const char *prefix, const char *path, bool require_fallback) { const char *previous_interface = NULL; - struct node_vtable *c; struct node *n; int r; @@ -2603,7 +2587,7 @@ static int object_removed_append_all_prefix( * skip it on any of its parents. The child vtables * always fully override any conflicting vtables of * any parent node. */ - if (set_get(s, c->interface)) + if (ordered_set_get(s, c->interface)) continue; r = node_vtable_get_userdata(bus, path, c, &u, &error); @@ -2614,7 +2598,7 @@ static int object_removed_append_all_prefix( if (r == 0) continue; - r = set_put(s, c->interface); + r = ordered_set_put(s, c->interface); if (r < 0) return r; @@ -2629,7 +2613,7 @@ static int object_removed_append_all_prefix( } static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { - _cleanup_set_free_ Set *s = NULL; + _cleanup_ordered_set_free_ OrderedSet *s = NULL; _cleanup_free_ char *prefix = NULL; size_t pl; int r; @@ -2640,7 +2624,7 @@ static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char /* see sd_bus_emit_object_added() for details */ - s = set_new(&string_hash_ops); + s = ordered_set_new(&string_hash_ops); if (!s) return -ENOMEM; @@ -2754,7 +2738,6 @@ static int interfaces_added_append_one_prefix( _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; bool found_interface = false; - struct node_vtable *c; struct node *n; void *u = NULL; int r; @@ -2853,7 +2836,6 @@ static int interfaces_added_append_one( _public_ int sd_bus_emit_interfaces_added_strv(sd_bus *bus, const char *path, char **interfaces) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; struct node *object_manager; - char **i; int r; assert_return(bus, -EINVAL); diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 9e1d29cc1..f96149473 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -2783,7 +2783,6 @@ static int process_reply(sd_bus *bus, sd_bus_message *m) { static int process_filter(sd_bus *bus, sd_bus_message *m) { _cleanup_(sd_bus_error_free) sd_bus_error error_buffer = SD_BUS_ERROR_NULL; - struct filter_callback *l; int r; assert(bus); diff --git a/src/libsystemd/sd-bus/test-bus-address.c b/src/libsystemd/sd-bus/test-bus-address.c index 59421094c..ec9ee83fa 100644 --- a/src/libsystemd/sd-bus/test-bus-address.c +++ b/src/libsystemd/sd-bus/test-bus-address.c @@ -22,13 +22,12 @@ static void test_one_address(sd_bus *b, } } -static void test_bus_set_address_system_remote(char **args) { +TEST(bus_set_address_system_remote) { _cleanup_(sd_bus_unrefp) sd_bus *b = NULL; assert_se(sd_bus_new(&b) >= 0); - if (!strv_isempty(args)) { - char **a; - STRV_FOREACH(a, args) + if (!strv_isempty(saved_argv + 1)) { + STRV_FOREACH(a, saved_argv + 1) test_one_address(b, *a, 0, NULL); return; }; @@ -61,10 +60,4 @@ static void test_bus_set_address_system_remote(char **args) { -EINVAL, NULL); } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_INFO); - - test_bus_set_address_system_remote(argv + 1); - - return 0; -} +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/libsystemd/sd-bus/test-bus-error.c b/src/libsystemd/sd-bus/test-bus-error.c index 84728e4e8..952455255 100644 --- a/src/libsystemd/sd-bus/test-bus-error.c +++ b/src/libsystemd/sd-bus/test-bus-error.c @@ -8,8 +8,9 @@ #include "errno-list.h" #include "errno-util.h" #include "string-util.h" +#include "tests.h" -static void test_error(void) { +TEST(error) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL, second = SD_BUS_ERROR_NULL; const sd_bus_error const_error = SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "const error"); const sd_bus_error temporarily_const_error = { @@ -126,7 +127,7 @@ static void test_error(void) { extern const sd_bus_error_map __start_SYSTEMD_BUS_ERROR_MAP[]; extern const sd_bus_error_map __stop_SYSTEMD_BUS_ERROR_MAP[]; -static void dump_mapping_table(void) { +static int dump_mapping_table(void) { const sd_bus_error_map *m; printf("----- errno mappings ------\n"); @@ -142,9 +143,11 @@ static void dump_mapping_table(void) { m++; } printf("---------------------------\n"); + + return EXIT_SUCCESS; } -static void test_errno_mapping_standard(void) { +TEST(errno_mapping_standard) { assert_se(sd_bus_error_set(NULL, "System.Error.EUCLEAN", NULL) == -EUCLEAN); assert_se(sd_bus_error_set(NULL, "System.Error.EBUSY", NULL) == -EBUSY); assert_se(sd_bus_error_set(NULL, "System.Error.EINVAL", NULL) == -EINVAL); @@ -186,7 +189,7 @@ static const sd_bus_error_map test_errors_bad2[] = { SD_BUS_ERROR_MAP_END }; -static void test_errno_mapping_custom(void) { +TEST(errno_mapping_custom) { assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error", NULL) == -5); assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-2", NULL) == -52); assert_se(sd_bus_error_set(NULL, "org.freedesktop.custom-dbus-error-x", NULL) == -EIO); @@ -214,7 +217,7 @@ static void test_errno_mapping_custom(void) { assert_se(sd_bus_error_add_map(test_errors_bad2) == -EINVAL); } -static void test_sd_bus_error_set_errnof(void) { +TEST(sd_bus_error_set_errnof) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *str = NULL; @@ -288,13 +291,4 @@ static void test_sd_bus_error_set_errnof(void) { assert_se(error._need_free == 1); } -int main(int argc, char *argv[]) { - dump_mapping_table(); - - test_error(); - test_errno_mapping_standard(); - test_errno_mapping_custom(); - test_sd_bus_error_set_errnof(); - - return 0; -} +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, dump_mapping_table); diff --git a/src/libsystemd/sd-bus/test-bus-gvariant.c b/src/libsystemd/sd-bus/test-bus-gvariant.c index edd8301d4..47b175236 100644 --- a/src/libsystemd/sd-bus/test-bus-gvariant.c +++ b/src/libsystemd/sd-bus/test-bus-gvariant.c @@ -15,9 +15,7 @@ #include "tests.h" #include "util.h" -static void test_bus_gvariant_is_fixed_size(void) { - log_info("/* %s */", __func__); - +TEST(bus_gvariant_is_fixed_size) { assert_se(bus_gvariant_is_fixed_size("") > 0); assert_se(bus_gvariant_is_fixed_size("()") == -EINVAL); assert_se(bus_gvariant_is_fixed_size("y") > 0); @@ -42,9 +40,7 @@ static void test_bus_gvariant_is_fixed_size(void) { assert_se(bus_gvariant_is_fixed_size("((u)yyy(b(iiivi)))") == 0); } -static void test_bus_gvariant_get_size(void) { - log_info("/* %s */", __func__); - +TEST(bus_gvariant_get_size) { assert_se(bus_gvariant_get_size("") == 0); assert_se(bus_gvariant_get_size("()") == -EINVAL); assert_se(bus_gvariant_get_size("y") == 1); @@ -76,9 +72,7 @@ static void test_bus_gvariant_get_size(void) { assert_se(bus_gvariant_get_size("((t)(t))") == 16); } -static void test_bus_gvariant_get_alignment(void) { - log_info("/* %s */", __func__); - +TEST(bus_gvariant_get_alignment) { assert_se(bus_gvariant_get_alignment("") == 1); assert_se(bus_gvariant_get_alignment("()") == -EINVAL); assert_se(bus_gvariant_get_alignment("y") == 1); @@ -119,7 +113,7 @@ static void test_bus_gvariant_get_alignment(void) { assert_se(bus_gvariant_get_alignment("((t)(t))") == 8); } -static int test_marshal(void) { +TEST_RET(marshal) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *n = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ void *blob = NULL; @@ -209,12 +203,4 @@ static int test_marshal(void) { return EXIT_SUCCESS; } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_DEBUG); - - test_bus_gvariant_is_fixed_size(); - test_bus_gvariant_get_size(); - test_bus_gvariant_get_alignment(); - - return test_marshal(); -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/libsystemd/sd-bus/test-bus-introspect.c b/src/libsystemd/sd-bus/test-bus-introspect.c index fb247c4d1..3c026aedb 100644 --- a/src/libsystemd/sd-bus/test-bus-introspect.c +++ b/src/libsystemd/sd-bus/test-bus-introspect.c @@ -6,7 +6,7 @@ #include "test-vtable-data.h" -static void test_manual_introspection(const sd_bus_vtable vtable[]) { +static void test_manual_introspection_one(const sd_bus_vtable vtable[]) { struct introspect intro = {}; _cleanup_free_ char *s = NULL; @@ -23,13 +23,11 @@ static void test_manual_introspection(const sd_bus_vtable vtable[]) { fputs("\n", stdout); } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_DEBUG); - - test_manual_introspection(test_vtable_1); - test_manual_introspection(test_vtable_2); - test_manual_introspection(test_vtable_deprecated); - test_manual_introspection((const sd_bus_vtable *) vtable_format_221); - - return 0; +TEST(manual_introspection) { + test_manual_introspection_one(test_vtable_1); + test_manual_introspection_one(test_vtable_2); + test_manual_introspection_one(test_vtable_deprecated); + test_manual_introspection_one((const sd_bus_vtable *) vtable_format_221); } + +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/libsystemd/sd-daemon/sd-daemon.c b/src/libsystemd/sd-daemon/sd-daemon.c index b373c173c..c6380768b 100644 --- a/src/libsystemd/sd-daemon/sd-daemon.c +++ b/src/libsystemd/sd-daemon/sd-daemon.c @@ -23,6 +23,7 @@ #include "path-util.h" #include "process-util.h" #include "socket-util.h" +#include "stat-util.h" #include "strv.h" #include "time-util.h" #include "util.h" @@ -150,9 +151,7 @@ _public_ int sd_is_fifo(int fd, const char *path) { return -errno; } - return - st_path.st_dev == st_fd.st_dev && - st_path.st_ino == st_fd.st_ino; + return stat_inode_same(&st_path, &st_fd); } return 1; @@ -181,9 +180,7 @@ _public_ int sd_is_special(int fd, const char *path) { } if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode)) - return - st_path.st_dev == st_fd.st_dev && - st_path.st_ino == st_fd.st_ino; + return stat_inode_same(&st_path, &st_fd); else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode)) return st_path.st_rdev == st_fd.st_rdev; else @@ -415,7 +412,7 @@ _public_ int sd_is_mq(int fd, const char *path) { } if (path) { - char fpath[PATH_MAX]; + _cleanup_free_ char *fpath = NULL; struct stat a, b; assert_return(path_is_absolute(path), -EINVAL); @@ -423,14 +420,14 @@ _public_ int sd_is_mq(int fd, const char *path) { if (fstat(fd, &a) < 0) return -errno; - strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12); - fpath[sizeof(fpath)-1] = 0; + fpath = path_join("/dev/mqueue", path); + if (!fpath) + return -ENOMEM; if (stat(fpath, &b) < 0) return -errno; - if (a.st_dev != b.st_dev || - a.st_ino != b.st_ino) + if (!stat_inode_same(&a, &b)) return 0; } @@ -644,7 +641,7 @@ _public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) { r = safe_atou64(s, &u); if (r < 0) goto finish; - if (u <= 0 || u >= USEC_INFINITY) { + if (!timestamp_is_set(u)) { r = -EINVAL; goto finish; } diff --git a/src/libsystemd/sd-device/device-enumerator-private.h b/src/libsystemd/sd-device/device-enumerator-private.h index 9c6437dbe..11378d893 100644 --- a/src/libsystemd/sd-device/device-enumerator-private.h +++ b/src/libsystemd/sd-device/device-enumerator-private.h @@ -3,11 +3,22 @@ #include "sd-device.h" -int device_enumerator_scan_devices(sd_device_enumerator *enumeartor); -int device_enumerator_scan_subsystems(sd_device_enumerator *enumeartor); +typedef enum MatchInitializedType { + MATCH_INITIALIZED_NO, /* only devices without a db entry */ + MATCH_INITIALIZED_YES, /* only devices with a db entry */ + MATCH_INITIALIZED_ALL, /* all devices */ + MATCH_INITIALIZED_COMPAT, /* only devices that have no devnode/ifindex or have a db entry */ + _MATCH_INITIALIZED_MAX, + _MATCH_INITIALIZED_INVALID = -EINVAL, +} MatchInitializedType; + +int device_enumerator_scan_devices(sd_device_enumerator *enumerator); +int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator); +int device_enumerator_scan_devices_and_subsystems(sd_device_enumerator *enumerator); int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device); -int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator); +int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator, MatchInitializedType type); int device_enumerator_add_match_parent_incremental(sd_device_enumerator *enumerator, sd_device *parent); +int device_enumerator_add_prioritized_subsystem(sd_device_enumerator *enumerator, const char *subsystem); sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator); sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator); sd_device **device_enumerator_get_devices(sd_device_enumerator *enumerator, size_t *ret_n_devices); diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c index 4f1719b3e..14794fb3a 100644 --- a/src/libsystemd/sd-device/device-enumerator.c +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -20,6 +20,7 @@ typedef enum DeviceEnumerationType { DEVICE_ENUMERATION_TYPE_DEVICES, DEVICE_ENUMERATION_TYPE_SUBSYSTEMS, + DEVICE_ENUMERATION_TYPE_ALL, _DEVICE_ENUMERATION_TYPE_MAX, _DEVICE_ENUMERATION_TYPE_INVALID = -EINVAL, } DeviceEnumerationType; @@ -28,10 +29,13 @@ struct sd_device_enumerator { unsigned n_ref; DeviceEnumerationType type; + Hashmap *devices_by_syspath; sd_device **devices; size_t n_devices, current_device_index; bool scan_uptodate; + bool sorted; + char **prioritized_subsystems; Set *match_subsystem; Set *nomatch_subsystem; Hashmap *match_sysattr; @@ -40,7 +44,7 @@ struct sd_device_enumerator { Set *match_sysname; Set *match_tag; Set *match_parent; - bool match_allow_uninitialized; + MatchInitializedType match_initialized; }; _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { @@ -55,6 +59,7 @@ _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { *enumerator = (sd_device_enumerator) { .n_ref = 1, .type = _DEVICE_ENUMERATION_TYPE_INVALID, + .match_initialized = MATCH_INITIALIZED_COMPAT, }; *ret = TAKE_PTR(enumerator); @@ -62,13 +67,29 @@ _public_ int sd_device_enumerator_new(sd_device_enumerator **ret) { return 0; } +static void device_unref_many(sd_device **devices, size_t n) { + assert(devices || n == 0); + + for (size_t i = 0; i < n; i++) + sd_device_unref(devices[i]); +} + +static void device_enumerator_unref_devices(sd_device_enumerator *enumerator) { + assert(enumerator); + + hashmap_clear_with_destructor(enumerator->devices_by_syspath, sd_device_unref); + device_unref_many(enumerator->devices, enumerator->n_devices); + enumerator->devices = mfree(enumerator->devices); + enumerator->n_devices = 0; +} + static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumerator) { assert(enumerator); - for (size_t i = 0; i < enumerator->n_devices; i++) - sd_device_unref(enumerator->devices[i]); + device_enumerator_unref_devices(enumerator); - free(enumerator->devices); + hashmap_free(enumerator->devices_by_syspath); + strv_free(enumerator->prioritized_subsystems); set_free(enumerator->match_subsystem); set_free(enumerator->nomatch_subsystem); hashmap_free(enumerator->match_sysattr); @@ -83,6 +104,24 @@ static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumer DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_device_enumerator, sd_device_enumerator, device_enumerator_free); +int device_enumerator_add_prioritized_subsystem(sd_device_enumerator *enumerator, const char *subsystem) { + int r; + + assert(enumerator); + assert(subsystem); + + if (strv_contains(enumerator->prioritized_subsystems, subsystem)) + return 0; + + r = strv_extend(&enumerator->prioritized_subsystems, subsystem); + if (r < 0) + return r; + + enumerator->scan_uptodate = false; + + return 1; +} + _public_ int sd_device_enumerator_add_match_subsystem(sd_device_enumerator *enumerator, const char *subsystem, int match) { Set **set; int r; @@ -206,85 +245,214 @@ _public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumera _public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) { assert_return(enumerator, -EINVAL); - enumerator->match_allow_uninitialized = true; + enumerator->match_initialized = MATCH_INITIALIZED_ALL; enumerator->scan_uptodate = false; return 1; } -int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator) { +int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator, MatchInitializedType type) { assert_return(enumerator, -EINVAL); + assert_return(type >= 0 && type < _MATCH_INITIALIZED_MAX, -EINVAL); - enumerator->match_allow_uninitialized = false; + enumerator->match_initialized = type; enumerator->scan_uptodate = false; return 1; } -static int device_compare(sd_device * const *_a, sd_device * const *_b) { - sd_device *a = *(sd_device **)_a, *b = *(sd_device **)_b; - const char *devpath_a, *devpath_b, *sound_a; - bool delay_a, delay_b; - int r; +static int sound_device_compare(const char *devpath_a, const char *devpath_b) { + const char *sound_a, *sound_b; + size_t prefix_len; - assert_se(sd_device_get_devpath(a, &devpath_a) >= 0); - assert_se(sd_device_get_devpath(b, &devpath_b) >= 0); + assert(devpath_a); + assert(devpath_b); + + /* For sound cards the control device must be enumerated last to make sure it's the final + * device node that gets ACLs applied. Applications rely on this fact and use ACL changes on + * the control node as an indicator that the ACL change of the entire sound card completed. The + * kernel makes this guarantee when creating those devices, and hence we should too when + * enumerating them. */ sound_a = strstr(devpath_a, "/sound/card"); - if (sound_a) { - /* For sound cards the control device must be enumerated last to - * make sure it's the final device node that gets ACLs applied. - * Applications rely on this fact and use ACL changes on the - * control node as an indicator that the ACL change of the - * entire sound card completed. The kernel makes this guarantee - * when creating those devices, and hence we should too when - * enumerating them. */ - sound_a += STRLEN("/sound/card"); - sound_a = strchr(sound_a, '/'); + if (!sound_a) + return 0; - if (sound_a) { - unsigned prefix_len; + sound_a += STRLEN("/sound/card"); + sound_a = strchr(devpath_a, '/'); + if (!sound_a) + return 0; - prefix_len = sound_a - devpath_a; + prefix_len = sound_a - devpath_a; - if (strncmp(devpath_a, devpath_b, prefix_len) == 0) { - const char *sound_b; + if (!strneq(devpath_a, devpath_b, prefix_len)) + return 0; - sound_b = devpath_b + prefix_len; + sound_b = devpath_b + prefix_len; - if (startswith(sound_a, "/controlC") && - !startswith(sound_b, "/contolC")) - return 1; + return CMP(!!startswith(sound_a, "/controlC"), + !!startswith(sound_b, "/controlC")); +} - if (!startswith(sound_a, "/controlC") && - startswith(sound_b, "/controlC")) - return -1; - } - } - } +static bool devpath_is_late_block(const char *devpath) { + assert(devpath); - /* md and dm devices are enumerated after all other devices */ - delay_a = strstr(devpath_a, "/block/md") || strstr(devpath_a, "/block/dm-"); - delay_b = strstr(devpath_b, "/block/md") || strstr(devpath_b, "/block/dm-"); - r = CMP(delay_a, delay_b); + return strstr(devpath, "/block/md") || strstr(devpath, "/block/dm-"); +} + +static int device_compare(sd_device * const *a, sd_device * const *b) { + const char *devpath_a, *devpath_b; + int r; + + assert(a); + assert(b); + assert(*a); + assert(*b); + + assert_se(sd_device_get_devpath(*(sd_device**) a, &devpath_a) >= 0); + assert_se(sd_device_get_devpath(*(sd_device**) b, &devpath_b) >= 0); + + r = sound_device_compare(devpath_a, devpath_b); if (r != 0) return r; - return strcmp(devpath_a, devpath_b); + /* md and dm devices are enumerated after all other devices */ + r = CMP(devpath_is_late_block(devpath_a), devpath_is_late_block(devpath_b)); + if (r != 0) + return r; + + return path_compare(devpath_a, devpath_b); +} + +static int enumerator_sort_devices(sd_device_enumerator *enumerator) { + size_t n_sorted = 0, n = 0; + sd_device **devices; + sd_device *device; + int r; + + assert(enumerator); + + if (enumerator->sorted) + return 0; + + devices = new(sd_device*, hashmap_size(enumerator->devices_by_syspath)); + if (!devices) + return -ENOMEM; + + STRV_FOREACH(prioritized_subsystem, enumerator->prioritized_subsystems) { + + for (;;) { + const char *syspath; + size_t m = n; + + HASHMAP_FOREACH_KEY(device, syspath, enumerator->devices_by_syspath) { + _cleanup_free_ char *p = NULL; + const char *subsys; + + if (sd_device_get_subsystem(device, &subsys) < 0) + continue; + + if (!streq(subsys, *prioritized_subsystem)) + continue; + + devices[n++] = sd_device_ref(device); + + for (;;) { + _cleanup_free_ char *q = NULL; + + r = path_extract_directory(p ?: syspath, &q); + if (r == -EADDRNOTAVAIL) + break; + if (r < 0) + goto failed; + + device = hashmap_get(enumerator->devices_by_syspath, q); + if (device) + devices[n++] = sd_device_ref(device); + + free_and_replace(p, q); + } + + break; + } + + /* We cannot remove multiple entries in the loop HASHMAP_FOREACH_KEY() above. */ + for (size_t i = m; i < n; i++) { + r = sd_device_get_syspath(devices[i], &syspath); + if (r < 0) + goto failed; + + assert_se(hashmap_remove(enumerator->devices_by_syspath, syspath) == devices[i]); + sd_device_unref(devices[i]); + } + + if (m == n) + break; + } + + typesafe_qsort(devices + n_sorted, n - n_sorted, device_compare); + n_sorted = n; + } + + HASHMAP_FOREACH(device, enumerator->devices_by_syspath) + devices[n++] = sd_device_ref(device); + + /* Move all devices back to the hashmap. Otherwise, devices added by + * udev_enumerate_add_syspath() -> device_enumerator_add_device() may not be listed. */ + for (size_t i = 0; i < n_sorted; i++) { + const char *syspath; + + r = sd_device_get_syspath(devices[i], &syspath); + if (r < 0) + goto failed; + + r = hashmap_put(enumerator->devices_by_syspath, syspath, devices[i]); + if (r < 0) + goto failed; + assert(r > 0); + + sd_device_ref(devices[i]); + } + + typesafe_qsort(devices + n_sorted, n - n_sorted, device_compare); + + device_unref_many(enumerator->devices, enumerator->n_devices); + + enumerator->n_devices = n; + free_and_replace(enumerator->devices, devices); + + enumerator->sorted = true; + return 0; + +failed: + device_unref_many(devices, n); + free(devices); + return r; } int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device) { + const char *syspath; + int r; + assert_return(enumerator, -EINVAL); assert_return(device, -EINVAL); - if (!GREEDY_REALLOC(enumerator->devices, enumerator->n_devices + 1)) - return -ENOMEM; + r = sd_device_get_syspath(device, &syspath); + if (r < 0) + return r; - enumerator->devices[enumerator->n_devices++] = sd_device_ref(device); + r = hashmap_ensure_put(&enumerator->devices_by_syspath, &string_hash_ops, syspath, device); + if (IN_SET(r, -EEXIST, 0)) + return 0; + if (r < 0) + return r; - return 0; + sd_device_ref(device); + + enumerator->sorted = false; + return 1; } static bool match_property(sd_device_enumerator *enumerator, sd_device *device) { @@ -347,10 +515,42 @@ static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) return false; } +static int match_initialized(sd_device_enumerator *enumerator, sd_device *device) { + int r; + + assert(enumerator); + assert(device); + + if (enumerator->match_initialized == MATCH_INITIALIZED_ALL) + return true; + + r = sd_device_get_is_initialized(device); + if (r == -ENOENT) /* this is necessarily racey, so ignore missing devices */ + return false; + if (r < 0) + return r; + + if (enumerator->match_initialized == MATCH_INITIALIZED_COMPAT) { + /* only devices that have no devnode/ifindex or have a db entry are accepted. */ + if (r > 0) + return true; + + if (sd_device_get_devnum(device, NULL) >= 0) + return true; + + if (sd_device_get_ifindex(device, NULL) >= 0) + return true; + + return false; + } + + return (enumerator->match_initialized == MATCH_INITIALIZED_NO) == (r == 0); +} + static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, const char *basedir, const char *subdir1, const char *subdir2) { _cleanup_closedir_ DIR *dir = NULL; char *path; - int r = 0; + int k, r = 0; assert(enumerator); assert(basedir); @@ -371,7 +571,6 @@ static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, FOREACH_DIRENT_ALL(de, dir, return -errno) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; char syspath[strlen(path) + 1 + strlen(de->d_name) + 1]; - int initialized, k; if (de->d_name[0] == '.') continue; @@ -390,31 +589,13 @@ static int enumerator_scan_dir_and_add_devices(sd_device_enumerator *enumerator, continue; } - initialized = sd_device_get_is_initialized(device); - if (initialized < 0) { - if (initialized != -ENOENT) - /* this is necessarily racey, so ignore missing devices */ - r = initialized; - + k = match_initialized(enumerator, device); + if (k <= 0) { + if (k < 0) + r = k; continue; } - /* - * All devices with a device node or network interfaces - * possibly need udev to adjust the device node permission - * or context, or rename the interface before it can be - * reliably used from other processes. - * - * For now, we can only check these types of devices, we - * might not store a database, and have no way to find out - * for all other types of devices. - */ - if (!enumerator->match_allow_uninitialized && - !initialized && - (sd_device_get_devnum(device, NULL) >= 0 || - sd_device_get_ifindex(device, NULL) >= 0)) - continue; - if (!device_match_parent(device, enumerator->match_parent, NULL)) continue; @@ -673,57 +854,21 @@ static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) { } static int enumerator_scan_devices_all(sd_device_enumerator *enumerator) { - int r = 0; + int k, r = 0; log_debug("sd-device-enumerator: Scan all dirs"); - if (access("/sys/subsystem", F_OK) >= 0) { - /* we have /subsystem/, forget all the old stuff */ - r = enumerator_scan_dir(enumerator, "subsystem", "devices", NULL); - if (r < 0) - return log_debug_errno(r, "sd-device-enumerator: Failed to scan /sys/subsystem: %m"); - } else { - int k; + k = enumerator_scan_dir(enumerator, "bus", "devices", NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m"); - k = enumerator_scan_dir(enumerator, "bus", "devices", NULL); - if (k < 0) - r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m"); - - k = enumerator_scan_dir(enumerator, "class", NULL, NULL); - if (k < 0) - r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m"); - } + k = enumerator_scan_dir(enumerator, "class", NULL, NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m"); return r; } -static void device_enumerator_dedup_devices(sd_device_enumerator *enumerator) { - sd_device **a, **b, **end; - - assert(enumerator); - - if (enumerator->n_devices <= 1) - return; - - a = enumerator->devices + 1; - b = enumerator->devices; - end = enumerator->devices + enumerator->n_devices; - - for (; a < end; a++) { - const char *devpath_a, *devpath_b; - - assert_se(sd_device_get_devpath(*a, &devpath_a) >= 0); - assert_se(sd_device_get_devpath(*b, &devpath_b) >= 0); - - if (path_equal(devpath_a, devpath_b)) - sd_device_unref(*a); - else - *(++b) = *a; - } - - enumerator->n_devices = b - enumerator->devices + 1; -} - int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { int r = 0, k; @@ -733,10 +878,7 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { enumerator->type == DEVICE_ENUMERATION_TYPE_DEVICES) return 0; - for (size_t i = 0; i < enumerator->n_devices; i++) - sd_device_unref(enumerator->devices[i]); - - enumerator->n_devices = 0; + device_enumerator_unref_devices(enumerator); if (!set_isempty(enumerator->match_tag)) { k = enumerator_scan_devices_tags(enumerator); @@ -752,9 +894,6 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { r = k; } - typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare); - device_enumerator_dedup_devices(enumerator); - enumerator->scan_uptodate = true; enumerator->type = DEVICE_ENUMERATION_TYPE_DEVICES; @@ -762,12 +901,12 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumerator) { } _public_ sd_device *sd_device_enumerator_get_device_first(sd_device_enumerator *enumerator) { - int r; - assert_return(enumerator, NULL); - r = device_enumerator_scan_devices(enumerator); - if (r < 0) + if (device_enumerator_scan_devices(enumerator) < 0) + return NULL; + + if (enumerator_sort_devices(enumerator) < 0) return NULL; enumerator->current_device_index = 0; @@ -782,6 +921,7 @@ _public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *e assert_return(enumerator, NULL); if (!enumerator->scan_uptodate || + !enumerator->sorted || enumerator->type != DEVICE_ENUMERATION_TYPE_DEVICES || enumerator->current_device_index + 1 >= enumerator->n_devices) return NULL; @@ -790,7 +930,6 @@ _public_ sd_device *sd_device_enumerator_get_device_next(sd_device_enumerator *e } int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { - const char *subsysdir; int r = 0, k; assert(enumerator); @@ -799,10 +938,7 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { enumerator->type == DEVICE_ENUMERATION_TYPE_SUBSYSTEMS) return 0; - for (size_t i = 0; i < enumerator->n_devices; i++) - sd_device_unref(enumerator->devices[i]); - - enumerator->n_devices = 0; + device_enumerator_unref_devices(enumerator); /* modules */ if (match_subsystem(enumerator, "module")) { @@ -811,28 +947,20 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { r = log_debug_errno(k, "sd-device-enumerator: Failed to scan modules: %m"); } - if (access("/sys/subsystem", F_OK) >= 0) - subsysdir = "subsystem"; - else - subsysdir = "bus"; - /* subsystems (only buses support coldplug) */ if (match_subsystem(enumerator, "subsystem")) { - k = enumerator_scan_dir_and_add_devices(enumerator, subsysdir, NULL, NULL); + k = enumerator_scan_dir_and_add_devices(enumerator, "bus", NULL, NULL); if (k < 0) r = log_debug_errno(k, "sd-device-enumerator: Failed to scan subsystems: %m"); } /* subsystem drivers */ if (match_subsystem(enumerator, "drivers")) { - k = enumerator_scan_dir(enumerator, subsysdir, "drivers", "drivers"); + k = enumerator_scan_dir(enumerator, "bus", "drivers", "drivers"); if (k < 0) r = log_debug_errno(k, "sd-device-enumerator: Failed to scan drivers: %m"); } - typesafe_qsort(enumerator->devices, enumerator->n_devices, device_compare); - device_enumerator_dedup_devices(enumerator); - enumerator->scan_uptodate = true; enumerator->type = DEVICE_ENUMERATION_TYPE_SUBSYSTEMS; @@ -840,12 +968,12 @@ int device_enumerator_scan_subsystems(sd_device_enumerator *enumerator) { } _public_ sd_device *sd_device_enumerator_get_subsystem_first(sd_device_enumerator *enumerator) { - int r; - assert_return(enumerator, NULL); - r = device_enumerator_scan_subsystems(enumerator); - if (r < 0) + if (device_enumerator_scan_subsystems(enumerator) < 0) + return NULL; + + if (enumerator_sort_devices(enumerator) < 0) return NULL; enumerator->current_device_index = 0; @@ -860,6 +988,7 @@ _public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator assert_return(enumerator, NULL); if (!enumerator->scan_uptodate || + !enumerator->sorted || enumerator->type != DEVICE_ENUMERATION_TYPE_SUBSYSTEMS || enumerator->current_device_index + 1 >= enumerator->n_devices) return NULL; @@ -867,12 +996,67 @@ _public_ sd_device *sd_device_enumerator_get_subsystem_next(sd_device_enumerator return enumerator->devices[++enumerator->current_device_index]; } +int device_enumerator_scan_devices_and_subsystems(sd_device_enumerator *enumerator) { + int r = 0, k; + + assert(enumerator); + + if (enumerator->scan_uptodate && + enumerator->type == DEVICE_ENUMERATION_TYPE_ALL) + return 0; + + device_enumerator_unref_devices(enumerator); + + if (!set_isempty(enumerator->match_tag)) { + k = enumerator_scan_devices_tags(enumerator); + if (k < 0) + r = k; + } else if (enumerator->match_parent) { + k = enumerator_scan_devices_children(enumerator); + if (k < 0) + r = k; + } else { + k = enumerator_scan_dir(enumerator, "class", NULL, NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/class: %m"); + + k = enumerator_scan_dir(enumerator, "bus", "devices", NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan /sys/bus: %m"); + + if (match_subsystem(enumerator, "module")) { + k = enumerator_scan_dir_and_add_devices(enumerator, "module", NULL, NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan modules: %m"); + } + if (match_subsystem(enumerator, "subsystem")) { + k = enumerator_scan_dir_and_add_devices(enumerator, "bus", NULL, NULL); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan subsystems: %m"); + } + + if (match_subsystem(enumerator, "drivers")) { + k = enumerator_scan_dir(enumerator, "bus", "drivers", "drivers"); + if (k < 0) + r = log_debug_errno(k, "sd-device-enumerator: Failed to scan drivers: %m"); + } + } + + enumerator->scan_uptodate = true; + enumerator->type = DEVICE_ENUMERATION_TYPE_ALL; + + return r; +} + sd_device *device_enumerator_get_first(sd_device_enumerator *enumerator) { assert_return(enumerator, NULL); if (!enumerator->scan_uptodate) return NULL; + if (enumerator_sort_devices(enumerator) < 0) + return NULL; + enumerator->current_device_index = 0; if (enumerator->n_devices == 0) @@ -885,6 +1069,7 @@ sd_device *device_enumerator_get_next(sd_device_enumerator *enumerator) { assert_return(enumerator, NULL); if (!enumerator->scan_uptodate || + !enumerator->sorted || enumerator->current_device_index + 1 >= enumerator->n_devices) return NULL; @@ -898,6 +1083,9 @@ sd_device **device_enumerator_get_devices(sd_device_enumerator *enumerator, size if (!enumerator->scan_uptodate) return NULL; + if (enumerator_sort_devices(enumerator) < 0) + return NULL; + *ret_n_devices = enumerator->n_devices; return enumerator->devices; } diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h index 76a1727b1..49d0f0fd1 100644 --- a/src/libsystemd/sd-device/device-internal.h +++ b/src/libsystemd/sd-device/device-internal.h @@ -101,7 +101,6 @@ int device_add_property_aux(sd_device *device, const char *key, const char *valu static inline int device_add_property_internal(sd_device *device, const char *key, const char *value) { return device_add_property_aux(device, key, value, false); } -int device_read_uevent_file(sd_device *device); int device_set_syspath(sd_device *device, const char *_syspath, bool verify); int device_set_ifindex(sd_device *device, const char *ifindex); diff --git a/src/libsystemd/sd-device/device-monitor.c b/src/libsystemd/sd-device/device-monitor.c index 524d10b9d..80cb0ce62 100644 --- a/src/libsystemd/sd-device/device-monitor.c +++ b/src/libsystemd/sd-device/device-monitor.c @@ -24,6 +24,7 @@ #include "mountpoint-util.h" #include "set.h" #include "socket-util.h" +#include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -195,7 +196,7 @@ int device_monitor_new_full(sd_device_monitor **ret, MonitorNetlinkGroup group, else log_debug_errno(errno, "sd-device-monitor: Failed to stat PID1's netns, ignoring: %m"); - } else if (a.st_dev != b.st_dev || a.st_ino != b.st_ino) + } else if (!stat_inode_same(&a, &b)) log_debug("sd-device-monitor: Netlink socket we listen on is not from host netns, we won't see device events."); } } diff --git a/src/libsystemd/sd-device/device-private.c b/src/libsystemd/sd-device/device-private.c index 37eda23a5..5f3644aac 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -47,6 +47,27 @@ int device_add_property(sd_device *device, const char *key, const char *value) { return 0; } +int device_add_propertyf(sd_device *device, const char *key, const char *format, ...) { + _cleanup_free_ char *value = NULL; + va_list ap; + int r; + + assert(device); + assert(key); + + if (!format) + return device_add_property(device, key, NULL); + + va_start(ap, format); + r = vasprintf(&value, format, ap); + va_end(ap); + + if (r < 0) + return -ENOMEM; + + return device_add_property(device, key, value); +} + void device_set_devlink_priority(sd_device *device, int priority) { assert(device); @@ -184,16 +205,11 @@ static int device_set_devgid(sd_device *device, const char *gid) { return 0; } -static int device_set_action(sd_device *device, const char *action) { - sd_device_action_t a; +int device_set_action(sd_device *device, sd_device_action_t a) { int r; assert(device); - assert(action); - - a = device_action_from_string(action); - if (a < 0) - return a; + assert(a >= 0 && a < _SD_DEVICE_ACTION_MAX); r = device_add_property_internal(device, "ACTION", device_action_to_string(a)); if (r < 0) @@ -204,6 +220,19 @@ static int device_set_action(sd_device *device, const char *action) { return 0; } +static int device_set_action_from_string(sd_device *device, const char *action) { + sd_device_action_t a; + + assert(device); + assert(action); + + a = device_action_from_string(action); + if (a < 0) + return a; + + return device_set_action(device, a); +} + static int device_set_seqnum(sd_device *device, const char *str) { uint64_t seqnum; int r; @@ -307,7 +336,7 @@ static int device_amend(sd_device *device, const char *key, const char *value) { if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set devgid to '%s': %m", value); } else if (streq(key, "ACTION")) { - r = device_set_action(device, value); + r = device_set_action_from_string(device, value); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set action to '%s': %m", value); } else if (streq(key, "SEQNUM")) { @@ -429,7 +458,6 @@ static int device_verify(sd_device *device) { int device_new_from_strv(sd_device **ret, char **strv) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; - char **key; const char *major = NULL, *minor = NULL; int r; @@ -850,31 +878,6 @@ int device_clone_with_db(sd_device *old_device, sd_device **new_device) { return 0; } -int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action) { - _cleanup_(sd_device_unrefp) sd_device *ret = NULL; - int r; - - assert(new_device); - assert(syspath); - assert(action); - - r = sd_device_new_from_syspath(&ret, syspath); - if (r < 0) - return r; - - r = device_read_uevent_file(ret); - if (r < 0) - return r; - - r = device_set_action(ret, action); - if (r < 0) - return r; - - *new_device = TAKE_PTR(ret); - - return 0; -} - int device_copy_properties(sd_device *device_dst, sd_device *device_src) { const char *property, *value; int r; @@ -1046,18 +1049,9 @@ int device_update_db(sd_device *device) { * set 'sticky' bit to indicate that we should not clean the * database when we transition from initramfs to the real root */ - if (device->db_persist) { - r = fchmod(fileno(f), 01644); - if (r < 0) { - r = -errno; - goto fail; - } - } else { - r = fchmod(fileno(f), 0644); - if (r < 0) { - r = -errno; - goto fail; - } + if (fchmod(fileno(f), device->db_persist ? 01644 : 0644) < 0) { + r = -errno; + goto fail; } if (has_info) { @@ -1094,8 +1088,7 @@ int device_update_db(sd_device *device) { if (r < 0) goto fail; - r = rename(path_tmp, path); - if (r < 0) { + if (rename(path_tmp, path) < 0) { r = -errno; goto fail; } diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h index 04b932309..68caee2cb 100644 --- a/src/libsystemd/sd-device/device-private.h +++ b/src/libsystemd/sd-device/device-private.h @@ -36,6 +36,7 @@ int device_ensure_usec_initialized(sd_device *device, sd_device *device_old); int device_add_devlink(sd_device *device, const char *devlink); bool device_has_devlink(sd_device *device, const char *devlink); int device_add_property(sd_device *device, const char *property, const char *value); +int device_add_propertyf(sd_device *device, const char *key, const char *format, ...) _printf_(3, 4); int device_add_tag(sd_device *device, const char *tag, bool both); void device_remove_tag(sd_device *device, const char *tag); void device_cleanup_tags(sd_device *device); @@ -53,7 +54,6 @@ int device_rename(sd_device *device, const char *name); int device_shallow_clone(sd_device *old_device, sd_device **new_device); int device_clone_with_db(sd_device *old_device, sd_device **new_device); int device_copy_properties(sd_device *device_dst, sd_device *device_src); -int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, const char *action); int device_tag_index(sd_device *dev, sd_device *dev_old, bool add); int device_update_db(sd_device *device); @@ -64,6 +64,9 @@ static inline int device_read_db(sd_device *device) { return device_read_db_internal(device, false); } +int device_read_uevent_file(sd_device *device); + +int device_set_action(sd_device *device, sd_device_action_t a); sd_device_action_t device_action_from_string(const char *s) _pure_; const char *device_action_to_string(sd_device_action_t a) _const_; void dump_device_action_table(void); diff --git a/src/libsystemd/sd-device/sd-device.c b/src/libsystemd/sd-device/sd-device.c index b163a0fb6..f22088db5 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -152,7 +152,9 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { if (verify) { r = chase_symlinks(_syspath, NULL, 0, &syspath, NULL); if (r == -ENOENT) - return -ENODEV; /* the device does not exist (any more?) */ + /* the device does not exist (any more?) */ + return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), + "sd-device: Failed to chase symlinks in \"%s\".", _syspath); if (r < 0) return log_debug_errno(r, "sd-device: Failed to get target of '%s': %m", _syspath); @@ -173,7 +175,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { new_syspath = path_join("/sys", p); if (!new_syspath) - return -ENOMEM; + return log_oom_debug(); free_and_replace(syspath, new_syspath); path_simplify(syspath); @@ -186,31 +188,35 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) { path = strjoina(syspath, "/uevent"); if (access(path, F_OK) < 0) { if (errno == ENOENT) - /* this is not a valid device */ - return -ENODEV; + /* This is not a valid device. + * Note, this condition is quite often satisfied when + * enumerating devices or finding a parent device. + * Hence, use log_trace_errno() here. */ + return log_trace_errno(SYNTHETIC_ERRNO(ENODEV), + "sd-device: the uevent file \"%s\" does not exist.", path); return log_debug_errno(errno, "sd-device: cannot access uevent file for %s: %m", syspath); } } else { /* everything else just needs to be a directory */ if (!is_dir(syspath, false)) - return -ENODEV; + return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), + "sd-device: the syspath \"%s\" is not a directory.", syspath); } } else { syspath = strdup(_syspath); if (!syspath) - return -ENOMEM; + return log_oom_debug(); } devpath = syspath + STRLEN("/sys"); if (devpath[0] != '/') - /* '/sys' alone is not a valid device path */ - return -ENODEV; + return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "sd-device: \"/sys\" alone is not a valid device path."); r = device_add_property_internal(device, "DEVPATH", devpath); if (r < 0) - return r; + return log_debug_errno(r, "sd-device: Failed to add \"DEVPATH\" property for device \"%s\": %m", syspath); free_and_replace(device->syspath, syspath); device->devpath = devpath; @@ -242,6 +248,10 @@ _public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) assert_return(ret, -EINVAL); assert_return(IN_SET(type, 'b', 'c'), -EINVAL); + if (devnum == 0) + return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), + "sd-device: Attempted to allocate device by zero major/minor, refusing."); + /* use /sys/dev/{block,char}/: link */ xsprintf(id, "%u:%u", major(devnum), minor(devnum)); @@ -322,7 +332,6 @@ _public_ int sd_device_new_from_subsystem_sysname( const char *subsystem, const char *sysname) { - const char *s; char *name; int r; @@ -331,7 +340,6 @@ _public_ int sd_device_new_from_subsystem_sysname( assert_return(path_is_normalized(sysname), -EINVAL); if (streq(subsystem, "subsystem")) { - FOREACH_STRING(s, "/sys/subsystem/", "/sys/bus/", "/sys/class/") { r = device_strjoin_new(s, sysname, NULL, NULL, ret); if (r < 0) @@ -341,7 +349,6 @@ _public_ int sd_device_new_from_subsystem_sysname( } } else if (streq(subsystem, "module")) { - r = device_strjoin_new("/sys/module/", sysname, NULL, NULL, ret); if (r < 0) return r; @@ -353,9 +360,8 @@ _public_ int sd_device_new_from_subsystem_sysname( sep = strchr(sysname, ':'); if (sep && sep[1] != '\0') { /* Require ":" and something non-empty after that. */ - const char *subsys; - subsys = memdupa_suffix0(sysname, sep - sysname); + const char *subsys = memdupa_suffix0(sysname, sep - sysname); sep++; FOREACH_STRING(s, "/sys/subsystem/", "/sys/bus/") { @@ -804,32 +810,26 @@ int device_set_subsystem(sd_device *device, const char *subsystem) { int device_set_drivers_subsystem(sd_device *device) { _cleanup_free_ char *subsystem = NULL; - const char *syspath, *drivers, *p; + const char *devpath, *drivers, *p; int r; assert(device); - r = sd_device_get_syspath(device, &syspath); + r = sd_device_get_devpath(device, &devpath); if (r < 0) return r; - drivers = strstr(syspath, "/drivers/"); + drivers = strstr(devpath, "/drivers/"); if (!drivers) return -EINVAL; - for (p = drivers - 1; p >= syspath; p--) - if (*p == '/') - break; - - if (p <= syspath) - /* syspath does not start with /sys/ ?? */ - return -EINVAL; - p++; - if (p >= drivers) - /* refuse duplicated slashes */ + r = path_find_last_component(devpath, false, &drivers, &p); + if (r < 0) + return r; + if (r == 0) return -EINVAL; - subsystem = strndup(p, drivers - p); + subsystem = strndup(p, r); if (!subsystem) return -ENOMEM; @@ -1419,7 +1419,7 @@ int device_read_db_internal_filename(sd_device *device, const char *filename) { device->db_loaded = true; - for (size_t i = 0; i < db_len; i++) { + for (size_t i = 0; i < db_len; i++) switch (state) { case PRE_KEY: if (!strchr(NEWLINE, db[i])) { @@ -1467,7 +1467,6 @@ int device_read_db_internal_filename(sd_device *device, const char *filename) { default: return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), "sd-device: invalid db syntax."); } - } return 0; } @@ -2189,7 +2188,7 @@ _public_ int sd_device_trigger_with_uuid( if (r < 0) return r; - j = strjoina(s, " ", ID128_TO_UUID_STRING(u)); + j = strjoina(s, " ", SD_ID128_TO_UUID_STRING(u)); r = sd_device_set_sysattr_value(device, "uevent", j); if (r < 0) diff --git a/src/libsystemd/sd-device/test-device-util.c b/src/libsystemd/sd-device/test-device-util.c index 93fc105d9..bc8ab6671 100644 --- a/src/libsystemd/sd-device/test-device-util.c +++ b/src/libsystemd/sd-device/test-device-util.c @@ -3,11 +3,9 @@ #include "device-util.h" #include "tests.h" -static void test_log_device_full(void) { +TEST(log_device_full) { int r; - log_info("/* %s */", __func__); - for (int level = LOG_ERR; level <= LOG_DEBUG; level++) { log_device_full(NULL, level, "test level=%d: %m", level); @@ -22,9 +20,4 @@ static void test_log_device_full(void) { } } -int main(int argc, char **argv) { - test_setup_logging(LOG_INFO); - - test_log_device_full(); - return 0; -} +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/libsystemd/sd-device/test-sd-device-monitor.c b/src/libsystemd/sd-device/test-sd-device-monitor.c index ae973cdba..67f9bf4ae 100644 --- a/src/libsystemd/sd-device/test-sd-device-monitor.c +++ b/src/libsystemd/sd-device/test-sd-device-monitor.c @@ -10,6 +10,7 @@ #include "device-private.h" #include "device-util.h" #include "macro.h" +#include "stat-util.h" #include "string-util.h" #include "tests.h" #include "util.h" @@ -24,11 +25,10 @@ static int monitor_handler(sd_device_monitor *m, sd_device *d, void *userdata) { return sd_event_exit(sd_device_monitor_get_event(m), 100); } -static int test_receive_device_fail(void) { +static void test_receive_device_fail(void) { _cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor_server = NULL, *monitor_client = NULL; _cleanup_(sd_device_unrefp) sd_device *loopback = NULL; const char *syspath; - int r; log_info("/* %s */", __func__); @@ -47,14 +47,8 @@ static int test_receive_device_fail(void) { assert_se(sd_device_monitor_start(monitor_client, monitor_handler, (void *) syspath) >= 0); assert_se(sd_event_source_set_description(sd_device_monitor_get_event_source(monitor_client), "receiver") >= 0); - /* Do not use assert_se() here. */ - r = device_monitor_send_device(monitor_server, monitor_client, loopback); - if (r < 0) - return log_error_errno(r, "Failed to send loopback device: %m"); - + assert_se(device_monitor_send_device(monitor_server, monitor_client, loopback) >= 0); assert_se(sd_event_run(sd_device_monitor_get_event(monitor_client), 0) >= 0); - - return 0; } static void test_send_receive_one(sd_device *device, bool subsystem_filter, bool tag_filter, bool use_bpf) { @@ -299,11 +293,10 @@ int main(int argc, char *argv[]) { if (getuid() != 0) return log_tests_skipped("not root"); - r = test_receive_device_fail(); - if (r < 0) { - assert_se(r == -EPERM && detect_container() > 0); - return log_tests_skipped("Running in container? Skipping remaining tests"); - } + if (path_is_read_only_fs("/sys") > 0) + return log_tests_skipped("Running in container"); + + test_receive_device_fail(); assert_se(sd_device_new_from_syspath(&loopback, "/sys/class/net/lo") >= 0); assert_se(device_add_property(loopback, "ACTION", "add") >= 0); diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c index aaa16f740..8a534ca8e 100644 --- a/src/libsystemd/sd-device/test-sd-device.c +++ b/src/libsystemd/sd-device/test-sd-device.c @@ -12,7 +12,8 @@ #include "time-util.h" static void test_sd_device_one(sd_device *d) { - const char *syspath, *subsystem, *val; + _cleanup_(sd_device_unrefp) sd_device *device_from_id = NULL; + const char *syspath, *subsystem, *id, *val; dev_t devnum; usec_t usec; int i, r; @@ -57,27 +58,33 @@ static void test_sd_device_one(sd_device *d) { r = sd_device_get_property_value(d, "ID_NET_DRIVER", &val); assert_se(r >= 0 || r == -ENOENT); - log_info("syspath:%s subsystem:%s initialized:%s", syspath, strna(subsystem), yes_no(i)); + r = device_get_device_id(d, &id); + assert_se(r >= 0); + + r = sd_device_new_from_device_id(&device_from_id, id); + assert_se(r >= 0); + + r = sd_device_get_syspath(device_from_id, &val); + assert_se(r >= 0); + assert_se(streq(syspath, val)); + + log_info("syspath:%s subsystem:%s id:%s initialized:%s", syspath, strna(subsystem), id, yes_no(i)); } -static void test_sd_device_enumerator_devices(void) { +TEST(sd_device_enumerator_devices) { _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; sd_device *d; - log_info("/* %s */", __func__); - assert_se(sd_device_enumerator_new(&e) >= 0); assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0); FOREACH_DEVICE(e, d) test_sd_device_one(d); } -static void test_sd_device_enumerator_subsystems(void) { +TEST(sd_device_enumerator_subsystems) { _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; sd_device *d; - log_info("/* %s */", __func__); - assert_se(sd_device_enumerator_new(&e) >= 0); assert_se(sd_device_enumerator_allow_uninitialized(e) >= 0); FOREACH_SUBSYSTEM(e, d) @@ -113,7 +120,7 @@ static unsigned test_sd_device_enumerator_filter_subsystem_one(const char *subsy return n_new_dev; } -static void test_sd_device_enumerator_filter_subsystem(void) { +TEST(sd_device_enumerator_filter_subsystem) { _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; _cleanup_(hashmap_freep) Hashmap *subsystems; unsigned n_new_dev = 0; @@ -121,8 +128,6 @@ static void test_sd_device_enumerator_filter_subsystem(void) { Hashmap *h; char *s; - log_info("/* %s */", __func__); - assert_se(subsystems = hashmap_new(&string_hash_ops)); assert_se(sd_device_enumerator_new(&e) >= 0); @@ -164,7 +169,7 @@ static void test_sd_device_enumerator_filter_subsystem(void) { assert_se(n_new_dev <= 10); } -static void test_sd_device_new_from_nulstr(void) { +TEST(sd_device_new_from_nulstr) { const char *devlinks = "/dev/disk/by-partuuid/1290d63a-42cc-4c71-b87c-xxxxxxxxxxxx\0" "/dev/disk/by-path/pci-0000:00:0f.0-scsi-0:0:0:0-part3\0" @@ -179,8 +184,6 @@ static void test_sd_device_new_from_nulstr(void) { const uint8_t *nulstr; size_t len; - log_info("/* %s */", __func__); - assert_se(sd_device_new_from_syspath(&device, "/sys/class/net/lo") >= 0); /* Yeah, of course, setting devlink to the loopback interface is nonsense. But this is just a @@ -205,14 +208,4 @@ static void test_sd_device_new_from_nulstr(void) { } } -int main(int argc, char **argv) { - test_setup_logging(LOG_INFO); - - test_sd_device_enumerator_devices(); - test_sd_device_enumerator_subsystems(); - test_sd_device_enumerator_filter_subsystem(); - - test_sd_device_new_from_nulstr(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/libsystemd/sd-event/event-util.c b/src/libsystemd/sd-event/event-util.c index 0e53406a9..8c24e7db6 100644 --- a/src/libsystemd/sd-event/event-util.c +++ b/src/libsystemd/sd-event/event-util.c @@ -4,6 +4,7 @@ #include "event-source.h" #include "event-util.h" +#include "fd-util.h" #include "log.h" #include "string-util.h" @@ -121,3 +122,41 @@ int event_source_is_enabled(sd_event_source *s) { return sd_event_source_get_enabled(s, NULL); } + +int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata) { + _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + _cleanup_close_ int fd = -1; + int r; + + assert(e); + + /* Allocates an IO event source that gets woken up whenever the clock changes. Needs to be recreated on each event */ + + fd = time_change_fd(); + if (fd < 0) + return fd; + + r = sd_event_add_io(e, &s, fd, EPOLLIN, callback, userdata); + if (r < 0) + return r; + + r = sd_event_source_set_io_fd_own(s, true); + if (r < 0) + return r; + + TAKE_FD(fd); + + r = sd_event_source_set_description(s, "time-change"); + if (r < 0) + return r; + + if (ret) + *ret = TAKE_PTR(s); + else { + r = sd_event_source_set_floating(s, true); + if (r < 0) + return r; + } + + return 0; +} diff --git a/src/libsystemd/sd-event/event-util.h b/src/libsystemd/sd-event/event-util.h index 64a419924..abd043bdc 100644 --- a/src/libsystemd/sd-event/event-util.h +++ b/src/libsystemd/sd-event/event-util.h @@ -29,3 +29,5 @@ int event_reset_time_relative( bool force_reset); int event_source_disable(sd_event_source *s); int event_source_is_enabled(sd_event_source *s); + +int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata); diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 82056998b..4dbdf1987 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -1920,7 +1920,6 @@ static int event_make_inode_data( static uint32_t inode_data_determine_mask(struct inode_data *d) { bool excl_unlink = true; uint32_t combined = 0; - sd_event_source *s; assert(d); @@ -3458,9 +3457,7 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { /* The queue overran, let's pass this event to all event sources connected to this inotify * object */ - HASHMAP_FOREACH(inode_data, d->inodes) { - sd_event_source *s; - + HASHMAP_FOREACH(inode_data, d->inodes) LIST_FOREACH(inotify.by_inode_data, s, inode_data->event_sources) { if (event_source_is_offline(s)) @@ -3470,10 +3467,8 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { if (r < 0) return r; } - } } else { struct inode_data *inode_data; - sd_event_source *s; /* Find the inode object for this watch descriptor. If IN_IGNORED is set we also remove it from * our watch descriptor table. */ @@ -3521,7 +3516,6 @@ static int event_inotify_data_process(sd_event *e, struct inotify_data *d) { } static int process_inotify(sd_event *e) { - struct inotify_data *d; int r, done = 0; assert(e); @@ -3906,6 +3900,7 @@ static int epoll_wait_usec( int msec; #if 0 static bool epoll_pwait2_absent = false; + int r; /* A wrapper that uses epoll_pwait2() if available, and falls back to epoll_wait() if not. * @@ -3914,12 +3909,10 @@ static int epoll_wait_usec( * https://github.com/systemd/systemd/issues/19052. */ if (!epoll_pwait2_absent && timeout != USEC_INFINITY) { - struct timespec ts; - r = epoll_pwait2(fd, events, maxevents, - timespec_store(&ts, timeout), + TIMESPEC_STORE(timeout), NULL); if (r >= 0) return r; @@ -4320,12 +4313,6 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) { if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock)) return -EOPNOTSUPP; - /* Generate a clean error in case CLOCK_BOOTTIME is not available. Note that don't use clock_supported() here, - * for a reason: there are systems where CLOCK_BOOTTIME is supported, but CLOCK_BOOTTIME_ALARM is not, but for - * the purpose of getting the time this doesn't matter. */ - if (IN_SET(clock, CLOCK_BOOTTIME, CLOCK_BOOTTIME_ALARM) && !clock_boottime_supported()) - return -EOPNOTSUPP; - if (!triple_timestamp_is_set(&e->timestamp)) { /* Implicitly fall back to now() if we never ran before and thus have no cached time. */ *usec = now(clock); diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c index 0ac23c111..ea29def3d 100644 --- a/src/libsystemd/sd-event/test-event.c +++ b/src/libsystemd/sd-event/test-event.c @@ -195,7 +195,7 @@ static int post_handler(sd_event_source *s, void *userdata) { return 2; } -static void test_basic(bool with_pidfd) { +static void test_basic_one(bool with_pidfd) { sd_event *e = NULL; sd_event_source *w = NULL, *x = NULL, *y = NULL, *z = NULL, *q = NULL, *t = NULL; static const char ch = 'x'; @@ -302,20 +302,21 @@ static void test_basic(bool with_pidfd) { assert_se(unsetenv("SYSTEMD_PIDFD") >= 0); } -static void test_sd_event_now(void) { +TEST(basic) { + test_basic_one(true); /* test with pidfd */ + test_basic_one(false); /* test without pidfd */ +} + +TEST(sd_event_now) { _cleanup_(sd_event_unrefp) sd_event *e = NULL; uint64_t event_now; - log_info("/* %s */", __func__); - assert_se(sd_event_new(&e) >= 0); assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) > 0); assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) > 0); assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) > 0); - if (clock_boottime_supported()) { - assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0); - } + assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) > 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) > 0); assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); @@ -324,10 +325,8 @@ static void test_sd_event_now(void) { assert_se(sd_event_now(e, CLOCK_MONOTONIC, &event_now) == 0); assert_se(sd_event_now(e, CLOCK_REALTIME, &event_now) == 0); assert_se(sd_event_now(e, CLOCK_REALTIME_ALARM, &event_now) == 0); - if (clock_boottime_supported()) { - assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0); - assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0); - } + assert_se(sd_event_now(e, CLOCK_BOOTTIME, &event_now) == 0); + assert_se(sd_event_now(e, CLOCK_BOOTTIME_ALARM, &event_now) == 0); assert_se(sd_event_now(e, -1, &event_now) == -EOPNOTSUPP); assert_se(sd_event_now(e, 900 /* arbitrary big number */, &event_now) == -EOPNOTSUPP); } @@ -341,12 +340,10 @@ static int rtqueue_handler(sd_event_source *s, const struct signalfd_siginfo *si return 0; } -static void test_rtqueue(void) { +TEST(rtqueue) { sd_event_source *u = NULL, *v = NULL, *s = NULL; sd_event *e = NULL; - log_info("/* %s */", __func__); - assert_se(sd_event_default(&e) >= 0); assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGRTMIN+2, SIGRTMIN+3, SIGUSR2, -1) >= 0); @@ -476,7 +473,7 @@ static int delete_self_handler(sd_event_source *s, const struct inotify_event *e return 1; } -static void test_inotify(unsigned n_create_events) { +static void test_inotify_one(unsigned n_create_events) { _cleanup_(rm_rf_physical_and_freep) char *p = NULL; sd_event_source *a = NULL, *b = NULL, *c = NULL, *d = NULL; struct inotify_context context = { @@ -529,6 +526,11 @@ static void test_inotify(unsigned n_create_events) { sd_event_unref(e); } +TEST(inotify) { + test_inotify_one(100); /* should work without overflow */ + test_inotify_one(33000); /* should trigger a q overflow */ +} + static int pidfd_handler(sd_event_source *s, const siginfo_t *si, void *userdata) { assert_se(s); assert_se(si); @@ -548,14 +550,12 @@ static int pidfd_handler(sd_event_source *s, const siginfo_t *si, void *userdata return 0; } -static void test_pidfd(void) { +TEST(pidfd) { sd_event_source *s = NULL, *t = NULL; sd_event *e = NULL; int pidfd; pid_t pid, pid2; - log_info("/* %s */", __func__); - assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); pid = fork(); @@ -628,15 +628,13 @@ static int ratelimit_expired(sd_event_source *s, void *userdata) { return ++expired; } -static void test_ratelimit(void) { +TEST(ratelimit) { _cleanup_close_pair_ int p[2] = {-1, -1}; _cleanup_(sd_event_unrefp) sd_event *e = NULL; _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; uint64_t interval; unsigned count, burst; - log_info("/* %s */", __func__); - assert_se(sd_event_default(&e) >= 0); assert_se(pipe2(p, O_CLOEXEC|O_NONBLOCK) >= 0); @@ -706,7 +704,7 @@ static void test_ratelimit(void) { assert_se(expired == 0); } -static void test_simple_timeout(void) { +TEST(simple_timeout) { _cleanup_(sd_event_unrefp) sd_event *e = NULL; usec_t f, t, some_time; @@ -741,7 +739,7 @@ static int inotify_self_destroy_handler(sd_event_source *s, const struct inotify return 1; } -static void test_inotify_self_destroy(void) { +TEST(inotify_self_destroy) { _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; _cleanup_(sd_event_unrefp) sd_event *e = NULL; char path[] = "/tmp/inotifyXXXXXX"; @@ -759,25 +757,4 @@ static void test_inotify_self_destroy(void) { assert_se(sd_event_loop(e) >= 0); } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_DEBUG); - - test_simple_timeout(); - - test_basic(true); /* test with pidfd */ - test_basic(false); /* test without pidfd */ - - test_sd_event_now(); - test_rtqueue(); - - test_inotify(100); /* should work without overflow */ - test_inotify(33000); /* should trigger a q overflow */ - - test_pidfd(); - - test_ratelimit(); - - test_inotify_self_destroy(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/libsystemd/sd-id128/id128-util.c b/src/libsystemd/sd-id128/id128-util.c index 7c66d1c2d..e4a3bbd3e 100644 --- a/src/libsystemd/sd-id128/id128-util.c +++ b/src/libsystemd/sd-id128/id128-util.c @@ -12,29 +12,6 @@ #include "string-util.h" #include "sync-util.h" -char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]) { - unsigned n, k = 0; - - assert(s); - - /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */ - - for (n = 0; n < 16; n++) { - - if (IN_SET(n, 4, 6, 8, 10)) - s[k++] = '-'; - - s[k++] = hexchar(id.bytes[n] >> 4); - s[k++] = hexchar(id.bytes[n] & 0xF); - } - - assert(k == 36); - - s[k] = 0; - - return s; -} - bool id128_is_valid(const char *s) { size_t i, l; @@ -153,13 +130,13 @@ int id128_write_fd(int fd, Id128Format f, sd_id128_t id, bool do_sync) { assert(f < _ID128_FORMAT_MAX); if (f != ID128_UUID) { - sd_id128_to_string(id, buffer); - buffer[32] = '\n'; - sz = 33; + assert_se(sd_id128_to_string(id, buffer)); + buffer[SD_ID128_STRING_MAX - 1] = '\n'; + sz = SD_ID128_STRING_MAX; } else { - id128_to_uuid_string(id, buffer); - buffer[36] = '\n'; - sz = 37; + assert_se(sd_id128_to_uuid_string(id, buffer)); + buffer[SD_ID128_UUID_STRING_MAX - 1] = '\n'; + sz = SD_ID128_UUID_STRING_MAX; } r = loop_write(fd, buffer, sz, false); @@ -229,3 +206,19 @@ int id128_get_product(sd_id128_t *ret) { *ret = uuid; return 0; } + +int id128_equal_string(const char *s, sd_id128_t id) { + sd_id128_t parsed; + int r; + + if (!s) + return false; + + /* Checks if the specified string matches a valid string representation of the specified 128 bit ID/uuid */ + + r = sd_id128_from_string(s, &parsed); + if (r < 0) + return r; + + return sd_id128_equal(parsed, id); +} diff --git a/src/libsystemd/sd-id128/id128-util.h b/src/libsystemd/sd-id128/id128-util.h index b7327a1f0..65a278c8e 100644 --- a/src/libsystemd/sd-id128/id128-util.h +++ b/src/libsystemd/sd-id128/id128-util.h @@ -8,12 +8,6 @@ #include "hash-funcs.h" #include "macro.h" -#define ID128_UUID_STRING_MAX 37 - -char *id128_to_uuid_string(sd_id128_t id, char s[static ID128_UUID_STRING_MAX]); - -#define ID128_TO_UUID_STRING(id) id128_to_uuid_string((id), (char[ID128_UUID_STRING_MAX]) {}) - bool id128_is_valid(const char *s) _pure_; typedef enum Id128Format { @@ -40,3 +34,5 @@ extern const struct hash_ops id128_hash_ops; sd_id128_t id128_make_v4_uuid(sd_id128_t id); int id128_get_product(sd_id128_t *ret); + +int id128_equal_string(const char *s, sd_id128_t id); diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index 992b19130..09c3401ca 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -19,16 +19,36 @@ #include "util.h" _public_ char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]) { - unsigned n; - assert_return(s, NULL); - for (n = 0; n < 16; n++) { + for (size_t n = 0; n < 16; n++) { s[n*2] = hexchar(id.bytes[n] >> 4); s[n*2+1] = hexchar(id.bytes[n] & 0xF); } - s[32] = 0; + s[SD_ID128_STRING_MAX-1] = 0; + + return s; +} + +_public_ char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]) { + size_t k = 0; + + assert_return(s, NULL); + + /* Similar to sd_id128_to_string() but formats the result as UUID instead of plain hex chars */ + + for (size_t n = 0; n < 16; n++) { + + if (IN_SET(n, 4, 6, 8, 10)) + s[k++] = '-'; + + s[k++] = hexchar(id.bytes[n] >> 4); + s[k++] = hexchar(id.bytes[n] & 0xF); + } + + assert(k == SD_ID128_UUID_STRING_MAX - 1); + s[k] = 0; return s; } @@ -256,9 +276,7 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) { assert_return(ret, -EINVAL); - /* We allow usage if x86-64 RDRAND here. It might not be trusted enough for keeping secrets, but it should be - * fine for UUIDS. */ - r = genuine_random_bytes(&t, sizeof t, RANDOM_ALLOW_RDRAND); + r = genuine_random_bytes(&t, sizeof(t), 0); if (r < 0) return r; diff --git a/src/libsystemd/sd-journal/catalog.c b/src/libsystemd/sd-journal/catalog.c index 8fc87b131..b988ee3bb 100644 --- a/src/libsystemd/sd-journal/catalog.c +++ b/src/libsystemd/sd-journal/catalog.c @@ -125,15 +125,12 @@ static char *combine_entries(const char *one, const char *two) { /* Body from @one */ n = l1 - (b1 - one); - if (n > 0) { - memcpy(p, b1, n); - p += n; - + if (n > 0) + p = mempcpy(p, b1, n); /* Body from @two */ - } else { + else { n = l2 - (b2 - two); - memcpy(p, b2, n); - p += n; + p = mempcpy(p, b2, n); } assert(p - dest <= (ptrdiff_t)(l1 + l2)); @@ -442,7 +439,6 @@ error: int catalog_update(const char* database, const char* root, const char* const* dirs) { _cleanup_strv_free_ char **files = NULL; - char **f; _cleanup_(strbuf_freep) struct strbuf *sb = NULL; _cleanup_ordered_hashmap_free_free_free_ OrderedHashmap *h = NULL; _cleanup_free_ CatalogItem *items = NULL; @@ -709,7 +705,6 @@ int catalog_list(FILE *f, const char *database, bool oneline) { } int catalog_list_items(FILE *f, const char *database, bool oneline, char **items) { - char **item; int r = 0; STRV_FOREACH(item, items) { diff --git a/src/libsystemd/sd-journal/journal-authenticate.c b/src/libsystemd/sd-journal/journal-authenticate.c index 0ff25c1f4..83cbf4128 100644 --- a/src/libsystemd/sd-journal/journal-authenticate.c +++ b/src/libsystemd/sd-journal/journal-authenticate.c @@ -248,18 +248,18 @@ int journal_file_hmac_put_object(JournalFile *f, ObjectType type, Object *o, uin case OBJECT_DATA: /* All but hash and payload are mutable */ gcry_md_write(f->hmac, &o->data.hash, sizeof(o->data.hash)); - gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(DataObject, payload)); + gcry_md_write(f->hmac, o->data.payload, le64toh(o->object.size) - offsetof(Object, data.payload)); break; case OBJECT_FIELD: /* Same here */ gcry_md_write(f->hmac, &o->field.hash, sizeof(o->field.hash)); - gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(FieldObject, payload)); + gcry_md_write(f->hmac, o->field.payload, le64toh(o->object.size) - offsetof(Object, field.payload)); break; case OBJECT_ENTRY: /* All */ - gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(EntryObject, seqnum)); + gcry_md_write(f->hmac, &o->entry.seqnum, le64toh(o->object.size) - offsetof(Object, entry.seqnum)); break; case OBJECT_FIELD_HASH_TABLE: diff --git a/src/libsystemd/sd-journal/journal-file.c b/src/libsystemd/sd-journal/journal-file.c index 369b32856..b0f552500 100644 --- a/src/libsystemd/sd-journal/journal-file.c +++ b/src/libsystemd/sd-journal/journal-file.c @@ -91,8 +91,7 @@ # pragma GCC diagnostic ignored "-Waddress-of-packed-member" #endif -int journal_file_tail_end(JournalFile *f, uint64_t *ret_offset) { - Object tail; +int journal_file_tail_end_by_pread(JournalFile *f, uint64_t *ret_offset) { uint64_t p; int r; @@ -100,13 +99,17 @@ int journal_file_tail_end(JournalFile *f, uint64_t *ret_offset) { assert(f->header); assert(ret_offset); + /* Same as journal_file_tail_end_by_mmap() below, but operates with pread() to avoid the mmap cache + * (and thus is thread safe) */ + p = le64toh(f->header->tail_object_offset); if (p == 0) p = le64toh(f->header->header_size); else { + Object tail; uint64_t sz; - r = journal_file_read_object(f, OBJECT_UNUSED, p, &tail); + r = journal_file_read_object_header(f, OBJECT_UNUSED, p, &tail); if (r < 0) return r; @@ -126,6 +129,43 @@ int journal_file_tail_end(JournalFile *f, uint64_t *ret_offset) { return 0; } +int journal_file_tail_end_by_mmap(JournalFile *f, uint64_t *ret_offset) { + uint64_t p; + int r; + + assert(f); + assert(f->header); + assert(ret_offset); + + /* Same as journal_file_tail_end_by_pread() above, but operates with the usual mmap logic */ + + p = le64toh(f->header->tail_object_offset); + if (p == 0) + p = le64toh(f->header->header_size); + else { + Object *tail; + uint64_t sz; + + r = journal_file_move_to_object(f, OBJECT_UNUSED, p, &tail); + if (r < 0) + return r; + + sz = le64toh(READ_NOW(tail->object.size)); + if (sz > UINT64_MAX - sizeof(uint64_t) + 1) + return -EBADMSG; + + sz = ALIGN64(sz); + if (p > UINT64_MAX - sz) + return -EBADMSG; + + p += sz; + } + + *ret_offset = p; + + return 0; +} + int journal_file_set_offline_thread_join(JournalFile *f) { int r; @@ -618,10 +658,10 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o) le64toh(o->data.n_entries), offset); - if (le64toh(o->object.size) <= offsetof(DataObject, payload)) + if (le64toh(o->object.size) <= offsetof(Object, data.payload)) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Bad object size (<= %zu): %" PRIu64 ": %" PRIu64, - offsetof(DataObject, payload), + offsetof(Object, data.payload), le64toh(o->object.size), offset); @@ -640,10 +680,10 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o) break; case OBJECT_FIELD: - if (le64toh(o->object.size) <= offsetof(FieldObject, payload)) + if (le64toh(o->object.size) <= offsetof(Object, field.payload)) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Bad field size (<= %zu): %" PRIu64 ": %" PRIu64, - offsetof(FieldObject, payload), + offsetof(Object, field.payload), le64toh(o->object.size), offset); @@ -660,18 +700,18 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o) uint64_t sz; sz = le64toh(READ_NOW(o->object.size)); - if (sz < offsetof(EntryObject, items) || - (sz - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) + if (sz < offsetof(Object, entry.items) || + (sz - offsetof(Object, entry.items)) % sizeof(EntryItem) != 0) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Bad entry size (<= %zu): %" PRIu64 ": %" PRIu64, - offsetof(EntryObject, items), + offsetof(Object, entry.items), sz, offset); - if ((sz - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) + if ((sz - offsetof(Object, entry.items)) / sizeof(EntryItem) <= 0) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid number items in entry: %" PRIu64 ": %" PRIu64, - (sz - offsetof(EntryObject, items)) / sizeof(EntryItem), + (sz - offsetof(Object, entry.items)) / sizeof(EntryItem), offset); if (le64toh(o->entry.seqnum) <= 0) @@ -700,9 +740,9 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o) uint64_t sz; sz = le64toh(READ_NOW(o->object.size)); - if (sz < offsetof(HashTableObject, items) || - (sz - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 || - (sz - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) + if (sz < offsetof(Object, hash_table.items) || + (sz - offsetof(Object, hash_table.items)) % sizeof(HashItem) != 0 || + (sz - offsetof(Object, hash_table.items)) / sizeof(HashItem) <= 0) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid %s hash table size: %" PRIu64 ": %" PRIu64, o->object.type == OBJECT_DATA_HASH_TABLE ? "data" : "field", @@ -716,9 +756,9 @@ static int journal_file_check_object(JournalFile *f, uint64_t offset, Object *o) uint64_t sz; sz = le64toh(READ_NOW(o->object.size)); - if (sz < offsetof(EntryArrayObject, items) || - (sz - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 || - (sz - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) + if (sz < offsetof(Object, entry_array.items) || + (sz - offsetof(Object, entry_array.items)) % sizeof(le64_t) != 0 || + (sz - offsetof(Object, entry_array.items)) / sizeof(le64_t) <= 0) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Invalid object entry array size: %" PRIu64 ": %" PRIu64, sz, @@ -758,7 +798,6 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset uint64_t s; assert(f); - assert(ret); /* Objects may only be located at multiple of 64 bit */ if (!VALID64(offset)) @@ -813,17 +852,19 @@ int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset if (r < 0) return r; - *ret = o; + if (ret) + *ret = o; + return 0; } -int journal_file_read_object(JournalFile *f, ObjectType type, uint64_t offset, Object *ret) { - int r; - Object o; +int journal_file_read_object_header(JournalFile *f, ObjectType type, uint64_t offset, Object *ret) { uint64_t s; + ssize_t n; + Object o; + int r; assert(f); - assert(ret); /* Objects may only be located at multiple of 64 bit */ if (!VALID64(offset)) @@ -838,17 +879,22 @@ int journal_file_read_object(JournalFile *f, ObjectType type, uint64_t offset, O offset); /* This will likely read too much data but it avoids having to call pread() twice. */ - r = pread(f->fd, &o, sizeof(Object), offset); - if (r < 0) - return r; + n = pread(f->fd, &o, sizeof(o), offset); + if (n < 0) + return log_debug_errno(errno, "Failed to read journal file at offset: %" PRIu64, + offset); + + if ((size_t) n < sizeof(o.object)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Failed to read short object at offset: %" PRIu64, + offset); s = le64toh(o.object.size); - if (s == 0) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Attempt to read uninitialized object: %" PRIu64, offset); - if (s < sizeof(ObjectHeader)) + if (s < sizeof(o.object)) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Attempt to read overly short object: %" PRIu64, offset); @@ -863,6 +909,11 @@ int journal_file_read_object(JournalFile *f, ObjectType type, uint64_t offset, O "Attempt to read truncated object: %" PRIu64, offset); + if ((size_t) n < minimum_header_size(&o)) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "Short read while reading object: %" PRIu64, + offset); + if (type > OBJECT_UNUSED && o.object.type != type) return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "Attempt to read object of unexpected type: %" PRIu64, @@ -872,7 +923,9 @@ int journal_file_read_object(JournalFile *f, ObjectType type, uint64_t offset, O if (r < 0) return r; - *ret = o; + if (ret) + *ret = o; + return 0; } @@ -928,7 +981,7 @@ int journal_file_append_object( if (r < 0) return r; - r = journal_file_tail_end(f, &p); + r = journal_file_tail_end_by_mmap(f, &p); if (r < 0) return r; @@ -1453,19 +1506,11 @@ static int journal_file_append_field( hash = journal_file_hash_data(f, field, size); - r = journal_file_find_field_object_with_hash(f, field, size, hash, &o, &p); + r = journal_file_find_field_object_with_hash(f, field, size, hash, ret, ret_offset); if (r < 0) return r; - if (r > 0) { - - if (ret) - *ret = o; - - if (ret_offset) - *ret_offset = p; - + if (r > 0) return 0; - } osize = offsetof(Object, field.payload) + size; r = journal_file_append_object(f, OBJECT_FIELD, osize, &o, &p); @@ -1479,20 +1524,20 @@ static int journal_file_append_field( if (r < 0) return r; - /* The linking might have altered the window, so let's - * refresh our pointer */ - r = journal_file_move_to_object(f, OBJECT_FIELD, p, &o); - if (r < 0) - return r; + /* The linking might have altered the window, so let's only pass the offset to hmac which will + * move to the object again if needed. */ #if HAVE_GCRYPT - r = journal_file_hmac_put_object(f, OBJECT_FIELD, o, p); + r = journal_file_hmac_put_object(f, OBJECT_FIELD, NULL, p); if (r < 0) return r; #endif - if (ret) - *ret = o; + if (ret) { + r = journal_file_move_to_object(f, OBJECT_FIELD, p, ret); + if (r < 0) + return r; + } if (ret_offset) *ret_offset = p; @@ -1517,19 +1562,11 @@ static int journal_file_append_data( hash = journal_file_hash_data(f, data, size); - r = journal_file_find_data_object_with_hash(f, data, size, hash, &o, &p); + r = journal_file_find_data_object_with_hash(f, data, size, hash, ret, ret_offset); if (r < 0) return r; - if (r > 0) { - - if (ret) - *ret = o; - - if (ret_offset) - *ret_offset = p; - + if (r > 0) return 0; - } eq = memchr(data, '=', size); if (!eq) @@ -1567,18 +1604,17 @@ static int journal_file_append_data( if (r < 0) return r; + /* The linking might have altered the window, so let's refresh our pointer. */ + r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); + if (r < 0) + return r; + #if HAVE_GCRYPT r = journal_file_hmac_put_object(f, OBJECT_DATA, o, p); if (r < 0) return r; #endif - /* The linking might have altered the window, so let's - * refresh our pointer */ - r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); - if (r < 0) - return r; - /* Create field object ... */ r = journal_file_append_field(f, data, (uint8_t*) eq - (uint8_t*) data, &fo, &fp); if (r < 0) @@ -1801,12 +1837,20 @@ static int journal_file_link_entry(JournalFile *f, Object *o, uint64_t offset) { /* Link up the items */ n = journal_file_entry_n_items(o); for (uint64_t i = 0; i < n; i++) { - r = journal_file_link_entry_item(f, o, offset, i); - if (r < 0) - return r; + int k; + + /* If we fail to link an entry item because we can't allocate a new entry array, don't fail + * immediately but try to link the other entry items since it might still be possible to link + * those if they don't require a new entry array to be allocated. */ + + k = journal_file_link_entry_item(f, o, offset, i); + if (k == -E2BIG) + r = k; + else if (k < 0) + return k; } - return 0; + return r; } static int journal_file_append_entry_internal( @@ -1887,11 +1931,18 @@ static int post_change_thunk(sd_event_source *timer, uint64_t usec, void *userda } static void schedule_post_change(JournalFile *f) { + sd_event *e; int r; assert(f); assert(f->post_change_timer); + assert_se(e = sd_event_source_get_event(f->post_change_timer)); + + /* If we are already going down, post the change immediately. */ + if (IN_SET(sd_event_get_state(e), SD_EVENT_EXITING, SD_EVENT_FINISHED)) + goto fail; + r = sd_event_source_get_enabled(f->post_change_timer, NULL); if (r < 0) { log_debug_errno(r, "Failed to get ftruncate timer state: %m"); @@ -2161,7 +2212,7 @@ static int generic_array_get( direction_t direction, Object **ret, uint64_t *ret_offset) { - Object *o, *e; + Object *o; uint64_t p = 0, a, t = 0, k; int r; ChainCacheItem *ci; @@ -2231,9 +2282,16 @@ static int generic_array_get( do { p = le64toh(o->entry_array.items[i]); - r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &e); - if (r >= 0) - goto found; + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, ret); + if (r >= 0) { + /* Let's cache this item for the next invocation */ + chain_cache_put(f->chain_cache, ci, first, a, le64toh(o->entry_array.items[0]), t, i); + + if (ret_offset) + *ret_offset = p; + + return 1; + } if (!IN_SET(r, -EADDRNOTAVAIL, -EBADMSG)) return r; @@ -2251,18 +2309,6 @@ static int generic_array_get( } return 0; - -found: - /* Let's cache this item for the next invocation */ - chain_cache_put(f->chain_cache, ci, first, a, le64toh(o->entry_array.items[0]), t, i); - - if (ret) - *ret = e; - - if (ret_offset) - *ret_offset = p; - - return 1; } static int generic_array_get_plus_one( @@ -2273,21 +2319,17 @@ static int generic_array_get_plus_one( direction_t direction, Object **ret, uint64_t *ret_offset) { - Object *o; int r; assert(f); if (i == 0) { - r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); + r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, ret); if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG)) return generic_array_get(f, first, 0, direction, ret, ret_offset); if (r < 0) return r; - if (ret) - *ret = o; - if (ret_offset) *ret_offset = extra; @@ -2316,7 +2358,7 @@ static int generic_array_bisect( uint64_t a, p, t = 0, i = 0, last_p = 0, last_index = UINT64_MAX; bool subtract_one = false; - Object *o, *array = NULL; + Object *array = NULL; int r; ChainCacheItem *ci; @@ -2504,12 +2546,11 @@ found: else p = le64toh(array->entry_array.items[i]); - r = journal_file_move_to_object(f, OBJECT_ENTRY, p, &o); - if (r < 0) - return r; - - if (ret) - *ret = o; + if (ret) { + r = journal_file_move_to_object(f, OBJECT_ENTRY, p, ret); + if (r < 0) + return r; + } if (ret_offset) *ret_offset = p; @@ -2534,7 +2575,6 @@ static int generic_array_bisect_plus_one( int r; bool step_back = false; - Object *o; assert(f); assert(test_object); @@ -2577,12 +2617,11 @@ static int generic_array_bisect_plus_one( return r; found: - r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, &o); - if (r < 0) - return r; - - if (ret) - *ret = o; + if (ret) { + r = journal_file_move_to_object(f, OBJECT_ENTRY, extra, ret); + if (r < 0) + return r; + } if (ret_offset) *ret_offset = extra; @@ -2605,6 +2644,26 @@ _pure_ static int test_object_offset(JournalFile *f, uint64_t p, uint64_t needle return TEST_RIGHT; } +int journal_file_move_to_entry_by_offset( + JournalFile *f, + uint64_t p, + direction_t direction, + Object **ret, + uint64_t *ret_offset) { + + assert(f); + assert(f->header); + + return generic_array_bisect( + f, + le64toh(f->header->entry_array_offset), + le64toh(f->header->n_entries), + p, + test_object_offset, + direction, + ret, ret_offset, NULL); +} + static int test_object_seqnum(JournalFile *f, uint64_t p, uint64_t needle) { uint64_t sq; Object *o; @@ -2884,19 +2943,16 @@ int journal_file_next_entry( int journal_file_next_entry_for_data( JournalFile *f, - uint64_t data_offset, + Object *d, direction_t direction, Object **ret, uint64_t *ret_offset) { uint64_t i, n, ofs; - Object *d; int r; assert(f); - - r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); - if (r < 0) - return r; + assert(d); + assert(d->object.type == OBJECT_DATA); n = le64toh(READ_NOW(d->data.n_entries)); if (n <= 0) @@ -2921,19 +2977,14 @@ int journal_file_next_entry_for_data( int journal_file_move_to_entry_by_offset_for_data( JournalFile *f, - uint64_t data_offset, + Object *d, uint64_t p, direction_t direction, Object **ret, uint64_t *ret_offset) { - int r; - Object *d; - assert(f); - - r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); - if (r < 0) - return r; + assert(d); + assert(d->object.type == OBJECT_DATA); return generic_array_bisect_plus_one( f, @@ -2948,17 +2999,24 @@ int journal_file_move_to_entry_by_offset_for_data( int journal_file_move_to_entry_by_monotonic_for_data( JournalFile *f, - uint64_t data_offset, + Object *d, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *ret_offset) { - Object *o, *d; + Object *o; int r; - uint64_t b, z; + uint64_t b, z, entry_offset, entry_array_offset, n_entries; assert(f); + assert(d); + assert(d->object.type == OBJECT_DATA); + + /* Save all the required data before the data object gets invalidated. */ + entry_offset = le64toh(READ_NOW(d->data.entry_offset)); + entry_array_offset = le64toh(READ_NOW(d->data.entry_array_offset)); + n_entries = le64toh(READ_NOW(d->data.n_entries)); /* First, seek by time */ r = find_data_object_by_boot_id(f, boot_id, &o, &b); @@ -2981,18 +3039,17 @@ int journal_file_move_to_entry_by_monotonic_for_data( /* And now, continue seeking until we find an entry that * exists in both bisection arrays */ + r = journal_file_move_to_object(f, OBJECT_DATA, b, &o); + if (r < 0) + return r; + for (;;) { - Object *qo; uint64_t p, q; - r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); - if (r < 0) - return r; - r = generic_array_bisect_plus_one(f, - le64toh(d->data.entry_offset), - le64toh(d->data.entry_array_offset), - le64toh(d->data.n_entries), + entry_offset, + entry_array_offset, + n_entries, z, test_object_offset, direction, @@ -3000,10 +3057,6 @@ int journal_file_move_to_entry_by_monotonic_for_data( if (r <= 0) return r; - r = journal_file_move_to_object(f, OBJECT_DATA, b, &o); - if (r < 0) - return r; - r = generic_array_bisect_plus_one(f, le64toh(o->data.entry_offset), le64toh(o->data.entry_array_offset), @@ -3011,14 +3064,18 @@ int journal_file_move_to_entry_by_monotonic_for_data( p, test_object_offset, direction, - &qo, &q, NULL); + NULL, &q, NULL); if (r <= 0) return r; if (p == q) { - if (ret) - *ret = qo; + if (ret) { + r = journal_file_move_to_object(f, OBJECT_ENTRY, q, ret); + if (r < 0) + return r; + } + if (ret_offset) *ret_offset = q; @@ -3031,19 +3088,14 @@ int journal_file_move_to_entry_by_monotonic_for_data( int journal_file_move_to_entry_by_seqnum_for_data( JournalFile *f, - uint64_t data_offset, + Object *d, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *ret_offset) { - Object *d; - int r; - assert(f); - - r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); - if (r < 0) - return r; + assert(d); + assert(d->object.type == OBJECT_DATA); return generic_array_bisect_plus_one( f, @@ -3058,19 +3110,14 @@ int journal_file_move_to_entry_by_seqnum_for_data( int journal_file_move_to_entry_by_realtime_for_data( JournalFile *f, - uint64_t data_offset, + Object *d, uint64_t realtime, direction_t direction, Object **ret, uint64_t *ret_offset) { - Object *d; - int r; - assert(f); - - r = journal_file_move_to_object(f, OBJECT_DATA, data_offset, &d); - if (r < 0) - return r; + assert(d); + assert(d->object.type == OBJECT_DATA); return generic_array_bisect_plus_one( f, @@ -3274,11 +3321,10 @@ static int journal_file_warn_btrfs(JournalFile *f) { int journal_file_open( int fd, const char *fname, - int flags, + int open_flags, + JournalFileFlags file_flags, mode_t mode, - bool compress, uint64_t compress_threshold_bytes, - bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, JournalFile *template, @@ -3293,10 +3339,13 @@ int journal_file_open( assert(fd >= 0 || fname); assert(mmap_cache); - if (!IN_SET((flags & O_ACCMODE), O_RDONLY, O_RDWR)) + if (!IN_SET((open_flags & O_ACCMODE), O_RDONLY, O_RDWR)) return -EINVAL; - if (fname && (flags & O_CREAT) && !endswith(fname, ".journal")) + if ((open_flags & O_ACCMODE) == O_RDONLY && FLAGS_SET(open_flags, O_CREAT)) + return -EINVAL; + + if (fname && (open_flags & O_CREAT) && !endswith(fname, ".journal")) return -EINVAL; f = new(JournalFile, 1); @@ -3307,21 +3356,21 @@ int journal_file_open( .fd = fd, .mode = mode, - .flags = flags, - .writable = (flags & O_ACCMODE) != O_RDONLY, + .open_flags = open_flags, + .writable = (open_flags & O_ACCMODE) != O_RDONLY, #if HAVE_ZSTD - .compress_zstd = compress, + .compress_zstd = FLAGS_SET(file_flags, JOURNAL_COMPRESS), #elif HAVE_LZ4 - .compress_lz4 = compress, + .compress_lz4 = FLAGS_SET(file_flags, JOURNAL_COMPRESS), #elif HAVE_XZ - .compress_xz = compress, + .compress_xz = FLAGS_SET(file_flags, JOURNAL_COMPRESS), #endif .compress_threshold_bytes = compress_threshold_bytes == UINT64_MAX ? DEFAULT_COMPRESS_THRESHOLD : MAX(MIN_COMPRESS_THRESHOLD, compress_threshold_bytes), #if HAVE_GCRYPT - .seal = seal, + .seal = FLAGS_SET(file_flags, JOURNAL_SEAL), #endif }; @@ -3330,7 +3379,7 @@ int journal_file_open( r = getenv_bool("SYSTEMD_JOURNAL_KEYED_HASH"); if (r < 0) { if (r != -ENXIO) - log_debug_errno(r, "Failed to parse $SYSTEMD_JOURNAL_KEYED_HASH environment variable, ignoring."); + log_debug_errno(r, "Failed to parse $SYSTEMD_JOURNAL_KEYED_HASH environment variable, ignoring: %m"); f->keyed_hash = true; } else f->keyed_hash = r; @@ -3381,9 +3430,9 @@ int journal_file_open( * or so, we likely fail quickly than block for long. For regular files O_NONBLOCK has no effect, hence * it doesn't hurt in that case. */ - f->fd = open(f->path, f->flags|O_CLOEXEC|O_NONBLOCK, f->mode); + f->fd = openat_report_new(AT_FDCWD, f->path, f->open_flags|O_CLOEXEC|O_NONBLOCK, f->mode, &newly_created); if (f->fd < 0) { - r = -errno; + r = f->fd; goto fail; } @@ -3393,20 +3442,28 @@ int journal_file_open( r = fd_nonblock(f->fd, false); if (r < 0) goto fail; + + if (!newly_created) { + r = journal_file_fstat(f); + if (r < 0) + goto fail; + } + } else { + r = journal_file_fstat(f); + if (r < 0) + goto fail; + + /* If we just got the fd passed in, we don't really know if we created the file anew */ + newly_created = f->last_stat.st_size == 0 && f->writable; } - f->cache_fd = mmap_cache_add_fd(mmap_cache, f->fd, prot_from_flags(flags)); + f->cache_fd = mmap_cache_add_fd(mmap_cache, f->fd, prot_from_flags(open_flags)); if (!f->cache_fd) { r = -ENOMEM; goto fail; } - r = journal_file_fstat(f); - if (r < 0) - goto fail; - - if (f->last_stat.st_size == 0 && f->writable) { - + if (newly_created) { (void) journal_file_warn_btrfs(f); /* Let's attach the creation time to the journal file, so that the vacuuming code knows the age of this @@ -3433,8 +3490,6 @@ int journal_file_open( r = journal_file_fstat(f); if (r < 0) goto fail; - - newly_created = true; } if (f->last_stat.st_size < (off_t) HEADER_SIZE_MIN) { @@ -3632,21 +3687,16 @@ int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint6 for (uint64_t i = 0; i < n; i++) { uint64_t l, h; - le64_t le_hash; size_t t; void *data; Object *u; q = le64toh(o->entry.items[i].object_offset); - le_hash = o->entry.items[i].hash; r = journal_file_move_to_object(from, OBJECT_DATA, q, &o); if (r < 0) return r; - if (le_hash != o->data.hash) - return -EBADMSG; - l = le64toh(READ_NOW(o->object.size)); if (l < offsetof(Object, data.payload)) return -EBADMSG; diff --git a/src/libsystemd/sd-journal/journal-file.h b/src/libsystemd/sd-journal/journal-file.h index 39e91d71c..cc2f41f63 100644 --- a/src/libsystemd/sd-journal/journal-file.h +++ b/src/libsystemd/sd-journal/journal-file.h @@ -18,7 +18,7 @@ #include "time-util.h" typedef struct JournalMetrics { - /* For all these: -1 means "pick automatically", and 0 means "no limit enforced" */ + /* For all these: UINT64_MAX means "pick automatically", and 0 means "no limit enforced" */ uint64_t max_size; /* how large journal files grow at max */ uint64_t min_size; /* how large journal files grow at least */ uint64_t max_use; /* how much disk space to use in total at max, keep_free permitting */ @@ -62,7 +62,7 @@ typedef struct JournalFile { mode_t mode; - int flags; + int open_flags; bool writable:1; bool compress_xz:1; bool compress_lz4:1; @@ -126,14 +126,18 @@ typedef struct JournalFile { #endif } JournalFile; +typedef enum JournalFileFlags { + JOURNAL_COMPRESS = 1 << 0, + JOURNAL_SEAL = 1 << 1, +} JournalFileFlags; + int journal_file_open( int fd, const char *fname, - int flags, + int open_flags, + JournalFileFlags file_flags, mode_t mode, - bool compress, uint64_t compress_threshold_bytes, - bool seal, JournalMetrics *metrics, MMapCache *mmap_cache, JournalFile *template, @@ -185,15 +189,16 @@ static inline bool VALID_EPOCH(uint64_t u) { FLAGS_SET(le32toh((h)->incompatible_flags), HEADER_INCOMPATIBLE_KEYED_HASH) int journal_file_move_to_object(JournalFile *f, ObjectType type, uint64_t offset, Object **ret); -int journal_file_read_object(JournalFile *f, ObjectType type, uint64_t offset, Object *ret); +int journal_file_read_object_header(JournalFile *f, ObjectType type, uint64_t offset, Object *ret); -int journal_file_tail_end(JournalFile *f, uint64_t *ret_offset); +int journal_file_tail_end_by_pread(JournalFile *f, uint64_t *ret_offset); +int journal_file_tail_end_by_mmap(JournalFile *f, uint64_t *ret_offset); uint64_t journal_file_entry_n_items(Object *o) _pure_; uint64_t journal_file_entry_array_n_items(Object *o) _pure_; uint64_t journal_file_hash_table_n_items(Object *o) _pure_; -int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *offset); +int journal_file_append_object(JournalFile *f, ObjectType type, uint64_t size, Object **ret, uint64_t *ret_offset); int journal_file_append_entry( JournalFile *f, const dual_timestamp *ts, @@ -201,29 +206,30 @@ int journal_file_append_entry( const struct iovec iovec[], unsigned n_iovec, uint64_t *seqno, Object **ret, - uint64_t *offset); + uint64_t *ret_offset); -int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *offset); -int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset); +int journal_file_find_data_object(JournalFile *f, const void *data, uint64_t size, Object **ret, uint64_t *ret_offset); +int journal_file_find_data_object_with_hash(JournalFile *f, const void *data, uint64_t size, uint64_t hash, Object **ret, uint64_t *ret_offset); -int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t size, Object **ret, uint64_t *offset); -int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *offset); +int journal_file_find_field_object(JournalFile *f, const void *field, uint64_t size, Object **ret, uint64_t *ret_offset); +int journal_file_find_field_object_with_hash(JournalFile *f, const void *field, uint64_t size, uint64_t hash, Object **ret, uint64_t *ret_offset); void journal_file_reset_location(JournalFile *f); void journal_file_save_location(JournalFile *f, Object *o, uint64_t offset); int journal_file_compare_locations(JournalFile *af, JournalFile *bf); -int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_next_entry(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *ret_offset); -int journal_file_next_entry_for_data(JournalFile *f, uint64_t data_offset, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_next_entry_for_data(JournalFile *f, Object *d, direction_t direction, Object **ret, uint64_t *ret_offset); -int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); -int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); -int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_offset(JournalFile *f, uint64_t p, direction_t direction, Object **ret, uint64_t *ret_offset); +int journal_file_move_to_entry_by_seqnum(JournalFile *f, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *ret_offset); +int journal_file_move_to_entry_by_realtime(JournalFile *f, uint64_t realtime, direction_t direction, Object **ret, uint64_t *ret_offset); +int journal_file_move_to_entry_by_monotonic(JournalFile *f, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *ret_offset); -int journal_file_move_to_entry_by_offset_for_data(JournalFile *f, uint64_t data_offset, uint64_t p, direction_t direction, Object **ret, uint64_t *offset); -int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, uint64_t data_offset, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *offset); -int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, uint64_t data_offset, uint64_t realtime, direction_t direction, Object **ret, uint64_t *offset); -int journal_file_move_to_entry_by_monotonic_for_data(JournalFile *f, uint64_t data_offset, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *offset); +int journal_file_move_to_entry_by_offset_for_data(JournalFile *f, Object *d, uint64_t p, direction_t direction, Object **ret, uint64_t *ret_offset); +int journal_file_move_to_entry_by_seqnum_for_data(JournalFile *f, Object *d, uint64_t seqnum, direction_t direction, Object **ret, uint64_t *ret_offset); +int journal_file_move_to_entry_by_realtime_for_data(JournalFile *f, Object *d, uint64_t realtime, direction_t direction, Object **ret, uint64_t *ret_offset); +int journal_file_move_to_entry_by_monotonic_for_data(JournalFile *f, Object *d, sd_id128_t boot_id, uint64_t monotonic, direction_t direction, Object **ret, uint64_t *ret_offset); int journal_file_copy_entry(JournalFile *from, JournalFile *to, Object *o, uint64_t p); diff --git a/src/libsystemd/sd-journal/journal-vacuum.c b/src/libsystemd/sd-journal/journal-vacuum.c index 8296448ed..eac350020 100644 --- a/src/libsystemd/sd-journal/journal-vacuum.c +++ b/src/libsystemd/sd-journal/journal-vacuum.c @@ -64,15 +64,15 @@ static void patch_realtime( assert(realtime); x = timespec_load(&st->st_ctim); - if (x > 0 && x != USEC_INFINITY && x < *realtime) + if (timestamp_is_set(x) && x < *realtime) *realtime = x; x = timespec_load(&st->st_atim); - if (x > 0 && x != USEC_INFINITY && x < *realtime) + if (timestamp_is_set(x) && x < *realtime) *realtime = x; x = timespec_load(&st->st_mtim); - if (x > 0 && x != USEC_INFINITY && x < *realtime) + if (timestamp_is_set(x) && x < *realtime) *realtime = x; /* Let's read the original creation time, if possible. Ideally we'd just query the creation time the diff --git a/src/libsystemd/sd-journal/journal-verify.c b/src/libsystemd/sd-journal/journal-verify.c index 8288ebcd6..56eaecb10 100644 --- a/src/libsystemd/sd-journal/journal-verify.c +++ b/src/libsystemd/sd-journal/journal-verify.c @@ -137,8 +137,6 @@ static int hash_payload(JournalFile *f, Object *o, uint64_t offset, const uint8_ } static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o) { - uint64_t i; - assert(f); assert(offset); assert(o); @@ -169,9 +167,9 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o return -EBADMSG; } - if (le64toh(o->object.size) - offsetof(DataObject, payload) <= 0) { + if (le64toh(o->object.size) - offsetof(Object, data.payload) <= 0) { error(offset, "Bad object size (<= %zu): %"PRIu64, - offsetof(DataObject, payload), + offsetof(Object, data.payload), le64toh(o->object.size)); return -EBADMSG; } @@ -207,10 +205,10 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o uint64_t h1, h2; int r; - if (le64toh(o->object.size) - offsetof(FieldObject, payload) <= 0) { + if (le64toh(o->object.size) - offsetof(Object, field.payload) <= 0) { error(offset, "Bad field size (<= %zu): %"PRIu64, - offsetof(FieldObject, payload), + offsetof(Object, field.payload), le64toh(o->object.size)); return -EBADMSG; } @@ -239,18 +237,18 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o } case OBJECT_ENTRY: - if ((le64toh(o->object.size) - offsetof(EntryObject, items)) % sizeof(EntryItem) != 0) { + if ((le64toh(o->object.size) - offsetof(Object, entry.items)) % sizeof(EntryItem) != 0) { error(offset, "Bad entry size (<= %zu): %"PRIu64, - offsetof(EntryObject, items), + offsetof(Object, entry.items), le64toh(o->object.size)); return -EBADMSG; } - if ((le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem) <= 0) { + if ((le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem) <= 0) { error(offset, "Invalid number items in entry: %"PRIu64, - (le64toh(o->object.size) - offsetof(EntryObject, items)) / sizeof(EntryItem)); + (le64toh(o->object.size) - offsetof(Object, entry.items)) / sizeof(EntryItem)); return -EBADMSG; } @@ -275,7 +273,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o return -EBADMSG; } - for (i = 0; i < journal_file_entry_n_items(o); i++) { + for (uint64_t i = 0; i < journal_file_entry_n_items(o); i++) { if (le64toh(o->entry.items[i].object_offset) == 0 || !VALID64(le64toh(o->entry.items[i].object_offset))) { error(offset, @@ -290,8 +288,8 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o case OBJECT_DATA_HASH_TABLE: case OBJECT_FIELD_HASH_TABLE: - if ((le64toh(o->object.size) - offsetof(HashTableObject, items)) % sizeof(HashItem) != 0 || - (le64toh(o->object.size) - offsetof(HashTableObject, items)) / sizeof(HashItem) <= 0) { + if ((le64toh(o->object.size) - offsetof(Object, hash_table.items)) % sizeof(HashItem) != 0 || + (le64toh(o->object.size) - offsetof(Object, hash_table.items)) / sizeof(HashItem) <= 0) { error(offset, "Invalid %s size: %"PRIu64, journal_object_type_to_string(o->object.type), @@ -299,7 +297,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o return -EBADMSG; } - for (i = 0; i < journal_file_hash_table_n_items(o); i++) { + for (uint64_t i = 0; i < journal_file_hash_table_n_items(o); i++) { if (o->hash_table.items[i].head_hash_offset != 0 && !VALID64(le64toh(o->hash_table.items[i].head_hash_offset))) { error(offset, @@ -334,8 +332,8 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o break; case OBJECT_ENTRY_ARRAY: - if ((le64toh(o->object.size) - offsetof(EntryArrayObject, items)) % sizeof(le64_t) != 0 || - (le64toh(o->object.size) - offsetof(EntryArrayObject, items)) / sizeof(le64_t) <= 0) { + if ((le64toh(o->object.size) - offsetof(Object, entry_array.items)) % sizeof(le64_t) != 0 || + (le64toh(o->object.size) - offsetof(Object, entry_array.items)) / sizeof(le64_t) <= 0) { error(offset, "Invalid object entry array size: %"PRIu64, le64toh(o->object.size)); @@ -349,7 +347,7 @@ static int journal_file_object_verify(JournalFile *f, uint64_t offset, Object *o return -EBADMSG; } - for (i = 0; i < journal_file_entry_array_n_items(o); i++) + for (uint64_t i = 0; i < journal_file_entry_array_n_items(o); i++) if (le64toh(o->entry_array.items[i]) != 0 && !VALID64(le64toh(o->entry_array.items[i]))) { error(offset, @@ -422,92 +420,6 @@ static int contains_uint64(MMapFileDescriptor *f, uint64_t n, uint64_t p) { return 0; } -static int entry_points_to_data( - JournalFile *f, - MMapFileDescriptor *cache_entry_fd, - uint64_t n_entries, - uint64_t entry_p, - uint64_t data_p) { - - int r; - uint64_t i, n, a; - Object *o; - bool found = false; - - assert(f); - assert(cache_entry_fd); - - if (!contains_uint64(cache_entry_fd, n_entries, entry_p)) { - error(data_p, "Data object references invalid entry at "OFSfmt, entry_p); - return -EBADMSG; - } - - r = journal_file_move_to_object(f, OBJECT_ENTRY, entry_p, &o); - if (r < 0) - return r; - - n = journal_file_entry_n_items(o); - for (i = 0; i < n; i++) - if (le64toh(o->entry.items[i].object_offset) == data_p) { - found = true; - break; - } - - if (!found) { - error(entry_p, "Data object at "OFSfmt" not referenced by linked entry", data_p); - return -EBADMSG; - } - - /* Check if this entry is also in main entry array. Since the - * main entry array has already been verified we can rely on - * its consistency. */ - - i = 0; - n = le64toh(f->header->n_entries); - a = le64toh(f->header->entry_array_offset); - - while (i < n) { - uint64_t m, u; - - r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); - if (r < 0) - return r; - - m = journal_file_entry_array_n_items(o); - u = MIN(n - i, m); - - if (entry_p <= le64toh(o->entry_array.items[u-1])) { - uint64_t x, y, z; - - x = 0; - y = u; - - while (x < y) { - z = (x + y) / 2; - - if (le64toh(o->entry_array.items[z]) == entry_p) - return 0; - - if (x + 1 >= y) - break; - - if (entry_p < le64toh(o->entry_array.items[z])) - y = z; - else - x = z; - } - - error(entry_p, "Entry object doesn't exist in main entry array"); - return -EBADMSG; - } - - i += u; - a = le64toh(o->entry_array.next_entry_array_offset); - } - - return 0; -} - static int verify_data( JournalFile *f, Object *o, uint64_t p, @@ -538,9 +450,18 @@ static int verify_data( assert(o->data.entry_offset); last = q = le64toh(o->data.entry_offset); - r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p); + if (!contains_uint64(cache_entry_fd, n_entries, q)) { + error(p, "Data object references invalid entry at "OFSfmt, q); + return -EBADMSG; + } + + r = journal_file_move_to_entry_by_offset(f, q, DIRECTION_DOWN, NULL, NULL); if (r < 0) return r; + if (r == 0) { + error(q, "Entry object doesn't exist in the main entry array"); + return -EBADMSG; + } i = 1; while (i < n) { @@ -576,9 +497,18 @@ static int verify_data( } last = q; - r = entry_points_to_data(f, cache_entry_fd, n_entries, q, p); + if (!contains_uint64(cache_entry_fd, n_entries, q)) { + error(p, "Data object references invalid entry at "OFSfmt, q); + return -EBADMSG; + } + + r = journal_file_move_to_entry_by_offset(f, q, DIRECTION_DOWN, NULL, NULL); if (r < 0) return r; + if (r == 0) { + error(q, "Entry object doesn't exist in the main entry array"); + return -EBADMSG; + } /* Pointer might have moved, reposition */ r = journal_file_move_to_object(f, OBJECT_ENTRY_ARRAY, a, &o); @@ -703,7 +633,8 @@ static int data_object_in_hash_table(JournalFile *f, uint64_t hash, uint64_t p) static int verify_entry( JournalFile *f, Object *o, uint64_t p, - MMapFileDescriptor *cache_data_fd, uint64_t n_data) { + MMapFileDescriptor *cache_data_fd, uint64_t n_data, + bool last) { uint64_t i, n; int r; @@ -714,11 +645,10 @@ static int verify_entry( n = journal_file_entry_n_items(o); for (i = 0; i < n; i++) { - uint64_t q, h; + uint64_t q; Object *u; q = le64toh(o->entry.items[i].object_offset); - h = le64toh(o->entry.items[i].hash); if (!contains_uint64(cache_data_fd, n_data, q)) { error(p, "Invalid data object of entry"); @@ -729,18 +659,25 @@ static int verify_entry( if (r < 0) return r; - if (le64toh(u->data.hash) != h) { - error(p, "Hash mismatch for data object of entry"); - return -EBADMSG; - } - - r = data_object_in_hash_table(f, h, q); + r = data_object_in_hash_table(f, le64toh(u->data.hash), q); if (r < 0) return r; if (r == 0) { error(p, "Data object missing from hash table"); return -EBADMSG; } + + r = journal_file_move_to_entry_by_offset_for_data(f, u, p, DIRECTION_DOWN, NULL, NULL); + if (r < 0) + return r; + + /* The last entry object has a very high chance of not being referenced as journal files + * almost always run out of space during linking of entry items when trying to add a new + * entry array so let's not error in that scenario. */ + if (r == 0 && !last) { + error(p, "Entry object not referenced by linked data object at "OFSfmt, q); + return -EBADMSG; + } } return 0; @@ -812,7 +749,7 @@ static int verify_entry_array( if (r < 0) return r; - r = verify_entry(f, o, p, cache_data_fd, n_data); + r = verify_entry(f, o, p, cache_data_fd, n_data, /*last=*/ i + 1 == n); if (r < 0) return r; @@ -842,21 +779,21 @@ static int verify_hash_table( return -EBADMSG; } - if (header_offset != p + offsetof(HashTableObject, items)) { + if (header_offset != p + offsetof(Object, hash_table.items)) { error(p, "Header offset for %s invalid (%" PRIu64 " != %" PRIu64 ")", journal_object_type_to_string(o->object.type), header_offset, - p + offsetof(HashTableObject, items)); + p + offsetof(Object, hash_table.items)); return -EBADMSG; } - if (header_size != le64toh(o->object.size) - offsetof(HashTableObject, items)) { + if (header_size != le64toh(o->object.size) - offsetof(Object, hash_table.items)) { error(p, "Header size for %s invalid (%" PRIu64 " != %" PRIu64 ")", journal_object_type_to_string(o->object.type), header_size, - le64toh(o->object.size) - offsetof(HashTableObject, items)); + le64toh(o->object.size) - offsetof(Object, hash_table.items)); return -EBADMSG; } diff --git a/src/libsystemd/sd-journal/mmap-cache.c b/src/libsystemd/sd-journal/mmap-cache.c index 124ee3f8c..82407f939 100644 --- a/src/libsystemd/sd-journal/mmap-cache.c +++ b/src/libsystemd/sd-journal/mmap-cache.c @@ -86,7 +86,6 @@ MMapCache* mmap_cache_new(void) { } static void window_unlink(Window *w) { - Context *c; assert(w); @@ -306,7 +305,7 @@ static int find_mmap( size_t size, void **ret) { - Window *w; + Window *found = NULL; assert(f); assert(f->cache); @@ -318,16 +317,18 @@ static int find_mmap( return -EIO; LIST_FOREACH(by_fd, w, f->windows) - if (window_matches(w, offset, size)) + if (window_matches(w, offset, size)) { + found = w; break; + } - if (!w) + if (!found) return 0; - context_attach_window(f->cache, c, w); - w->keep_always = w->keep_always || keep_always; + context_attach_window(f->cache, c, found); + found->keep_always = found->keep_always || keep_always; - *ret = (uint8_t*) w->ptr + (offset - w->offset); + *ret = (uint8_t*) found->ptr + (offset - found->offset); f->cache->n_window_list_hit++; return 1; @@ -494,8 +495,6 @@ static void mmap_cache_process_sigbus(MMapCache *m) { ours = false; HASHMAP_FOREACH(f, m->fds) { - Window *w; - LIST_FOREACH(by_fd, w, f->windows) { if ((uint8_t*) addr >= (uint8_t*) w->ptr && (uint8_t*) addr < (uint8_t*) w->ptr + w->size) { @@ -523,8 +522,6 @@ static void mmap_cache_process_sigbus(MMapCache *m) { return; HASHMAP_FOREACH(f, m->fds) { - Window *w; - if (!f->sigbus) continue; diff --git a/src/libsystemd/sd-journal/sd-journal.c b/src/libsystemd/sd-journal/sd-journal.c index 7a6cc4aca..e8c66a5e2 100644 --- a/src/libsystemd/sd-journal/sd-journal.c +++ b/src/libsystemd/sd-journal/sd-journal.c @@ -233,7 +233,7 @@ static Match *match_free_if_empty(Match *m) { } _public_ int sd_journal_add_match(sd_journal *j, const void *data, size_t size) { - Match *l3, *l4, *add_here = NULL, *m = NULL; + Match *add_here = NULL, *m = NULL; uint64_t hash; assert_return(j, -EINVAL); @@ -372,7 +372,6 @@ _public_ int sd_journal_add_disjunction(sd_journal *j) { static char *match_make_string(Match *m) { char *p = NULL, *r; - Match *i; bool enclose = false; if (!m) @@ -501,7 +500,8 @@ static int next_for_match( assert(f); if (m->type == MATCH_DISCRETE) { - uint64_t dp, hash; + Object *d; + uint64_t hash; /* If the keyed hash logic is used, we need to calculate the hash fresh per file. Otherwise * we can use what we pre-calculated. */ @@ -510,14 +510,13 @@ static int next_for_match( else hash = m->hash; - r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, NULL, &dp); + r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, &d, NULL); if (r <= 0) return r; - return journal_file_move_to_entry_by_offset_for_data(f, dp, after_offset, direction, ret, offset); + return journal_file_move_to_entry_by_offset_for_data(f, d, after_offset, direction, ret, offset); } else if (m->type == MATCH_OR_TERM) { - Match *i; /* Find the earliest match beyond after_offset */ @@ -537,7 +536,7 @@ static int next_for_match( return 0; } else if (m->type == MATCH_AND_TERM) { - Match *i, *last_moved; + Match *last_moved; /* Always jump to the next matching entry and repeat * this until we find an offset that matches for all @@ -597,6 +596,7 @@ static int find_location_for_match( assert(f); if (m->type == MATCH_DISCRETE) { + Object *d; uint64_t dp, hash; if (JOURNAL_HEADER_KEYED_HASH(f->header)) @@ -604,32 +604,36 @@ static int find_location_for_match( else hash = m->hash; - r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, NULL, &dp); + r = journal_file_find_data_object_with_hash(f, m->data, m->size, hash, &d, &dp); if (r <= 0) return r; /* FIXME: missing: find by monotonic */ if (j->current_location.type == LOCATION_HEAD) - return journal_file_next_entry_for_data(f, dp, DIRECTION_DOWN, ret, offset); + return journal_file_next_entry_for_data(f, d, DIRECTION_DOWN, ret, offset); if (j->current_location.type == LOCATION_TAIL) - return journal_file_next_entry_for_data(f, dp, DIRECTION_UP, ret, offset); + return journal_file_next_entry_for_data(f, d, DIRECTION_UP, ret, offset); if (j->current_location.seqnum_set && sd_id128_equal(j->current_location.seqnum_id, f->header->seqnum_id)) - return journal_file_move_to_entry_by_seqnum_for_data(f, dp, j->current_location.seqnum, direction, ret, offset); + return journal_file_move_to_entry_by_seqnum_for_data(f, d, j->current_location.seqnum, direction, ret, offset); if (j->current_location.monotonic_set) { - r = journal_file_move_to_entry_by_monotonic_for_data(f, dp, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset); + r = journal_file_move_to_entry_by_monotonic_for_data(f, d, j->current_location.boot_id, j->current_location.monotonic, direction, ret, offset); if (r != -ENOENT) return r; + + /* The data object might have been invalidated. */ + r = journal_file_move_to_object(f, OBJECT_DATA, dp, &d); + if (r < 0) + return r; } if (j->current_location.realtime_set) - return journal_file_move_to_entry_by_realtime_for_data(f, dp, j->current_location.realtime, direction, ret, offset); + return journal_file_move_to_entry_by_realtime_for_data(f, d, j->current_location.realtime, direction, ret, offset); - return journal_file_next_entry_for_data(f, dp, direction, ret, offset); + return journal_file_next_entry_for_data(f, d, direction, ret, offset); } else if (m->type == MATCH_OR_TERM) { uint64_t np = 0; Object *n; - Match *i; /* Find the earliest match */ @@ -660,7 +664,6 @@ static int find_location_for_match( return 1; } else { - Match *i; uint64_t np = 0; assert(m->type == MATCH_AND_TERM); @@ -1310,8 +1313,7 @@ static int add_any_file( f = ordered_hashmap_get(j->files, path); if (f) { - if (f->last_stat.st_dev == st.st_dev && - f->last_stat.st_ino == st.st_ino) { + if (stat_inode_same(&f->last_stat, &st)) { /* We already track this file, under the same path and with the same device/inode numbers, it's * hence really the same. Mark this file as seen in this generation. This is used to GC old @@ -1335,7 +1337,7 @@ static int add_any_file( goto finish; } - r = journal_file_open(fd, path, O_RDONLY, 0, false, 0, false, NULL, j->mmap, NULL, &f); + r = journal_file_open(fd, path, O_RDONLY, 0, 0, 0, NULL, j->mmap, NULL, &f); if (r < 0) { log_debug_errno(r, "Failed to open journal file %s: %m", path); goto finish; @@ -2029,7 +2031,6 @@ _public_ int sd_journal_open_directory(sd_journal **ret, const char *path, int f _public_ int sd_journal_open_files(sd_journal **ret, const char **paths, int flags) { _cleanup_(sd_journal_closep) sd_journal *j = NULL; - const char **path; int r; assert_return(ret, -EINVAL); @@ -2296,12 +2297,10 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void ** for (i = 0; i < n; i++) { Object *d; uint64_t p, l; - le64_t le_hash; size_t t; int compression; p = le64toh(o->entry.items[i].object_offset); - le_hash = o->entry.items[i].hash; r = journal_file_move_to_object(f, OBJECT_DATA, p, &d); if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG)) { log_debug_errno(r, "Entry item %"PRIu64" data object is bad, skipping over it: %m", i); @@ -2310,11 +2309,6 @@ _public_ int sd_journal_get_data(sd_journal *j, const char *field, const void ** if (r < 0) return r; - if (le_hash != d->data.hash) { - log_debug("Entry item %"PRIu64" hash is bad, skipping over it.", i); - continue; - } - l = le64toh(d->object.size) - offsetof(Object, data.payload); compression = d->object.flags & OBJECT_COMPRESSION_MASK; @@ -2443,10 +2437,8 @@ _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t for (uint64_t n = journal_file_entry_n_items(o); j->current_field < n; j->current_field++) { uint64_t p; - le64_t le_hash; p = le64toh(o->entry.items[j->current_field].object_offset); - le_hash = o->entry.items[j->current_field].hash; r = journal_file_move_to_object(f, OBJECT_DATA, p, &o); if (IN_SET(r, -EADDRNOTAVAIL, -EBADMSG)) { log_debug_errno(r, "Entry item %"PRIu64" data object is bad, skipping over it: %m", j->current_field); @@ -2455,11 +2447,6 @@ _public_ int sd_journal_enumerate_data(sd_journal *j, const void **data, size_t if (r < 0) return r; - if (le_hash != o->data.hash) { - log_debug("Entry item %"PRIu64" hash is bad, skipping over it.", j->current_field); - continue; - } - r = return_data(j, f, o, data, size); if (r == -EBADMSG) { log_debug("Entry item %"PRIu64" data payload is bad, skipping over it.", j->current_field); @@ -2690,7 +2677,6 @@ _public_ int sd_journal_process(sd_journal *j) { for (;;) { union inotify_event_buffer buffer; - struct inotify_event *e; ssize_t l; l = read(j->inotify_fd, &buffer, sizeof(buffer)); diff --git a/src/libsystemd/sd-journal/test-audit-type.c b/src/libsystemd/sd-journal/test-audit-type.c index 5adbf0d5b..1d5003bd9 100644 --- a/src/libsystemd/sd-journal/test-audit-type.c +++ b/src/libsystemd/sd-journal/test-audit-type.c @@ -4,6 +4,7 @@ #include #include "audit-type.h" +#include "tests.h" static void print_audit_label(int i) { const char *name; @@ -13,14 +14,11 @@ static void print_audit_label(int i) { printf("%i → %s → %s\n", i, audit_type_to_string(i), name); } -static void test_audit_type(void) { +TEST(audit_type) { int i; for (i = 0; i <= AUDIT_KERNEL; i++) print_audit_label(i); } -int main(int argc, char **argv) { - test_audit_type(); - return 0; -} +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/libsystemd/sd-journal/test-journal-send.c b/src/libsystemd/sd-journal/test-journal-send.c index 533b8d91e..8f9d9c4a3 100644 --- a/src/libsystemd/sd-journal/test-journal-send.c +++ b/src/libsystemd/sd-journal/test-journal-send.c @@ -10,8 +10,9 @@ #include "journal-send.h" #include "macro.h" #include "memory-util.h" +#include "tests.h" -static void test_journal_print(void) { +TEST(journal_print) { assert_se(sd_journal_print(LOG_INFO, "XXX") == 0); assert_se(sd_journal_print(LOG_INFO, "%s", "YYY") == 0); assert_se(sd_journal_print(LOG_INFO, "X%4094sY", "ZZZ") == 0); @@ -19,7 +20,7 @@ static void test_journal_print(void) { assert_se(sd_journal_print(LOG_INFO, "X%*sY", LONG_LINE_MAX - 8 - 2, "ZZZ") == -ENOBUFS); } -static void test_journal_send(void) { +TEST(journal_send) { _cleanup_free_ char *huge = NULL; #define HUGE_SIZE (4096*1024) @@ -98,13 +99,13 @@ static void test_journal_send(void) { closelog(); } -int main(int argc, char *argv[]) { - test_journal_print(); - test_journal_send(); - +static int outro(void) { /* Sleep a bit to make it easy for journald to collect metadata. */ sleep(1); close_journal_fd(); - return 0; + + return EXIT_SUCCESS; } + +DEFINE_TEST_MAIN_FULL(LOG_INFO, NULL, outro); diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index 00129ff77..f134f0a8c 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -269,13 +269,11 @@ _public_ int sd_uid_get_state(uid_t uid, char**state) { return r; r = parse_env_file(NULL, p, "STATE", &s); - if (r == -ENOENT) { + if (r == -ENOENT) r = free_and_strdup(&s, "offline"); - if (r < 0) - return r; - } else if (r < 0) + if (r < 0) return r; - else if (isempty(s)) + if (isempty(s)) return -EIO; *state = TAKE_PTR(s); diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c index 3fc394d5e..f7cef6e30 100644 --- a/src/libsystemd/sd-login/test-login.c +++ b/src/libsystemd/sd-login/test-login.c @@ -11,6 +11,7 @@ #include "log.h" #include "string-util.h" #include "strv.h" +#include "tests.h" #include "time-util.h" #include "user-util.h" @@ -35,7 +36,7 @@ static const char *e(int r) { return r == 0 ? "OK" : errno_to_name(r); } -static void test_login(void) { +TEST(login) { _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_free_ char *pp = NULL, *qq = NULL, *display_session = NULL, *cgroup = NULL, @@ -259,10 +260,13 @@ static void test_login(void) { } } -static void test_monitor(void) { +TEST(monitor) { sd_login_monitor *m = NULL; int r; + if (!streq_ptr(saved_argv[1], "-m")) + return; + assert_se(sd_login_monitor_new("session", &m) == 0); for (unsigned n = 0; n < 5; n++) { @@ -290,16 +294,9 @@ static void test_monitor(void) { sd_login_monitor_unref(m); } -int main(int argc, char* argv[]) { - log_parse_environment(); - log_open(); - +static int intro(void) { log_info("/* Information printed is from the live system */"); - - test_login(); - - if (streq_ptr(argv[1], "-m")) - test_monitor(); - - return 0; + return EXIT_SUCCESS; } + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/libsystemd/sd-netlink/netlink-message-rtnl.c b/src/libsystemd/sd-netlink/netlink-message-rtnl.c index d15ca06aa..56fb74727 100644 --- a/src/libsystemd/sd-netlink/netlink-message-rtnl.c +++ b/src/libsystemd/sd-netlink/netlink-message-rtnl.c @@ -1043,88 +1043,73 @@ int sd_rtnl_message_routing_policy_rule_get_fib_src_prefixlen(sd_netlink_message return 0; } -int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex) { +int sd_rtnl_message_new_traffic_control( + sd_netlink *rtnl, + sd_netlink_message **ret, + uint16_t nlmsg_type, + int ifindex, + uint32_t handle, + uint32_t parent) { + struct tcmsg *tcm; int r; - assert_return(rtnl_message_type_is_qdisc(nlmsg_type), -EINVAL); + assert_return(rtnl_message_type_is_traffic_control(nlmsg_type), -EINVAL); assert_return(ret, -EINVAL); r = message_new(rtnl, ret, nlmsg_type); if (r < 0) return r; - if (nlmsg_type == RTM_NEWQDISC) + if (IN_SET(nlmsg_type, RTM_NEWQDISC, RTM_NEWTCLASS)) (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; tcm = NLMSG_DATA((*ret)->hdr); - tcm->tcm_family = tcm_family; - tcm->tcm_ifindex = tcm_ifindex; - - return 0; -} - -int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent) { - struct tcmsg *tcm; - - assert_return(rtnl_message_type_is_qdisc(m->hdr->nlmsg_type), -EINVAL); - - tcm = NLMSG_DATA(m->hdr); + tcm->tcm_ifindex = ifindex; + tcm->tcm_handle = handle; tcm->tcm_parent = parent; return 0; } -int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle) { +int sd_rtnl_message_traffic_control_get_ifindex(sd_netlink_message *m, int *ret) { struct tcmsg *tcm; - assert_return(rtnl_message_type_is_qdisc(m->hdr->nlmsg_type), -EINVAL); - - tcm = NLMSG_DATA(m->hdr); - tcm->tcm_handle = handle; - - return 0; -} - -int sd_rtnl_message_new_tclass(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex) { - struct tcmsg *tcm; - int r; - - assert_return(rtnl_message_type_is_tclass(nlmsg_type), -EINVAL); + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_traffic_control(m->hdr->nlmsg_type), -EINVAL); assert_return(ret, -EINVAL); - r = message_new(rtnl, ret, nlmsg_type); - if (r < 0) - return r; - - if (nlmsg_type == RTM_NEWTCLASS) - (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_EXCL; - - tcm = NLMSG_DATA((*ret)->hdr); - tcm->tcm_family = tcm_family; - tcm->tcm_ifindex = tcm_ifindex; + tcm = NLMSG_DATA(m->hdr); + *ret = tcm->tcm_ifindex; return 0; } -int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent) { +int sd_rtnl_message_traffic_control_get_handle(sd_netlink_message *m, uint32_t *ret) { struct tcmsg *tcm; - assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL); + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_traffic_control(m->hdr->nlmsg_type), -EINVAL); + assert_return(ret, -EINVAL); tcm = NLMSG_DATA(m->hdr); - tcm->tcm_parent = parent; + *ret = tcm->tcm_handle; return 0; } -int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle) { +int sd_rtnl_message_traffic_control_get_parent(sd_netlink_message *m, uint32_t *ret) { struct tcmsg *tcm; - assert_return(rtnl_message_type_is_tclass(m->hdr->nlmsg_type), -EINVAL); + assert_return(m, -EINVAL); + assert_return(m->hdr, -EINVAL); + assert_return(rtnl_message_type_is_traffic_control(m->hdr->nlmsg_type), -EINVAL); + assert_return(ret, -EINVAL); tcm = NLMSG_DATA(m->hdr); - tcm->tcm_handle = handle; + *ret = tcm->tcm_parent; return 0; } diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 182c74ed3..e846399a5 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -119,7 +119,8 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { assert_return(m->protocol != NETLINK_ROUTE || IN_SET(m->hdr->nlmsg_type, RTM_GETLINK, RTM_GETLINKPROP, RTM_GETADDR, RTM_GETROUTE, RTM_GETNEIGH, - RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP), -EINVAL); + RTM_GETRULE, RTM_GETADDRLABEL, RTM_GETNEXTHOP, RTM_GETQDISC, RTM_GETTCLASS), + -EINVAL); SET_FLAG(m->hdr->nlmsg_flags, NLM_F_DUMP, dump); @@ -258,7 +259,6 @@ int sd_netlink_message_append_string(sd_netlink_message *m, unsigned short type, int sd_netlink_message_append_strv(sd_netlink_message *m, unsigned short type, char * const *data) { size_t length, size; - char * const *p; int r; assert_return(m, -EINVAL); diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c index 8b4ed4479..ce2c4f3b5 100644 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ b/src/libsystemd/sd-netlink/netlink-util.c @@ -366,11 +366,13 @@ int rtnl_get_link_info( int ifindex, unsigned short *ret_iftype, unsigned *ret_flags, + char **ret_kind, struct hw_addr_data *ret_hw_addr, struct hw_addr_data *ret_permanent_hw_addr) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL; struct hw_addr_data addr = HW_ADDR_NULL, perm_addr = HW_ADDR_NULL; + _cleanup_free_ char *kind = NULL; unsigned short iftype; unsigned flags; int r; @@ -409,6 +411,19 @@ int rtnl_get_link_info( return r; } + if (ret_kind) { + r = sd_netlink_message_enter_container(reply, IFLA_LINKINFO); + if (r >= 0) { + r = sd_netlink_message_read_string_strdup(reply, IFLA_INFO_KIND, &kind); + if (r < 0 && r != -ENODATA) + return r; + + r = sd_netlink_message_exit_container(reply); + if (r < 0) + return r; + } + } + if (ret_hw_addr) { r = netlink_message_read_hw_addr(reply, IFLA_ADDRESS, &addr); if (r < 0 && r != -ENODATA) @@ -425,6 +440,8 @@ int rtnl_get_link_info( *ret_iftype = iftype; if (ret_flags) *ret_flags = flags; + if (ret_kind) + *ret_kind = TAKE_PTR(kind); if (ret_hw_addr) *ret_hw_addr = addr; if (ret_permanent_hw_addr) diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index 9024c0083..fee450cdc 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -59,12 +59,10 @@ static inline bool rtnl_message_type_is_routing_policy_rule(uint16_t type) { return IN_SET(type, RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE); } -static inline bool rtnl_message_type_is_qdisc(uint16_t type) { - return IN_SET(type, RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC); -} - -static inline bool rtnl_message_type_is_tclass(uint16_t type) { - return IN_SET(type, RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS); +static inline bool rtnl_message_type_is_traffic_control(uint16_t type) { + return IN_SET(type, + RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC, + RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS); } static inline bool rtnl_message_type_is_mdb(uint16_t type) { @@ -96,6 +94,7 @@ int rtnl_get_link_info( int ifindex, unsigned short *ret_iftype, unsigned *ret_flags, + char **ret_kind, struct hw_addr_data *ret_hw_addr, struct hw_addr_data *ret_permanent_hw_addr); diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 9de13f4c4..5271c1492 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -424,7 +424,6 @@ static int process_reply(sd_netlink *nl, sd_netlink_message *m) { } static int process_match(sd_netlink *nl, sd_netlink_message *m) { - struct match_callback *c; uint16_t type; uint8_t cmd; int r; @@ -977,7 +976,8 @@ int sd_netlink_add_match( neighbor_groups[] = { RTNLGRP_NEIGH, }, nexthop_groups[] = { RTNLGRP_NEXTHOP, }, route_groups[] = { RTNLGRP_IPV4_ROUTE, RTNLGRP_IPV6_ROUTE, }, - rule_groups[] = { RTNLGRP_IPV4_RULE, RTNLGRP_IPV6_RULE, }; + rule_groups[] = { RTNLGRP_IPV4_RULE, RTNLGRP_IPV6_RULE, }, + tc_groups[] = { RTNLGRP_TC }; const uint32_t *groups; size_t n_groups; @@ -1016,6 +1016,13 @@ int sd_netlink_add_match( groups = nexthop_groups; n_groups = ELEMENTSOF(nexthop_groups); break; + case RTM_NEWQDISC: + case RTM_DELQDISC: + case RTM_NEWTCLASS: + case RTM_DELTCLASS: + groups = tc_groups; + n_groups = ELEMENTSOF(tc_groups); + break; default: return -EOPNOTSUPP; } diff --git a/src/libsystemd/sd-network/network-util.c b/src/libsystemd/sd-network/network-util.c index 971247714..2059567ef 100644 --- a/src/libsystemd/sd-network/network-util.c +++ b/src/libsystemd/sd-network/network-util.c @@ -135,3 +135,23 @@ int parse_operational_state_range(const char *str, LinkOperationalStateRange *ou return 0; } + +int network_link_get_operational_state(int ifindex, LinkOperationalState *ret) { + _cleanup_free_ char *str = NULL; + LinkOperationalState s; + int r; + + assert(ifindex > 0); + assert(ret); + + r = sd_network_link_get_operational_state(ifindex, &str); + if (r < 0) + return r; + + s = link_operstate_from_string(str); + if (s < 0) + return s; + + *ret = s; + return 0; +} diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h index 6e2ad2842..c47e271a7 100644 --- a/src/libsystemd/sd-network/network-util.h +++ b/src/libsystemd/sd-network/network-util.h @@ -83,3 +83,4 @@ typedef struct LinkOperationalStateRange { LINK_OPERSTATE_ROUTABLE } int parse_operational_state_range(const char *str, LinkOperationalStateRange *out); +int network_link_get_operational_state(int ifindex, LinkOperationalState *ret); diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index e1e9a399c..a5612ab2d 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -469,7 +469,6 @@ sd_network_monitor* sd_network_monitor_unref(sd_network_monitor *m) { int sd_network_monitor_flush(sd_network_monitor *m) { union inotify_event_buffer buffer; - struct inotify_event *e; ssize_t l; int fd, k; diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c index ff1e0d5f8..385cfd300 100644 --- a/src/libsystemd/sd-path/sd-path.c +++ b/src/libsystemd/sd-path/sd-path.c @@ -601,8 +601,8 @@ static int get_search(uint64_t type, char ***list) { case SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT: case SD_PATH_SYSTEMD_SEARCH_USER_UNIT: { _cleanup_(lookup_paths_free) LookupPaths lp = {}; - const UnitFileScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT ? - UNIT_FILE_SYSTEM : UNIT_FILE_USER; + const LookupScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_UNIT ? + LOOKUP_SCOPE_SYSTEM : LOOKUP_SCOPE_USER; r = lookup_paths_init(&lp, scope, 0, NULL); if (r < 0) @@ -615,8 +615,8 @@ static int get_search(uint64_t type, char ***list) { case SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR: case SD_PATH_SYSTEMD_SEARCH_USER_GENERATOR: { char **t; - const UnitFileScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ? - UNIT_FILE_SYSTEM : UNIT_FILE_USER; + const LookupScope scope = type == SD_PATH_SYSTEMD_SEARCH_SYSTEM_GENERATOR ? + LOOKUP_SCOPE_SYSTEM : LOOKUP_SCOPE_USER; t = generator_binary_paths(scope); if (!t) @@ -669,7 +669,7 @@ _public_ int sd_path_lookup_strv(uint64_t type, const char *suffix, char ***path if (!n) return -ENOMEM; - char **i, **j = n; + char **j = n; STRV_FOREACH(i, l) { *j = path_join(*i, suffix); if (!*j) diff --git a/src/libudev/libudev-enumerate.c b/src/libudev/libudev-enumerate.c index 2dc695bd0..d71a31c56 100644 --- a/src/libudev/libudev-enumerate.c +++ b/src/libudev/libudev-enumerate.c @@ -365,7 +365,7 @@ _public_ int udev_enumerate_add_match_is_initialized(struct udev_enumerate *udev assert_return(udev_enumerate, -EINVAL); - r = device_enumerator_add_match_is_initialized(udev_enumerate->enumerator); + r = device_enumerator_add_match_is_initialized(udev_enumerate->enumerator, MATCH_INITIALIZED_COMPAT); if (r < 0) return r; diff --git a/src/libudev/libudev-list.c b/src/libudev/libudev-list.c index 69efc1013..0adc1d58e 100644 --- a/src/libudev/libudev-list.c +++ b/src/libudev/libudev-list.c @@ -110,8 +110,6 @@ struct udev_list_entry *udev_list_entry_add(struct udev_list *list, const char * } void udev_list_cleanup(struct udev_list *list) { - struct udev_list_entry *i, *n; - if (!list) return; @@ -119,7 +117,7 @@ void udev_list_cleanup(struct udev_list *list) { list->uptodate = false; hashmap_clear_with_destructor(list->unique_entries, udev_list_entry_free); } else - LIST_FOREACH_SAFE(entries, i, n, list->entries) + LIST_FOREACH(entries, i, list->entries) udev_list_entry_free(i); } @@ -154,7 +152,6 @@ struct udev_list_entry *udev_list_get_entry(struct udev_list *list) { else { _cleanup_free_ struct udev_list_entry **buf = NULL; struct udev_list_entry *entry, **p; - size_t j; buf = new(struct udev_list_entry *, n); if (!buf) @@ -166,7 +163,7 @@ struct udev_list_entry *udev_list_get_entry(struct udev_list *list) { typesafe_qsort(buf, n, udev_list_entry_compare_func); - for (j = n; j > 0; j--) + for (size_t j = n; j > 0; j--) LIST_PREPEND(entries, list->entries, buf[j-1]); } diff --git a/src/libudev/meson.build b/src/libudev/meson.build index 77b31a949..2d51ff7c5 100644 --- a/src/libudev/meson.build +++ b/src/libudev/meson.build @@ -45,7 +45,7 @@ custom_target( ############################################################ tests += [ - [['src/libudev/test-libudev.c'], + [files('test-libudev.c'), [libshared, libudev_basic]], ] diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c index 10d2ed7ae..9759f4616 100644 --- a/src/locale/keymap-util.c +++ b/src/locale/keymap-util.c @@ -157,7 +157,6 @@ int locale_read_data(Context *c, sd_bus_message *m) { int vconsole_read_data(Context *c, sd_bus_message *m) { struct stat st; usec_t t; - int r; /* Do not try to re-read the file within single bus operation. */ if (m) { @@ -185,13 +184,9 @@ int vconsole_read_data(Context *c, sd_bus_message *m) { c->vc_mtime = t; context_free_vconsole(c); - r = parse_env_file(NULL, "/etc/vconsole.conf", - "KEYMAP", &c->vc_keymap, - "KEYMAP_TOGGLE", &c->vc_keymap_toggle); - if (r < 0) - return r; - - return 0; + return parse_env_file(NULL, "/etc/vconsole.conf", + "KEYMAP", &c->vc_keymap, + "KEYMAP_TOGGLE", &c->vc_keymap_toggle); } int x11_read_data(Context *c, sd_bus_message *m) { diff --git a/src/locale/localectl.c b/src/locale/localectl.c index b5624209d..661d54c27 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -98,8 +98,6 @@ static void print_status_info(StatusInfo *i) { if (strv_isempty(i->locale)) puts(" System Locale: n/a"); else { - char **j; - printf(" System Locale: %s\n", i->locale[0]); STRV_FOREACH(j, i->locale + 1) printf(" %s\n", *j); diff --git a/src/locale/localed.c b/src/locale/localed.c index c228385d0..89bf9c6fb 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -346,7 +346,6 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er Context *c = userdata; bool modified = false; int interactive, r; - char **i; bool use_localegen; assert(m); @@ -475,7 +474,7 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_error *error) { Context *c = userdata; - const char *name, *keymap, *keymap_toggle; + const char *keymap, *keymap_toggle; int convert, interactive, r; assert(m); diff --git a/src/locale/meson.build b/src/locale/meson.build index 8510fe610..72c44aedc 100644 --- a/src/locale/meson.build +++ b/src/locale/meson.build @@ -30,7 +30,7 @@ if conf.get('ENABLE_LOCALED') == 1 endif tests += [ - [['src/locale/test-keymap-util.c', - 'src/locale/keymap-util.c', - 'src/locale/keymap-util.h']], + [files('test-keymap-util.c', + 'keymap-util.c', + 'keymap-util.h')], ] diff --git a/src/locale/test-keymap-util.c b/src/locale/test-keymap-util.c index f726e8e52..668f94ec8 100644 --- a/src/locale/test-keymap-util.c +++ b/src/locale/test-keymap-util.c @@ -6,11 +6,9 @@ #include "string-util.h" #include "tests.h" -static void test_find_language_fallback(void) { +TEST(find_language_fallback) { _cleanup_free_ char *ans = NULL, *ans2 = NULL; - log_info("/*** %s ***/", __func__); - assert_se(find_language_fallback("foobar", &ans) == 0); assert_se(ans == NULL); @@ -24,12 +22,10 @@ static void test_find_language_fallback(void) { assert_se(streq(ans2, "szl:pl")); } -static void test_find_converted_keymap(void) { +TEST(find_converted_keymap) { _cleanup_free_ char *ans = NULL, *ans2 = NULL; int r; - log_info("/*** %s ***/", __func__); - assert_se(find_converted_keymap("pl", "foobar", &ans) == 0); assert_se(ans == NULL); @@ -46,12 +42,10 @@ static void test_find_converted_keymap(void) { assert_se(streq(ans2, "pl-dvorak")); } -static void test_find_legacy_keymap(void) { +TEST(find_legacy_keymap) { Context c = {}; _cleanup_free_ char *ans = NULL, *ans2 = NULL; - log_info("/*** %s ***/", __func__); - c.x11_layout = (char*) "foobar"; assert_se(find_legacy_keymap(&c, &ans) == 0); assert_se(ans == NULL); @@ -65,11 +59,9 @@ static void test_find_legacy_keymap(void) { assert_se(streq(ans, "pl2")); } -static void test_vconsole_convert_to_x11(void) { +TEST(vconsole_convert_to_x11) { _cleanup_(context_clear) Context c = {}; - log_info("/*** %s ***/", __func__); - log_info("/* test emptying first (:) */"); assert_se(free_and_strdup(&c.x11_layout, "foo") >= 0); assert_se(free_and_strdup(&c.x11_variant, "bar") >= 0); @@ -119,12 +111,10 @@ static void test_vconsole_convert_to_x11(void) { assert_se(c.x11_variant == NULL); } -static void test_x11_convert_to_vconsole(void) { +TEST(x11_convert_to_vconsole) { _cleanup_(context_clear) Context c = {}; int r; - log_info("/*** %s ***/", __func__); - log_info("/* test emptying first (:) */"); assert_se(free_and_strdup(&c.vc_keymap, "foobar") >= 0); assert_se(x11_convert_to_vconsole(&c) == 1); @@ -189,20 +179,13 @@ static void test_x11_convert_to_vconsole(void) { assert_se(streq(c.vc_keymap, "ru")); } -int main(int argc, char **argv) { +static int intro(void) { _cleanup_free_ char *map = NULL; - test_setup_logging(LOG_DEBUG); - - test_find_language_fallback(); - test_find_converted_keymap(); - assert_se(get_testdata_dir("test-keymap-util/kbd-model-map", &map) >= 0); assert_se(setenv("SYSTEMD_KBD_MODEL_MAP", map, 1) == 0); - test_find_legacy_keymap(); - test_vconsole_convert_to_x11(); - test_x11_convert_to_vconsole(); - - return 0; + return EXIT_SUCCESS; } + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/login/70-uaccess.rules.in b/src/login/70-uaccess.rules.in index 052194b6c..a3301be1b 100644 --- a/src/login/70-uaccess.rules.in +++ b/src/login/70-uaccess.rules.in @@ -87,4 +87,13 @@ ENV{ID_SIGNAL_ANALYZER}=="?*", ENV{DEVTYPE}=="usb_device", TAG+="uaccess" # rfkill / radio killswitches KERNEL=="rfkill", SUBSYSTEM=="misc", TAG+="uaccess" +# AV production controllers +# Most of these devices use HID for the knobs, faders, buttons, encoders, and jog wheels. +SUBSYSTEM=="hidraw", ENV{ID_AV_PRODUCTION_CONTROLLER}=="1", TAG+="uaccess" + +# Some devices use vendor defined protocols on USB Bulk endpoints for controllers. +# Other devices transfer graphics to screens on the device through USB Bulk endpoints. +# This also allows accessing HID devices with the libusb backend of hidapi. +SUBSYSTEM=="usb", ENV{ID_AV_PRODUCTION_CONTROLLER}=="1", TAG+="uaccess" + LABEL="uaccess_end" diff --git a/src/login/loginctl.c b/src/login/loginctl.c index 3e1916725..83f25135a 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -469,7 +469,7 @@ static int print_session_status_info(sd_bus *bus, const char *path, bool *new_li else printf("%"PRIu32"\n", i.uid); - if (i.timestamp.realtime > 0 && i.timestamp.realtime < USEC_INFINITY) + if (timestamp_is_set(i.timestamp.realtime)) printf("\t Since: %s; %s\n", FORMAT_TIMESTAMP(i.timestamp.realtime), FORMAT_TIMESTAMP_RELATIVE(i.timestamp.realtime)); @@ -592,7 +592,7 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) else printf("%"PRIu32"\n", i.uid); - if (i.timestamp.realtime > 0 && i.timestamp.realtime < USEC_INFINITY) + if (timestamp_is_set(i.timestamp.realtime)) printf("\t Since: %s; %s\n", FORMAT_TIMESTAMP(i.timestamp.realtime), FORMAT_TIMESTAMP_RELATIVE(i.timestamp.realtime)); @@ -601,7 +601,6 @@ static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) printf("\t State: %s\n", i.state); if (!strv_isempty(i.sessions)) { - char **l; printf("\tSessions:"); STRV_FOREACH(l, i.sessions) @@ -662,7 +661,6 @@ static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) printf("%s\n", strna(i.id)); if (!strv_isempty(i.sessions)) { - char **l; printf("\tSessions:"); STRV_FOREACH(l, i.sessions) { diff --git a/src/login/logind-action.c b/src/login/logind-action.c index e17291094..5a691307b 100644 --- a/src/login/logind-action.c +++ b/src/login/logind-action.c @@ -2,6 +2,8 @@ #include +#include "sd-messages.h" + #include "alloc-util.h" #include "bus-error.h" #include "bus-util.h" @@ -11,29 +13,112 @@ #include "logind-dbus.h" #include "logind-session-dbus.h" #include "process-util.h" -#include "sleep-config.h" #include "special.h" #include "string-table.h" #include "terminal-util.h" #include "user-util.h" -const char* manager_target_for_action(HandleAction handle) { - static const char * const target_table[_HANDLE_ACTION_MAX] = { - [HANDLE_POWEROFF] = SPECIAL_POWEROFF_TARGET, - [HANDLE_REBOOT] = SPECIAL_REBOOT_TARGET, - [HANDLE_HALT] = SPECIAL_HALT_TARGET, - [HANDLE_KEXEC] = SPECIAL_KEXEC_TARGET, - [HANDLE_SUSPEND] = SPECIAL_SUSPEND_TARGET, - [HANDLE_HIBERNATE] = SPECIAL_HIBERNATE_TARGET, - [HANDLE_HYBRID_SLEEP] = SPECIAL_HYBRID_SLEEP_TARGET, - [HANDLE_SUSPEND_THEN_HIBERNATE] = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, - [HANDLE_FACTORY_RESET] = SPECIAL_FACTORY_RESET_TARGET, - }; +static const HandleActionData handle_action_data_table[_HANDLE_ACTION_MAX] = { + [HANDLE_POWEROFF] = { + .handle = HANDLE_POWEROFF, + .target = SPECIAL_POWEROFF_TARGET, + .inhibit_what = INHIBIT_SHUTDOWN, + .polkit_action = "org.freedesktop.login1.power-off", + .polkit_action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit", + .sleep_operation = _SLEEP_OPERATION_INVALID, + .message_id = SD_MESSAGE_SHUTDOWN_STR, + .message = "System is powering down", + .log_message = "power-off", + }, + [HANDLE_REBOOT] = { + .handle = HANDLE_REBOOT, + .target = SPECIAL_REBOOT_TARGET, + .inhibit_what = INHIBIT_SHUTDOWN, + .polkit_action = "org.freedesktop.login1.reboot", + .polkit_action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit", + .sleep_operation = _SLEEP_OPERATION_INVALID, + .message_id = SD_MESSAGE_SHUTDOWN_STR, + .message = "System is rebooting", + .log_message = "reboot", + }, + [HANDLE_HALT] = { + .handle = HANDLE_HALT, + .target = SPECIAL_HALT_TARGET, + .inhibit_what = INHIBIT_SHUTDOWN, + .polkit_action = "org.freedesktop.login1.halt", + .polkit_action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit", + .sleep_operation = _SLEEP_OPERATION_INVALID, + .message_id = SD_MESSAGE_SHUTDOWN_STR, + .message = "System is halting", + .log_message = "halt", + }, + [HANDLE_KEXEC] = { + .handle = HANDLE_KEXEC, + .target = SPECIAL_KEXEC_TARGET, + .inhibit_what = INHIBIT_SHUTDOWN, + .polkit_action = "org.freedesktop.login1.reboot", + .polkit_action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit", + .sleep_operation = _SLEEP_OPERATION_INVALID, + .message_id = SD_MESSAGE_SHUTDOWN_STR, + .message = "System is rebooting with kexec", + .log_message = "kexec", + }, + [HANDLE_SUSPEND] = { + .handle = HANDLE_SUSPEND, + .target = SPECIAL_SUSPEND_TARGET, + .inhibit_what = INHIBIT_SLEEP, + .polkit_action = "org.freedesktop.login1.suspend", + .polkit_action_multiple_sessions = "org.freedesktop.login1.suspend-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.suspend-ignore-inhibit", + .sleep_operation = SLEEP_SUSPEND, + }, + [HANDLE_HIBERNATE] = { + .handle = HANDLE_HIBERNATE, + .target = SPECIAL_HIBERNATE_TARGET, + .inhibit_what = INHIBIT_SLEEP, + .polkit_action = "org.freedesktop.login1.hibernate", + .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit", + .sleep_operation = SLEEP_HIBERNATE, + }, + [HANDLE_HYBRID_SLEEP] = { + .handle = HANDLE_HYBRID_SLEEP, + .target = SPECIAL_HYBRID_SLEEP_TARGET, + .inhibit_what = INHIBIT_SLEEP, + .polkit_action = "org.freedesktop.login1.hibernate", + .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit", + .sleep_operation = SLEEP_HYBRID_SLEEP, + }, + [HANDLE_SUSPEND_THEN_HIBERNATE] = { + .handle = HANDLE_SUSPEND_THEN_HIBERNATE, + .target = SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, + .inhibit_what = INHIBIT_SLEEP, + .polkit_action = "org.freedesktop.login1.hibernate", + .polkit_action_multiple_sessions = "org.freedesktop.login1.hibernate-multiple-sessions", + .polkit_action_ignore_inhibit = "org.freedesktop.login1.hibernate-ignore-inhibit", + .sleep_operation = SLEEP_SUSPEND_THEN_HIBERNATE, + }, + [HANDLE_FACTORY_RESET] = { + .handle = HANDLE_FACTORY_RESET, + .target = SPECIAL_FACTORY_RESET_TARGET, + .inhibit_what = _INHIBIT_WHAT_INVALID, + .sleep_operation = _SLEEP_OPERATION_INVALID, + .message_id = SD_MESSAGE_FACTORY_RESET_STR, + .message = "System is performing factory reset", + }, +}; - assert(handle >= 0); - if (handle < (ssize_t) ELEMENTSOF(target_table)) - return target_table[handle]; - return NULL; +const HandleActionData* handle_action_lookup(HandleAction action) { + + if (action < 0 || (size_t) action >= ELEMENTSOF(handle_action_data_table)) + return NULL; + + return &handle_action_data_table[action]; } int manager_handle_action( @@ -59,7 +144,6 @@ int manager_handle_action( InhibitWhat inhibit_operation; Inhibitor *offending = NULL; bool supported; - const char *target; int r; assert(m); @@ -129,17 +213,13 @@ int manager_handle_action( return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Requested %s operation not supported, ignoring.", handle_action_to_string(handle)); - if (m->action_what > 0) + if (m->delayed_action) return log_debug_errno(SYNTHETIC_ERRNO(EALREADY), "Action already in progress (%s), ignoring requested %s operation.", - inhibit_what_to_string(m->action_what), + inhibit_what_to_string(m->delayed_action->inhibit_what), handle_action_to_string(handle)); - assert_se(target = manager_target_for_action(handle)); - - inhibit_operation = IN_SET(handle, HANDLE_SUSPEND, HANDLE_HIBERNATE, - HANDLE_HYBRID_SLEEP, - HANDLE_SUSPEND_THEN_HIBERNATE) ? INHIBIT_SLEEP : INHIBIT_SHUTDOWN; + inhibit_operation = handle_action_lookup(handle)->inhibit_what; /* If the actual operation is inhibited, warn and fail */ if (!ignore_inhibited && @@ -162,7 +242,7 @@ int manager_handle_action( log_info("%s", message_table[handle]); - r = bus_manager_shutdown_or_sleep_now_or_later(m, target, inhibit_operation, &error); + r = bus_manager_shutdown_or_sleep_now_or_later(m, handle_action_lookup(handle), &error); if (r < 0) return log_error_errno(r, "Failed to execute %s operation: %s", handle_action_to_string(handle), diff --git a/src/login/logind-action.h b/src/login/logind-action.h index ec2fece2b..03284414c 100644 --- a/src/login/logind-action.h +++ b/src/login/logind-action.h @@ -19,8 +19,28 @@ typedef enum HandleAction { _HANDLE_ACTION_INVALID = -EINVAL, } HandleAction; +typedef struct HandleActionData HandleActionData; + #include "logind-inhibit.h" #include "logind.h" +#include "sleep-config.h" + +static inline bool handle_action_valid(HandleAction a) { + return a >= 0 && a < _HANDLE_ACTION_MAX; +} + +struct HandleActionData { + HandleAction handle; + const char *target; + InhibitWhat inhibit_what; + const char *polkit_action; + const char *polkit_action_multiple_sessions; + const char *polkit_action_ignore_inhibit; + SleepOperation sleep_operation; + const char* message_id; + const char* message; + const char* log_message; +}; int manager_handle_action( Manager *m, @@ -32,6 +52,6 @@ int manager_handle_action( const char* handle_action_to_string(HandleAction h) _const_; HandleAction handle_action_from_string(const char *s) _pure_; -const char* manager_target_for_action(HandleAction handle); +const HandleActionData* handle_action_lookup(HandleAction handle); CONFIG_PARSER_PROTOTYPE(config_parse_handle_action); diff --git a/src/login/logind-button.c b/src/login/logind-button.c index 7fb811463..a2b43d368 100644 --- a/src/login/logind-button.c +++ b/src/login/logind-button.c @@ -84,8 +84,7 @@ static void button_lid_switch_handle_action(Manager *manager, bool is_edge) { * differently */ if (manager_is_docked_or_external_displays(manager)) handle_action = manager->handle_lid_switch_docked; - else if (manager->handle_lid_switch_ep != _HANDLE_ACTION_INVALID && - manager_is_on_external_power()) + else if (handle_action_valid(manager->handle_lid_switch_ep) && manager_is_on_external_power()) handle_action = manager->handle_lid_switch_ep; else handle_action = manager->handle_lid_switch; diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index c05c0d02c..1212a1ac5 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -20,15 +20,19 @@ #include "cgroup-util.h" #include "device-util.h" #include "dirent-util.h" +#include "efi-api.h" #include "efi-loader.h" #include "efivars.h" +#include "env-file.h" #include "env-util.h" #include "escape.h" +#include "event-util.h" #include "fd-util.h" #include "fileio-label.h" #include "fileio.h" #include "format-util.h" #include "fs-util.h" +#include "logind-action.h" #include "logind-dbus.h" #include "logind-polkit.h" #include "logind-seat-dbus.h" @@ -44,6 +48,7 @@ #include "selinux-util.h" #include "sleep-config.h" #include "special.h" +#include "serialize.h" #include "stdio-util.h" #include "strv.h" #include "terminal-util.h" @@ -53,6 +58,21 @@ #include "utmp-wtmp.h" #include "virt.h" +/* As a random fun fact sysvinit had a 252 (256-(strlen(" \r\n")+1)) + * character limit for the wall message. + * https://git.savannah.nongnu.org/cgit/sysvinit.git/tree/src/shutdown.c#n72 + * There is no real technical need for that but doesn't make sense + * to store arbitrary amounts either. As we are not stingy here, we + * allow 4k. + */ +#define WALL_MESSAGE_MAX 4096 + +#define SHUTDOWN_SCHEDULE_FILE "/run/systemd/shutdown/scheduled" + +static int update_schedule_file(Manager *m); +static void reset_scheduled_shutdown(Manager *m); +static int manager_setup_shutdown_timers(Manager* m); + static int get_sender_session( Manager *m, sd_bus_message *message, @@ -309,16 +329,18 @@ static int property_get_preparing( sd_bus_error *error) { Manager *m = userdata; - bool b; + bool b = false; assert(bus); assert(reply); assert(m); - if (streq(property, "PreparingForShutdown")) - b = m->action_what & INHIBIT_SHUTDOWN; - else - b = m->action_what & INHIBIT_SLEEP; + if (m->delayed_action) { + if (streq(property, "PreparingForShutdown")) + b = m->delayed_action->inhibit_what & INHIBIT_SHUTDOWN; + else + b = m->delayed_action->inhibit_what & INHIBIT_SLEEP; + } return sd_bus_message_append(reply, "b", b); } @@ -343,7 +365,9 @@ static int property_get_scheduled_shutdown( if (r < 0) return r; - r = sd_bus_message_append(reply, "st", m->scheduled_shutdown_type, m->scheduled_shutdown_timeout); + r = sd_bus_message_append(reply, "st", + m->scheduled_shutdown_action ? handle_action_to_string(m->scheduled_shutdown_action->handle) : NULL, + m->scheduled_shutdown_timeout); if (r < 0) return r; @@ -1488,59 +1512,35 @@ static int have_multiple_sessions( return false; } -_printf_(2, 0) -static int log_with_wall_message(Manager *m, const char *d, const char *p, const char *q) { - assert(m); - - if (isempty(m->wall_message)) - p = strjoina(p, "."); - else - p = strjoina(p, " (", m->wall_message, ")."); - - return log_struct(LOG_NOTICE, d, p, q); -} - static int bus_manager_log_shutdown( Manager *m, - const char *unit_name) { + const HandleActionData *a) { + + const char *message, *log_message; assert(m); - assert(unit_name); + assert(a); - if (streq(unit_name, SPECIAL_POWEROFF_TARGET)) - return log_with_wall_message(m, - "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR, - "MESSAGE=System is powering down", - "SHUTDOWN=power-off"); + message = a->message; + log_message = a->log_message; - if (streq(unit_name, SPECIAL_REBOOT_TARGET)) - return log_with_wall_message(m, - "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR, - "MESSAGE=System is rebooting", - "SHUTDOWN=reboot"); + if (message) + message = strjoina("MESSAGE=", message); + else + message = "MESSAGE=System is shutting down"; - if (streq(unit_name, SPECIAL_HALT_TARGET)) - return log_with_wall_message(m, - "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR, - "MESSAGE=System is halting", - "SHUTDOWN=halt"); + if (isempty(m->wall_message)) + message = strjoina(message, "."); + else + message = strjoina(message, " (", m->wall_message, ")."); - if (streq(unit_name, SPECIAL_KEXEC_TARGET)) - return log_with_wall_message(m, - "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR, - "MESSAGE=System is rebooting with kexec", - "SHUTDOWN=kexec"); + if (log_message) + log_message = strjoina("SHUTDOWN=", log_message); - if (streq(unit_name, SPECIAL_FACTORY_RESET_TARGET)) - return log_with_wall_message(m, - "MESSAGE_ID=" SD_MESSAGE_FACTORY_RESET_STR, - "MESSAGE=System is performing factory reset", - NULL); - - return log_with_wall_message(m, - "MESSAGE_ID=" SD_MESSAGE_SHUTDOWN_STR, - "MESSAGE=System is shutting down", - NULL); + return log_struct(LOG_NOTICE, + "MESSAGE_ID=%s", a->message_id ? a->message_id : SD_MESSAGE_SHUTDOWN_STR, + message, + log_message); } static int lid_switch_ignore_handler(sd_event_source *e, uint64_t usec, void *userdata) { @@ -1604,8 +1604,7 @@ static int send_prepare_for(Manager *m, InhibitWhat w, bool _active) { static int execute_shutdown_or_sleep( Manager *m, - InhibitWhat w, - const char *unit_name, + const HandleActionData *a, sd_bus_error *error) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; @@ -1613,12 +1612,10 @@ static int execute_shutdown_or_sleep( int r; assert(m); - assert(w > 0); - assert(w < _INHIBIT_WHAT_MAX); - assert(unit_name); + assert(a); - if (w == INHIBIT_SHUTDOWN) - bus_manager_log_shutdown(m, unit_name); + if (a->inhibit_what == INHIBIT_SHUTDOWN) + bus_manager_log_shutdown(m, a); r = bus_call_method( m->bus, @@ -1626,7 +1623,7 @@ static int execute_shutdown_or_sleep( "StartUnit", error, &reply, - "ss", unit_name, "replace-irreversibly"); + "ss", a->target, "replace-irreversibly"); if (r < 0) goto error; @@ -1638,8 +1635,7 @@ static int execute_shutdown_or_sleep( if (r < 0) goto error; - m->action_unit = unit_name; - m->action_what = w; + m->delayed_action = a; /* Make sure the lid switch is ignored for a while */ manager_set_lid_switch_ignore(m, usec_add(now(CLOCK_MONOTONIC), m->holdoff_timeout_usec)); @@ -1648,7 +1644,7 @@ static int execute_shutdown_or_sleep( error: /* Tell people that they now may take a lock again */ - (void) send_prepare_for(m, w, false); + (void) send_prepare_for(m, a->inhibit_what, false); return r; } @@ -1660,10 +1656,10 @@ int manager_dispatch_delayed(Manager *manager, bool timeout) { assert(manager); - if (manager->action_what == 0 || manager->action_job) + if (!manager->delayed_action || manager->action_job) return 0; - if (manager_is_inhibited(manager, manager->action_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) { + if (manager_is_inhibited(manager, manager->delayed_action->inhibit_what, INHIBIT_DELAY, NULL, false, false, 0, &offending)) { _cleanup_free_ char *comm = NULL, *u = NULL; if (!timeout) @@ -1678,13 +1674,12 @@ int manager_dispatch_delayed(Manager *manager, bool timeout) { } /* Actually do the operation */ - r = execute_shutdown_or_sleep(manager, manager->action_what, manager->action_unit, &error); + r = execute_shutdown_or_sleep(manager, manager->delayed_action, &error); if (r < 0) { log_warning("Error during inhibitor-delayed operation (already returned success to client): %s", bus_error_message(&error, r)); - manager->action_unit = NULL; - manager->action_what = 0; + manager->delayed_action = NULL; } return 1; /* We did some work. */ @@ -1705,15 +1700,12 @@ static int manager_inhibit_timeout_handler( static int delay_shutdown_or_sleep( Manager *m, - InhibitWhat w, - const char *unit_name) { + const HandleActionData *a) { int r; assert(m); - assert(w >= 0); - assert(w < _INHIBIT_WHAT_MAX); - assert(unit_name); + assert(a); if (m->inhibit_timeout_source) { r = sd_event_source_set_time_relative(m->inhibit_timeout_source, m->inhibit_delay_max); @@ -1733,16 +1725,14 @@ static int delay_shutdown_or_sleep( return r; } - m->action_unit = unit_name; - m->action_what = w; + m->delayed_action = a; return 0; } int bus_manager_shutdown_or_sleep_now_or_later( Manager *m, - const char *unit_name, - InhibitWhat w, + const HandleActionData *a, sd_bus_error *error) { _cleanup_free_ char *load_state = NULL; @@ -1750,35 +1740,33 @@ int bus_manager_shutdown_or_sleep_now_or_later( int r; assert(m); - assert(unit_name); - assert(w > 0); - assert(w < _INHIBIT_WHAT_MAX); + assert(a); assert(!m->action_job); - r = unit_load_state(m->bus, unit_name, &load_state); + r = unit_load_state(m->bus, a->target, &load_state); if (r < 0) return r; if (!streq(load_state, "loaded")) return log_notice_errno(SYNTHETIC_ERRNO(EACCES), "Unit %s is %s, refusing operation.", - unit_name, load_state); + a->target, load_state); /* Tell everybody to prepare for shutdown/sleep */ - (void) send_prepare_for(m, w, true); + (void) send_prepare_for(m, a->inhibit_what, true); delayed = m->inhibit_delay_max > 0 && - manager_is_inhibited(m, w, INHIBIT_DELAY, NULL, false, false, 0, NULL); + manager_is_inhibited(m, a->inhibit_what, INHIBIT_DELAY, NULL, false, false, 0, NULL); if (delayed) /* Shutdown is delayed, keep in mind what we * want to do, and start a timeout */ - r = delay_shutdown_or_sleep(m, w, unit_name); + r = delay_shutdown_or_sleep(m, a); else /* Shutdown is not delayed, execute it * immediately */ - r = execute_shutdown_or_sleep(m, w, unit_name, error); + r = execute_shutdown_or_sleep(m, a, error); return r; } @@ -1786,10 +1774,7 @@ int bus_manager_shutdown_or_sleep_now_or_later( static int verify_shutdown_creds( Manager *m, sd_bus_message *message, - InhibitWhat w, - const char *action, - const char *action_multiple_sessions, - const char *action_ignore_inhibit, + const HandleActionData *a, uint64_t flags, sd_bus_error *error) { @@ -1799,12 +1784,8 @@ static int verify_shutdown_creds( int r; assert(m); + assert(a); assert(message); - assert(w >= 0); - assert(w <= _INHIBIT_WHAT_MAX); - assert(action); - assert(action_multiple_sessions); - assert(action_ignore_inhibit); r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds); if (r < 0) @@ -1819,11 +1800,19 @@ static int verify_shutdown_creds( return r; multiple_sessions = r > 0; - blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); + blocked = manager_is_inhibited(m, a->inhibit_what, INHIBIT_BLOCK, NULL, false, true, uid, NULL); interactive = flags & SD_LOGIND_INTERACTIVE; if (multiple_sessions) { - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, interactive, UID_INVALID, &m->polkit_registry, error); + r = bus_verify_polkit_async( + message, + CAP_SYS_BOOT, + a->polkit_action_multiple_sessions, + NULL, + interactive, + UID_INVALID, + &m->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -1836,7 +1825,14 @@ static int verify_shutdown_creds( return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Access denied to root due to active block inhibitor"); - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, interactive, UID_INVALID, &m->polkit_registry, error); + r = bus_verify_polkit_async(message, + CAP_SYS_BOOT, + a->polkit_action_ignore_inhibit, + NULL, + interactive, + UID_INVALID, + &m->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -1844,7 +1840,14 @@ static int verify_shutdown_creds( } if (!multiple_sessions && !blocked) { - r = bus_verify_polkit_async(message, CAP_SYS_BOOT, action, NULL, interactive, UID_INVALID, &m->polkit_registry, error); + r = bus_verify_polkit_async(message, + CAP_SYS_BOOT, + a->polkit_action, + NULL, + interactive, + UID_INVALID, + &m->polkit_registry, + error); if (r < 0) return r; if (r == 0) @@ -1854,15 +1857,33 @@ static int verify_shutdown_creds( return 0; } +static int setup_wall_message_timer(Manager *m, sd_bus_message* message) { + _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; + int r; + + r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); + if (r >= 0) { + const char *tty = NULL; + + (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid); + (void) sd_bus_creds_get_tty(creds, &tty); + + r = free_and_strdup(&m->scheduled_shutdown_tty, tty); + if (r < 0) + return log_oom(); + } + + r = manager_setup_wall_message_timer(m); + if (r < 0) + return r; + + return 0; +} + static int method_do_shutdown_or_sleep( Manager *m, sd_bus_message *message, - const char *unit_name, - InhibitWhat w, - const char *action, - const char *action_multiple_sessions, - const char *action_ignore_inhibit, - SleepOperation sleep_operation, + const HandleActionData *a, bool with_flags, sd_bus_error *error) { @@ -1871,9 +1892,7 @@ static int method_do_shutdown_or_sleep( assert(m); assert(message); - assert(unit_name); - assert(w >= 0); - assert(w <= _INHIBIT_WHAT_MAX); + assert(a); if (with_flags) { /* New style method: with flags parameter (and interactive bool in the bus message header) */ @@ -1882,7 +1901,7 @@ static int method_do_shutdown_or_sleep( return r; if ((flags & ~SD_LOGIND_SHUTDOWN_AND_SLEEP_FLAGS_PUBLIC) != 0) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid flags parameter"); - if (!streq(unit_name, SPECIAL_REBOOT_TARGET) && (flags & SD_LOGIND_REBOOT_VIA_KEXEC)) + if (a->handle != HANDLE_REBOOT && (flags & SD_LOGIND_REBOOT_VIA_KEXEC)) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Reboot via kexec is only applicable with reboot operations"); } else { /* Old style method: no flags parameter, but interactive bool passed as boolean in @@ -1898,31 +1917,39 @@ static int method_do_shutdown_or_sleep( } if ((flags & SD_LOGIND_REBOOT_VIA_KEXEC) && kexec_loaded()) - unit_name = SPECIAL_KEXEC_TARGET; + a = handle_action_lookup(HANDLE_KEXEC); /* Don't allow multiple jobs being executed at the same time */ - if (m->action_what > 0) + if (m->delayed_action) return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress"); - if (sleep_operation >= 0) { - r = can_sleep(sleep_operation); + if (a->sleep_operation >= 0) { + r = can_sleep(a->sleep_operation); if (r == -ENOSPC) return sd_bus_error_set(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, "Not enough swap space for hibernation"); if (r == 0) return sd_bus_error_setf(error, BUS_ERROR_SLEEP_VERB_NOT_SUPPORTED, - "Sleep verb \"%s\" not supported", sleep_operation_to_string(sleep_operation)); + "Sleep verb \"%s\" not supported", sleep_operation_to_string(a->sleep_operation)); if (r < 0) return r; } - r = verify_shutdown_creds(m, message, w, action, action_multiple_sessions, - action_ignore_inhibit, flags, error); + r = verify_shutdown_creds(m, message, a, flags, error); if (r != 0) return r; - r = bus_manager_shutdown_or_sleep_now_or_later(m, unit_name, w, error); + /* reset case we're shorting a scheduled shutdown */ + m->unlink_nologin = false; + reset_scheduled_shutdown(m); + + m->scheduled_shutdown_timeout = 0; + m->scheduled_shutdown_action = a; + + (void) setup_wall_message_timer(m, message); + + r = bus_manager_shutdown_or_sleep_now_or_later(m, a, error); if (r < 0) return r; @@ -1934,12 +1961,7 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error return method_do_shutdown_or_sleep( m, message, - SPECIAL_POWEROFF_TARGET, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.power-off", - "org.freedesktop.login1.power-off-multiple-sessions", - "org.freedesktop.login1.power-off-ignore-inhibit", - _SLEEP_OPERATION_INVALID, + handle_action_lookup(HANDLE_POWEROFF), sd_bus_message_is_method_call(message, NULL, "PowerOffWithFlags"), error); } @@ -1949,12 +1971,7 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error * return method_do_shutdown_or_sleep( m, message, - SPECIAL_REBOOT_TARGET, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.reboot", - "org.freedesktop.login1.reboot-multiple-sessions", - "org.freedesktop.login1.reboot-ignore-inhibit", - _SLEEP_OPERATION_INVALID, + handle_action_lookup(HANDLE_REBOOT), sd_bus_message_is_method_call(message, NULL, "RebootWithFlags"), error); } @@ -1964,12 +1981,7 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er return method_do_shutdown_or_sleep( m, message, - SPECIAL_HALT_TARGET, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.halt", - "org.freedesktop.login1.halt-multiple-sessions", - "org.freedesktop.login1.halt-ignore-inhibit", - _SLEEP_OPERATION_INVALID, + handle_action_lookup(HANDLE_HALT), sd_bus_message_is_method_call(message, NULL, "HaltWithFlags"), error); } @@ -1979,12 +1991,7 @@ static int method_suspend(sd_bus_message *message, void *userdata, sd_bus_error return method_do_shutdown_or_sleep( m, message, - SPECIAL_SUSPEND_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.suspend", - "org.freedesktop.login1.suspend-multiple-sessions", - "org.freedesktop.login1.suspend-ignore-inhibit", - SLEEP_SUSPEND, + handle_action_lookup(HANDLE_SUSPEND), sd_bus_message_is_method_call(message, NULL, "SuspendWithFlags"), error); } @@ -1994,12 +2001,7 @@ static int method_hibernate(sd_bus_message *message, void *userdata, sd_bus_erro return method_do_shutdown_or_sleep( m, message, - SPECIAL_HIBERNATE_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - SLEEP_HIBERNATE, + handle_action_lookup(HANDLE_HIBERNATE), sd_bus_message_is_method_call(message, NULL, "HibernateWithFlags"), error); } @@ -2009,12 +2011,7 @@ static int method_hybrid_sleep(sd_bus_message *message, void *userdata, sd_bus_e return method_do_shutdown_or_sleep( m, message, - SPECIAL_HYBRID_SLEEP_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - SLEEP_HYBRID_SLEEP, + handle_action_lookup(HANDLE_HYBRID_SLEEP), sd_bus_message_is_method_call(message, NULL, "HybridSleepWithFlags"), error); } @@ -2024,12 +2021,7 @@ static int method_suspend_then_hibernate(sd_bus_message *message, void *userdata return method_do_shutdown_or_sleep( m, message, - SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - SLEEP_SUSPEND_THEN_HIBERNATE, + handle_action_lookup(HANDLE_SUSPEND_THEN_HIBERNATE), sd_bus_message_is_method_call(message, NULL, "SuspendThenHibernateWithFlags"), error); } @@ -2054,48 +2046,118 @@ static usec_t nologin_timeout_usec(usec_t elapse) { return LESS_BY(elapse, 5 * USEC_PER_MINUTE); } +void manager_load_scheduled_shutdown(Manager *m) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *usec = NULL, + *warn_wall = NULL, + *mode = NULL, + *wall_message = NULL, + *uid = NULL, + *tty = NULL; + int r; + + assert(m); + + r = parse_env_file(f, SHUTDOWN_SCHEDULE_FILE, + "USEC", &usec, + "WARN_WALL", &warn_wall, + "MODE", &mode, + "WALL_MESSAGE", &wall_message, + "UID", &uid, + "TTY", &tty); + + /* reset will delete the file */ + reset_scheduled_shutdown(m); + + if (r == -ENOENT) + return; + if (r < 0) + return (void) log_debug_errno(r, "Failed to parse " SHUTDOWN_SCHEDULE_FILE ": %m"); + + HandleAction handle = handle_action_from_string(mode); + if (handle < 0) + return (void) log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to parse scheduled shutdown type: %s", mode); + + if (!usec) + return (void) log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "USEC is required"); + if (deserialize_usec(usec, &m->scheduled_shutdown_timeout) < 0) + return; + + /* assign parsed type only after we know usec is also valid */ + m->scheduled_shutdown_action = handle_action_lookup(handle); + + if (warn_wall) { + r = parse_boolean(warn_wall); + if (r < 0) + log_debug_errno(r, "Failed to parse enabling wall messages"); + else + m->enable_wall_messages = r; + } + + if (wall_message) { + _cleanup_free_ char *unescaped = NULL; + r = cunescape(wall_message, 0, &unescaped); + if (r < 0) + log_debug_errno(r, "Failed to parse wall message: %s", wall_message); + else + free_and_replace(m->wall_message, unescaped); + } + + if (uid) { + r = parse_uid(uid, &m->scheduled_shutdown_uid); + if (r < 0) + log_debug_errno(r, "Failed to parse wall uid: %s", uid); + } + + free_and_replace(m->scheduled_shutdown_tty, tty); + + r = manager_setup_shutdown_timers(m); + if (r < 0) + return reset_scheduled_shutdown(m); + + (void) manager_setup_wall_message_timer(m); + (void) update_schedule_file(m); + + return; +} + static int update_schedule_file(Manager *m) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; int r; assert(m); + assert(m->scheduled_shutdown_action); - r = mkdir_safe_label("/run/systemd/shutdown", 0755, 0, 0, MKDIR_WARN_MODE); + r = mkdir_parents_label(SHUTDOWN_SCHEDULE_FILE, 0755); if (r < 0) return log_error_errno(r, "Failed to create shutdown subdirectory: %m"); - r = fopen_temporary("/run/systemd/shutdown/scheduled", &f, &temp_path); + r = fopen_temporary(SHUTDOWN_SCHEDULE_FILE, &f, &temp_path); if (r < 0) return log_error_errno(r, "Failed to save information about scheduled shutdowns: %m"); (void) fchmod(fileno(f), 0644); - fprintf(f, - "USEC="USEC_FMT"\n" - "WARN_WALL=%i\n" - "MODE=%s\n", - m->scheduled_shutdown_timeout, - m->enable_wall_messages, - m->scheduled_shutdown_type); + serialize_usec(f, "USEC", m->scheduled_shutdown_timeout); + serialize_item_format(f, "WARN_WALL", "%s", one_zero(m->enable_wall_messages)); + serialize_item_format(f, "MODE", "%s", handle_action_to_string(m->scheduled_shutdown_action->handle)); + serialize_item_format(f, "UID", UID_FMT, m->scheduled_shutdown_uid); + + if (m->scheduled_shutdown_tty) + serialize_item_format(f, "TTY", "%s", m->scheduled_shutdown_tty); if (!isempty(m->wall_message)) { - _cleanup_free_ char *t = NULL; - - t = cescape(m->wall_message); - if (!t) { - r = -ENOMEM; + r = serialize_item_escaped(f, "WALL_MESSAGE", m->wall_message); + if (r < 0) goto fail; - } - - fprintf(f, "WALL_MESSAGE=%s\n", t); } r = fflush_and_check(f); if (r < 0) goto fail; - if (rename(temp_path, "/run/systemd/shutdown/scheduled") < 0) { + if (rename(temp_path, SHUTDOWN_SCHEDULE_FILE) < 0) { r = -errno; goto fail; } @@ -2104,7 +2166,7 @@ static int update_schedule_file(Manager *m) { fail: (void) unlink(temp_path); - (void) unlink("/run/systemd/shutdown/scheduled"); + (void) unlink(SHUTDOWN_SCHEDULE_FILE); return log_error_errno(r, "Failed to write information about scheduled shutdowns: %m"); } @@ -2116,8 +2178,11 @@ static void reset_scheduled_shutdown(Manager *m) { m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source); m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source); - m->scheduled_shutdown_type = mfree(m->scheduled_shutdown_type); - m->scheduled_shutdown_timeout = 0; + m->scheduled_shutdown_action = NULL; + m->scheduled_shutdown_timeout = USEC_INFINITY; + m->scheduled_shutdown_uid = UID_INVALID; + m->scheduled_shutdown_tty = mfree(m->scheduled_shutdown_tty); + m->wall_message = mfree(m->wall_message); m->shutdown_dry_run = false; if (m->unlink_nologin) { @@ -2125,7 +2190,7 @@ static void reset_scheduled_shutdown(Manager *m) { m->unlink_nologin = false; } - (void) unlink("/run/systemd/shutdown/scheduled"); + (void) unlink(SHUTDOWN_SCHEDULE_FILE); } static int manager_scheduled_shutdown_handler( @@ -2133,31 +2198,20 @@ static int manager_scheduled_shutdown_handler( uint64_t usec, void *userdata) { + const HandleActionData *a = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; Manager *m = userdata; - const char *target; int r; assert(m); - if (isempty(m->scheduled_shutdown_type)) - return 0; - - if (streq(m->scheduled_shutdown_type, "poweroff")) - target = SPECIAL_POWEROFF_TARGET; - else if (streq(m->scheduled_shutdown_type, "reboot")) - target = SPECIAL_REBOOT_TARGET; - else if (streq(m->scheduled_shutdown_type, "kexec")) - target = SPECIAL_KEXEC_TARGET; - else if (streq(m->scheduled_shutdown_type, "halt")) - target = SPECIAL_HALT_TARGET; - else - assert_not_reached(); + a = m->scheduled_shutdown_action; + assert(a); /* Don't allow multiple jobs being executed at the same time */ - if (m->action_what > 0) { + if (m->delayed_action) { r = -EALREADY; - log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress", target); + log_error("Scheduled shutdown to %s failed: shutdown or sleep operation already in progress", a->target); goto error; } @@ -2167,16 +2221,16 @@ static int manager_scheduled_shutdown_handler( * above) for some seconds after our admin has seen the final * wall message. */ - bus_manager_log_shutdown(m, target); + bus_manager_log_shutdown(m, a); log_info("Running in dry run, suppressing action."); reset_scheduled_shutdown(m); return 0; } - r = bus_manager_shutdown_or_sleep_now_or_later(m, target, INHIBIT_SHUTDOWN, &error); + r = bus_manager_shutdown_or_sleep_now_or_later(m, m->scheduled_shutdown_action, &error); if (r < 0) { - log_error_errno(r, "Scheduled shutdown to %s failed: %m", target); + log_error_errno(r, "Scheduled shutdown to %s failed: %m", a->target); goto error; } @@ -2189,10 +2243,8 @@ error: static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; - _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - const char *action_multiple_sessions = NULL; - const char *action_ignore_inhibit = NULL; - const char *action = NULL; + HandleAction handle; + const HandleActionData *a; uint64_t elapse; char *type; int r; @@ -2210,107 +2262,105 @@ static int method_schedule_shutdown(sd_bus_message *message, void *userdata, sd_ dry_run = true; } - if (streq(type, "poweroff")) { - action = "org.freedesktop.login1.power-off"; - action_multiple_sessions = "org.freedesktop.login1.power-off-multiple-sessions"; - action_ignore_inhibit = "org.freedesktop.login1.power-off-ignore-inhibit"; - } else if (STR_IN_SET(type, "reboot", "kexec")) { - action = "org.freedesktop.login1.reboot"; - action_multiple_sessions = "org.freedesktop.login1.reboot-multiple-sessions"; - action_ignore_inhibit = "org.freedesktop.login1.reboot-ignore-inhibit"; - } else if (streq(type, "halt")) { - action = "org.freedesktop.login1.halt"; - action_multiple_sessions = "org.freedesktop.login1.halt-multiple-sessions"; - action_ignore_inhibit = "org.freedesktop.login1.halt-ignore-inhibit"; - } else + handle = handle_action_from_string(type); + if (!IN_SET(handle, HANDLE_POWEROFF, HANDLE_REBOOT, HANDLE_HALT, HANDLE_KEXEC)) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Unsupported shutdown type"); - r = verify_shutdown_creds(m, message, INHIBIT_SHUTDOWN, action, action_multiple_sessions, - action_ignore_inhibit, 0, error); + a = handle_action_lookup(handle); + assert(a); + assert(a->polkit_action); + + r = verify_shutdown_creds(m, message, a, 0, error); if (r != 0) return r; - if (m->scheduled_shutdown_timeout_source) { - r = sd_event_source_set_time(m->scheduled_shutdown_timeout_source, elapse); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed: %m"); - - r = sd_event_source_set_enabled(m->scheduled_shutdown_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); - } else { - r = sd_event_add_time(m->event, &m->scheduled_shutdown_timeout_source, - CLOCK_REALTIME, elapse, 0, manager_scheduled_shutdown_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed: %m"); - } - - r = free_and_strdup(&m->scheduled_shutdown_type, type); - if (r < 0) { - m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); - return log_oom(); - } - + m->scheduled_shutdown_action = a; m->shutdown_dry_run = dry_run; - - if (m->nologin_timeout_source) { - r = sd_event_source_set_time(m->nologin_timeout_source, nologin_timeout_usec(elapse)); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed: %m"); - - r = sd_event_source_set_enabled(m->nologin_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed: %m"); - } else { - r = sd_event_add_time(m->event, &m->nologin_timeout_source, - CLOCK_REALTIME, nologin_timeout_usec(elapse), 0, nologin_timeout_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed: %m"); - } - m->scheduled_shutdown_timeout = elapse; - r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); - if (r >= 0) { - const char *tty = NULL; + r = manager_setup_shutdown_timers(m); + if (r < 0) + return r; - (void) sd_bus_creds_get_uid(creds, &m->scheduled_shutdown_uid); - (void) sd_bus_creds_get_tty(creds, &tty); + r = setup_wall_message_timer(m, message); + if (r >= 0) + r = update_schedule_file(m); - r = free_and_strdup(&m->scheduled_shutdown_tty, tty); - if (r < 0) { - m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); - return log_oom(); - } + if (r < 0) { + reset_scheduled_shutdown(m); + return r; } - r = manager_setup_wall_message_timer(m); - if (r < 0) - return r; - - r = update_schedule_file(m); - if (r < 0) - return r; - return sd_bus_reply_method_return(message, NULL); } +static int manager_setup_shutdown_timers(Manager* m) { + int r; + + r = event_reset_time(m->event, &m->scheduled_shutdown_timeout_source, + CLOCK_REALTIME, + m->scheduled_shutdown_timeout, 0, + manager_scheduled_shutdown_handler, m, + 0, "scheduled-shutdown-timeout", true); + if (r < 0) + goto fail; + + r = event_reset_time(m->event, &m->nologin_timeout_source, + CLOCK_REALTIME, + nologin_timeout_usec(m->scheduled_shutdown_timeout), 0, + nologin_timeout_handler, m, + 0, "nologin-timeout", true); + if (r < 0) + goto fail; + + return 0; + +fail: + m->scheduled_shutdown_timeout_source = sd_event_source_unref(m->scheduled_shutdown_timeout_source); + m->nologin_timeout_source = sd_event_source_unref(m->nologin_timeout_source); + + return r; +} + static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; + const HandleActionData *a; bool cancelled; + int r; assert(m); assert(message); - cancelled = m->scheduled_shutdown_type != NULL; + cancelled = m->scheduled_shutdown_action + && !IN_SET(m->scheduled_shutdown_action->handle, HANDLE_IGNORE, _HANDLE_ACTION_INVALID); + if (!cancelled) + return sd_bus_reply_method_return(message, "b", false); + + a = m->scheduled_shutdown_action; + if (!a->polkit_action) + return sd_bus_error_set(error, SD_BUS_ERROR_AUTH_FAILED, "Unsupported shutdown type"); + + r = bus_verify_polkit_async( + message, + CAP_SYS_BOOT, + a->polkit_action, + NULL, + false, + UID_INVALID, + &m->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + reset_scheduled_shutdown(m); - if (cancelled && m->enable_wall_messages) { + if (m->enable_wall_messages) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; _cleanup_free_ char *username = NULL; const char *tty = NULL; uid_t uid = 0; - int r; r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_AUGMENT|SD_BUS_CREDS_TTY|SD_BUS_CREDS_UID, &creds); if (r >= 0) { @@ -2323,21 +2373,16 @@ static int method_cancel_scheduled_shutdown(sd_bus_message *message, void *userd username, tty, logind_wall_tty_filter, m); } - return sd_bus_reply_method_return(message, "b", cancelled); + return sd_bus_reply_method_return(message, "b", true); } static int method_can_shutdown_or_sleep( Manager *m, sd_bus_message *message, - InhibitWhat w, - const char *action, - const char *action_multiple_sessions, - const char *action_ignore_inhibit, - SleepOperation sleep_operation, + const HandleActionData *a, sd_bus_error *error) { _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL; - HandleAction handle; bool multiple_sessions, challenge, blocked; const char *result = NULL; uid_t uid; @@ -2345,14 +2390,10 @@ static int method_can_shutdown_or_sleep( assert(m); assert(message); - assert(w >= 0); - assert(w <= _INHIBIT_WHAT_MAX); - assert(action); - assert(action_multiple_sessions); - assert(action_ignore_inhibit); + assert(a); - if (sleep_operation >= 0) { - r = can_sleep(sleep_operation); + if (a->sleep_operation >= 0) { + r = can_sleep(a->sleep_operation); if (IN_SET(r, 0, -ENOSPC)) return sd_bus_reply_method_return(message, "s", "na"); if (r < 0) @@ -2372,13 +2413,13 @@ static int method_can_shutdown_or_sleep( return r; multiple_sessions = r > 0; - blocked = manager_is_inhibited(m, w, INHIBIT_BLOCK, NULL, false, true, uid, NULL); + blocked = manager_is_inhibited(m, a->inhibit_what, INHIBIT_BLOCK, NULL, false, true, uid, NULL); - handle = handle_action_from_string(sleep_operation_to_string(sleep_operation)); + HandleAction handle = handle_action_from_string(sleep_operation_to_string(a->sleep_operation)); if (handle >= 0) { const char *target; - target = manager_target_for_action(handle); + target = handle_action_lookup(handle)->target; if (target) { _cleanup_free_ char *load_state = NULL; @@ -2394,7 +2435,7 @@ static int method_can_shutdown_or_sleep( } if (multiple_sessions) { - r = bus_test_polkit(message, CAP_SYS_BOOT, action_multiple_sessions, NULL, UID_INVALID, &challenge, error); + r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_multiple_sessions, NULL, UID_INVALID, &challenge, error); if (r < 0) return r; @@ -2407,7 +2448,7 @@ static int method_can_shutdown_or_sleep( } if (blocked) { - r = bus_test_polkit(message, CAP_SYS_BOOT, action_ignore_inhibit, NULL, UID_INVALID, &challenge, error); + r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action_ignore_inhibit, NULL, UID_INVALID, &challenge, error); if (r < 0) return r; @@ -2425,7 +2466,7 @@ static int method_can_shutdown_or_sleep( /* If neither inhibit nor multiple sessions * apply then just check the normal policy */ - r = bus_test_polkit(message, CAP_SYS_BOOT, action, NULL, UID_INVALID, &challenge, error); + r = bus_test_polkit(message, CAP_SYS_BOOT, a->polkit_action, NULL, UID_INVALID, &challenge, error); if (r < 0) return r; @@ -2445,12 +2486,7 @@ static int method_can_poweroff(sd_bus_message *message, void *userdata, sd_bus_e Manager *m = userdata; return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.power-off", - "org.freedesktop.login1.power-off-multiple-sessions", - "org.freedesktop.login1.power-off-ignore-inhibit", - _SLEEP_OPERATION_INVALID, + m, message, handle_action_lookup(HANDLE_POWEROFF), error); } @@ -2458,12 +2494,7 @@ static int method_can_reboot(sd_bus_message *message, void *userdata, sd_bus_err Manager *m = userdata; return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.reboot", - "org.freedesktop.login1.reboot-multiple-sessions", - "org.freedesktop.login1.reboot-ignore-inhibit", - _SLEEP_OPERATION_INVALID, + m, message, handle_action_lookup(HANDLE_REBOOT), error); } @@ -2471,12 +2502,7 @@ static int method_can_halt(sd_bus_message *message, void *userdata, sd_bus_error Manager *m = userdata; return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SHUTDOWN, - "org.freedesktop.login1.halt", - "org.freedesktop.login1.halt-multiple-sessions", - "org.freedesktop.login1.halt-ignore-inhibit", - _SLEEP_OPERATION_INVALID, + m, message, handle_action_lookup(HANDLE_HALT), error); } @@ -2484,12 +2510,7 @@ static int method_can_suspend(sd_bus_message *message, void *userdata, sd_bus_er Manager *m = userdata; return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SLEEP, - "org.freedesktop.login1.suspend", - "org.freedesktop.login1.suspend-multiple-sessions", - "org.freedesktop.login1.suspend-ignore-inhibit", - SLEEP_SUSPEND, + m, message, handle_action_lookup(HANDLE_SUSPEND), error); } @@ -2497,12 +2518,7 @@ static int method_can_hibernate(sd_bus_message *message, void *userdata, sd_bus_ Manager *m = userdata; return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - SLEEP_HIBERNATE, + m, message, handle_action_lookup(HANDLE_HIBERNATE), error); } @@ -2510,12 +2526,7 @@ static int method_can_hybrid_sleep(sd_bus_message *message, void *userdata, sd_b Manager *m = userdata; return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - SLEEP_HYBRID_SLEEP, + m, message, handle_action_lookup(HANDLE_HYBRID_SLEEP), error); } @@ -2523,12 +2534,7 @@ static int method_can_suspend_then_hibernate(sd_bus_message *message, void *user Manager *m = userdata; return method_can_shutdown_or_sleep( - m, message, - INHIBIT_SLEEP, - "org.freedesktop.login1.hibernate", - "org.freedesktop.login1.hibernate-multiple-sessions", - "org.freedesktop.login1.hibernate-ignore-inhibit", - SLEEP_SUSPEND_THEN_HIBERNATE, + m, message, handle_action_lookup(HANDLE_SUSPEND_THEN_HIBERNATE), error); } @@ -2996,21 +3002,21 @@ static int property_get_reboot_to_boot_loader_entry( } static int boot_loader_entry_exists(Manager *m, const char *id) { - _cleanup_(boot_config_free) BootConfig config = {}; + _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL; int r; assert(m); assert(id); - r = boot_entries_load_config_auto(NULL, NULL, &config); + r = boot_config_load_auto(&config, NULL, NULL); if (r < 0 && r != -ENOKEY) /* don't complain if no GPT is found, hence skip ENOKEY */ return r; r = manager_read_efi_boot_loader_entries(m); if (r >= 0) - (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, true); + (void) boot_config_augment_from_loader(&config, m->efi_boot_loader_entries, /* auto_only= */ true); - return boot_config_has_entry(&config, id); + return !!boot_config_find_entry(&config, id); } static int method_set_reboot_to_boot_loader_entry( @@ -3151,7 +3157,7 @@ static int property_get_boot_loader_entries( void *userdata, sd_bus_error *error) { - _cleanup_(boot_config_free) BootConfig config = {}; + _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL; Manager *m = userdata; size_t i; int r; @@ -3160,13 +3166,13 @@ static int property_get_boot_loader_entries( assert(reply); assert(m); - r = boot_entries_load_config_auto(NULL, NULL, &config); + r = boot_config_load_auto(&config, NULL, NULL); if (r < 0 && r != -ENOKEY) /* don't complain if there's no GPT found */ return r; r = manager_read_efi_boot_loader_entries(m); if (r >= 0) - (void) boot_entries_augment_from_loader(&config, m->efi_boot_loader_entries, true); + (void) boot_config_augment_from_loader(&config, m->efi_boot_loader_entries, /* auto_only= */ true); r = sd_bus_message_open_container(reply, 'a', "s"); if (r < 0) @@ -3191,7 +3197,7 @@ static int method_set_wall_message( int r; Manager *m = userdata; char *wall_message; - unsigned enable_wall_messages; + int enable_wall_messages; assert(message); assert(m); @@ -3200,6 +3206,11 @@ static int method_set_wall_message( if (r < 0) return r; + if (strlen(wall_message) > WALL_MESSAGE_MAX) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Wall message too long, maximum permitted length is %u characters.", + WALL_MESSAGE_MAX); + /* Short-circuit the operation if the desired state is already in place, to * avoid an unnecessary polkit permission check. */ if (streq_ptr(m->wall_message, empty_to_null(wall_message)) && @@ -3267,7 +3278,7 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error * executing the operation. We shouldn't create the impression * that the lock was successful if the machine is about to go * down/suspend any moment. */ - if (m->action_what & w) + if (m->delayed_action && m->delayed_action->inhibit_what & w) return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "The operation inhibition has been requested for is already running"); @@ -3347,13 +3358,13 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error static const sd_bus_vtable manager_vtable[] = { SD_BUS_VTABLE_START(0), - SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", NULL, NULL, offsetof(Manager, enable_wall_messages), 0), + SD_BUS_WRITABLE_PROPERTY("EnableWallMessages", "b", bus_property_get_bool, bus_property_set_bool, offsetof(Manager, enable_wall_messages), 0), SD_BUS_WRITABLE_PROPERTY("WallMessage", "s", NULL, NULL, offsetof(Manager, wall_message), 0), SD_BUS_PROPERTY("NAutoVTs", "u", NULL, offsetof(Manager, n_autovts), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KillOnlyUsers", "as", NULL, offsetof(Manager, kill_only_users), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KillExcludeUsers", "as", NULL, offsetof(Manager, kill_exclude_users), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("KillUserProcesses", "b", NULL, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("KillUserProcesses", "b", bus_property_get_bool, offsetof(Manager, kill_user_processes), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RebootParameter", "s", property_get_reboot_parameter, 0, 0), SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, 0), SD_BUS_PROPERTY("RebootToBootLoaderMenu", "t", property_get_reboot_to_boot_loader_menu, 0, 0), @@ -3774,14 +3785,14 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err } if (m->action_job && streq(m->action_job, path)) { - log_info("Operation '%s' finished.", inhibit_what_to_string(m->action_what)); + assert(m->delayed_action); + log_info("Operation '%s' finished.", inhibit_what_to_string(m->delayed_action->inhibit_what)); /* Tell people that they now may take a lock again */ - (void) send_prepare_for(m, m->action_what, false); + (void) send_prepare_for(m, m->delayed_action->inhibit_what, false); m->action_job = mfree(m->action_job); - m->action_unit = NULL; - m->action_what = 0; + m->delayed_action = NULL; return 0; } @@ -3803,8 +3814,8 @@ int match_job_removed(sd_bus_message *message, void *userdata, sd_bus_error *err if (streq_ptr(path, user->service_job)) { user->service_job = mfree(user->service_job); - LIST_FOREACH(sessions_by_user, session, user->sessions) - (void) session_jobs_reply(session, id, unit, NULL /* don't propagate user service failures to the client */); + LIST_FOREACH(sessions_by_user, s, user->sessions) + (void) session_jobs_reply(s, id, unit, NULL /* don't propagate user service failures to the client */); user_save(user); } @@ -3947,7 +3958,6 @@ int manager_start_scope( char **job) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; - char **i; int r; assert(manager); diff --git a/src/login/logind-dbus.h b/src/login/logind-dbus.h index 6b5d3abcd..11eb170d1 100644 --- a/src/login/logind-dbus.h +++ b/src/login/logind-dbus.h @@ -4,6 +4,7 @@ #include "sd-bus.h" #include "bus-object.h" +#include "logind-action.h" #include "logind-session.h" #include "logind-user.h" #include "logind.h" @@ -14,7 +15,7 @@ int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char 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 bus_manager_shutdown_or_sleep_now_or_later(Manager *m, const HandleActionData *a, 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); @@ -31,4 +32,6 @@ int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo 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); +void manager_load_scheduled_shutdown(Manager *m); + extern const BusObjectImplementation manager_object; diff --git a/src/login/logind-device.c b/src/login/logind-device.c index 982a77219..592ee3b9d 100644 --- a/src/login/logind-device.c +++ b/src/login/logind-device.c @@ -67,7 +67,6 @@ void device_free(Device *d) { } void device_attach(Device *d, Seat *s) { - Device *i; bool had_master; assert(d); diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c index f82991c5d..cd20693cb 100644 --- a/src/login/logind-seat-dbus.c +++ b/src/login/logind-seat-dbus.c @@ -56,7 +56,6 @@ static int property_get_sessions( sd_bus_error *error) { Seat *s = userdata; - Session *session; int r; assert(bus); diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index 58912b85b..88ff4ce45 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -122,8 +122,6 @@ int seat_save(Seat *s) { } if (s->sessions) { - Session *i; - fputs("SESSIONS=", f); LIST_FOREACH(sessions_by_seat, i, s->sessions) { fprintf(f, @@ -323,24 +321,19 @@ int seat_switch_to_next(Seat *s) { } int seat_switch_to_previous(Seat *s) { - unsigned start, i; - Session *session; - if (MALLOC_ELEMENTSOF(s->positions) == 0) return -EINVAL; - start = 1; - if (s->active && s->active->position > 0) - start = s->active->position; + size_t start = s->active && s->active->position > 0 ? s->active->position : 1; - for (i = start - 1; i > 0; --i) { - session = seat_get_position(s, i); + for (size_t i = start - 1; i > 0; i--) { + Session *session = seat_get_position(s, i); if (session) return session_activate(session); } - for (i = MALLOC_ELEMENTSOF(s->positions) - 1; i > start; --i) { - session = seat_get_position(s, i); + for (size_t i = MALLOC_ELEMENTSOF(s->positions) - 1; i > start; i--) { + Session *session = seat_get_position(s, i); if (session) return session_activate(session); } @@ -349,7 +342,7 @@ int seat_switch_to_previous(Seat *s) { } int seat_active_vt_changed(Seat *s, unsigned vtnr) { - Session *i, *new_active = NULL; + Session *new_active = NULL; int r; assert(s); @@ -368,7 +361,7 @@ int seat_active_vt_changed(Seat *s, unsigned vtnr) { break; } - if (!new_active) { + if (!new_active) /* no running one? then we can't decide which one is the * active one, let the first one win */ LIST_FOREACH(sessions_by_seat, i, s->sessions) @@ -376,7 +369,6 @@ int seat_active_vt_changed(Seat *s, unsigned vtnr) { new_active = i; break; } - } r = seat_set_active(s, new_active); manager_spawn_autovt(s->manager, vtnr); @@ -467,7 +459,6 @@ int seat_stop(Seat *s, bool force) { } int seat_stop_sessions(Seat *s, bool force) { - Session *session; int r = 0, k; assert(s); @@ -482,7 +473,6 @@ int seat_stop_sessions(Seat *s, bool force) { } void seat_evict_position(Seat *s, Session *session) { - Session *iter; unsigned pos = session->position; session->position = 0; @@ -496,12 +486,11 @@ void seat_evict_position(Seat *s, Session *session) { /* There might be another session claiming the same * position (eg., during gdm->session transition), so let's look * for it and set it on the free slot. */ - LIST_FOREACH(sessions_by_seat, iter, s->sessions) { + LIST_FOREACH(sessions_by_seat, iter, s->sessions) if (iter->position == pos && session_get_state(iter) != SESSION_CLOSING) { s->positions[pos] = iter; break; } - } } } @@ -599,7 +588,6 @@ bool seat_can_graphical(Seat *s) { } int seat_get_idle_hint(Seat *s, dual_timestamp *t) { - Session *session; bool idle_hint = true; dual_timestamp ts = DUAL_TIMESTAMP_NULL; diff --git a/src/login/logind-session.c b/src/login/logind-session.c index ab98a5055..eef48c252 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -445,7 +445,6 @@ int session_load(Session *s) { "ACTIVE", &active, "DEVICES", &devices, "IS_DISPLAY", &is_display); - if (r < 0) return log_error_errno(r, "Failed to read %s: %m", s->state_file); diff --git a/src/login/logind-user-dbus.c b/src/login/logind-user-dbus.c index 572a16a7a..67b6556d7 100644 --- a/src/login/logind-user-dbus.c +++ b/src/login/logind-user-dbus.c @@ -106,7 +106,6 @@ static int property_get_sessions( sd_bus_error *error) { User *u = userdata; - Session *session; int r; assert(bus); diff --git a/src/login/logind-user.c b/src/login/logind-user.c index 6d61b55c2..3f1b8f610 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -189,7 +189,6 @@ static int user_save_internal(User *u) { u->last_session_timestamp); if (u->sessions) { - Session *i; bool first; fputs("SESSIONS=", f); @@ -499,8 +498,8 @@ static void user_stop_service(User *u, bool force) { } int user_stop(User *u, bool force) { - Session *s; int r = 0; + assert(u); /* This is called whenever we begin with tearing down a user record. It's called in two cases: explicit API @@ -534,7 +533,6 @@ int user_stop(User *u, bool force) { } int user_finalize(User *u) { - Session *s; int r = 0, k; assert(u); @@ -575,7 +573,6 @@ int user_finalize(User *u) { } int user_get_idle_hint(User *u, dual_timestamp *t) { - Session *s; bool idle_hint = true; dual_timestamp ts = DUAL_TIMESTAMP_NULL; @@ -630,7 +627,6 @@ int user_check_linger_file(User *u) { } static bool user_unit_active(User *u) { - const char *i; int r; assert(u->service); @@ -642,7 +638,7 @@ static bool user_unit_active(User *u) { r = manager_unit_is_active(u->manager, i, &error); if (r < 0) - log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", u->service, bus_error_message(&error, r)); + log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", i, bus_error_message(&error, r)); if (r != 0) return true; } @@ -721,8 +717,6 @@ void user_add_to_gc_queue(User *u) { } UserState user_get_state(User *u) { - Session *i; - assert(u); if (u->stopping) @@ -805,8 +799,6 @@ static int elect_display_compare(Session *s1, Session *s2) { } void user_elect_display(User *u) { - Session *s; - assert(u); /* This elects a primary session for each user, which we call the "display". We try to keep the assignment @@ -856,7 +848,7 @@ void user_update_last_session_timer(User *u) { assert(!u->timer_event_source); user_stop_delay = user_get_stop_delay(u); - if (IN_SET(user_stop_delay, 0, USEC_INFINITY)) + if (!timestamp_is_set(user_stop_delay)) return; if (sd_event_get_state(u->manager->event) == SD_EVENT_FINISHED) { diff --git a/src/login/logind-utmp.c b/src/login/logind-utmp.c index 553383647..1db5050c3 100644 --- a/src/login/logind-utmp.c +++ b/src/login/logind-utmp.c @@ -10,6 +10,7 @@ #include "bus-common-errors.h" #include "bus-error.h" #include "bus-util.h" +#include "event-util.h" #include "format-util.h" #include "logind.h" #include "path-util.h" @@ -64,7 +65,7 @@ static int warn_wall(Manager *m, usec_t n) { assert(m); - if (!m->enable_wall_messages) + if (!m->enable_wall_messages || !m->scheduled_shutdown_action) return 0; left = m->scheduled_shutdown_timeout > n; @@ -72,7 +73,7 @@ static int warn_wall(Manager *m, usec_t n) { r = asprintf(&l, "%s%sThe system is going down for %s %s%s!", strempty(m->wall_message), isempty(m->wall_message) ? "" : "\n", - m->scheduled_shutdown_type, + handle_action_to_string(m->scheduled_shutdown_action->handle), left ? "at " : "NOW", left ? FORMAT_TIMESTAMP(m->scheduled_shutdown_timeout) : ""); if (r < 0) { @@ -130,16 +131,14 @@ int manager_setup_wall_message_timer(Manager *m) { /* wall message handling */ - if (isempty(m->scheduled_shutdown_type)) { - warn_wall(m, n); + if (!m->scheduled_shutdown_action) return 0; - } - if (elapse < n) + if (elapse > 0 && elapse < n) return 0; /* Warn immediately if less than 15 minutes are left */ - if (elapse - n < 15 * USEC_PER_MINUTE) { + if (elapse == 0 || elapse - n < 15 * USEC_PER_MINUTE) { r = warn_wall(m, n); if (r == 0) return 0; @@ -149,19 +148,15 @@ int manager_setup_wall_message_timer(Manager *m) { if (elapse == 0) return 0; - if (m->wall_message_timeout_source) { - r = sd_event_source_set_time(m->wall_message_timeout_source, n + elapse); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_time() failed. %m"); + r = event_reset_time(m->event, &m->wall_message_timeout_source, + CLOCK_REALTIME, + n + elapse, 0, + wall_message_timeout_handler, m, + 0, "wall-message-timer", true); - r = sd_event_source_set_enabled(m->wall_message_timeout_source, SD_EVENT_ONESHOT); - if (r < 0) - return log_error_errno(r, "sd_event_source_set_enabled() failed. %m"); - } else { - r = sd_event_add_time(m->event, &m->wall_message_timeout_source, - CLOCK_REALTIME, n + elapse, 0, wall_message_timeout_handler, m); - if (r < 0) - return log_error_errno(r, "sd_event_add_time() failed. %m"); + if (r < 0) { + m->wall_message_timeout_source = sd_event_source_unref(m->wall_message_timeout_source); + return log_error_errno(r, "Failed to set up wall message timer: %m"); } return 0; diff --git a/src/login/logind.c b/src/login/logind.c index 52b1d9503..0483902ed 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -54,6 +54,7 @@ static int manager_new(Manager **ret) { *m = (Manager) { .console_active_fd = -1, .reserve_vt_fd = -1, + .enable_wall_messages = true, .idle_action_not_before_usec = now(CLOCK_MONOTONIC), }; @@ -167,7 +168,6 @@ static Manager* manager_unref(Manager *m) { strv_free(m->kill_only_users); strv_free(m->kill_exclude_users); - free(m->scheduled_shutdown_type); free(m->scheduled_shutdown_tty); free(m->wall_message); free(m->action_job); @@ -685,7 +685,7 @@ static int manager_connect_bus(Manager *m) { static int manager_vt_switch(sd_event_source *src, const struct signalfd_siginfo *si, void *data) { Manager *m = data; - Session *active, *iter; + Session *active; /* * We got a VT-switch signal and we have to acknowledge it immediately. @@ -732,16 +732,14 @@ static int manager_vt_switch(sd_event_source *src, const struct signalfd_siginfo return 0; } - if (active->vtfd >= 0) { + if (active->vtfd >= 0) session_leave_vt(active); - } else { - LIST_FOREACH(sessions_by_seat, iter, m->seat0->sessions) { + else + LIST_FOREACH(sessions_by_seat, iter, m->seat0->sessions) if (iter->vtnr == active->vtnr && iter->vtfd >= 0) { session_leave_vt(iter); break; } - } - } return 0; } @@ -1090,6 +1088,8 @@ static int manager_startup(Manager *m) { if (r < 0) log_warning_errno(r, "Button enumeration failed: %m"); + manager_load_scheduled_shutdown(m); + /* Remove stale objects before we start them */ manager_gc(m, false); diff --git a/src/login/logind.h b/src/login/logind.h index 730c14a46..2136486c6 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -68,21 +68,15 @@ struct Manager { usec_t inhibit_delay_max; usec_t user_stop_delay; - /* If an action is currently being executed or is delayed, - * this is != 0 and encodes what is being done */ - InhibitWhat action_what; + /* If a shutdown/suspend was delayed due to an inhibitor this contains the action we are supposed to + * start after the delay is over */ + const HandleActionData *delayed_action; - /* If a shutdown/suspend was delayed due to an inhibitor this - contains the unit name we are supposed to start after the - delay is over */ - const char *action_unit; - - /* If a shutdown/suspend is currently executed, then this is - * the job of it */ + /* If a shutdown/suspend is currently executed, then this is the job of it */ char *action_job; sd_event_source *inhibit_timeout_source; - char *scheduled_shutdown_type; + const HandleActionData *scheduled_shutdown_action; usec_t scheduled_shutdown_timeout; sd_event_source *scheduled_shutdown_timeout_source; uid_t scheduled_shutdown_uid; @@ -91,7 +85,7 @@ struct Manager { bool unlink_nologin; char *wall_message; - unsigned enable_wall_messages; + bool enable_wall_messages; sd_event_source *wall_message_timeout_source; bool shutdown_dry_run; diff --git a/src/login/meson.build b/src/login/meson.build index 98495cc74..1a2d738e8 100644 --- a/src/login/meson.build +++ b/src/login/meson.build @@ -99,12 +99,12 @@ endif ############################################################ tests += [ - [['src/login/test-login-shared.c']], + [files('test-login-shared.c')], - [['src/login/test-inhibit.c'], + [files('test-inhibit.c'), [], [], [], '', 'manual'], - [['src/login/test-login-tables.c'], + [files('test-login-tables.c'), [liblogind_core, libshared], [threads]], diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 5bd7efc3e..2047dc181 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -533,7 +533,6 @@ static int pam_putenv_and_log(pam_handle_t *handle, const char *e, bool debug) { } static int apply_user_record_settings(pam_handle_t *handle, UserRecord *ur, bool debug) { - char **i; int r; assert(handle); diff --git a/src/login/test-login-shared.c b/src/login/test-login-shared.c index d3de9c495..17cd479dc 100644 --- a/src/login/test-login-shared.c +++ b/src/login/test-login-shared.c @@ -2,8 +2,9 @@ #include "login-util.h" #include "macro.h" +#include "tests.h" -static void test_session_id_valid(void) { +TEST(session_id_valid) { assert_se(session_id_valid("c1")); assert_se(session_id_valid("1234")); @@ -12,11 +13,4 @@ static void test_session_id_valid(void) { assert_se(!session_id_valid("\tid")); } -int main(int argc, char* argv[]) { - log_parse_environment(); - log_open(); - - test_session_id_valid(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/login/user-runtime-dir.c b/src/login/user-runtime-dir.c index 5ce5b35e1..f96a2d866 100644 --- a/src/login/user-runtime-dir.c +++ b/src/login/user-runtime-dir.c @@ -80,7 +80,9 @@ static int user_mkdir_runtime_path( uid, gid, runtime_dir_size, runtime_dir_inodes, mac_smack_use() ? ",smackfsroot=*" : ""); - (void) mkdir_label(runtime_path, 0700); + r = mkdir_label(runtime_path, 0700); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Failed to create %s: %m", runtime_path); r = mount_nofollow_verbose(LOG_DEBUG, "tmpfs", runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, options); if (r < 0) { diff --git a/src/machine/image-dbus.c b/src/machine/image-dbus.c index e6ffb5292..1ff186972 100644 --- a/src/machine/image-dbus.c +++ b/src/machine/image-dbus.c @@ -43,11 +43,17 @@ int bus_image_method_remove( if (m->n_operations >= OPERATIONS_MAX) return sd_bus_error_set(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Too many ongoing operations."); + const char *details[] = { + "image", image->name, + "verb", "remove", + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", - NULL, + details, false, UID_INVALID, &m->polkit_registry, @@ -108,11 +114,18 @@ int bus_image_method_rename( if (!image_name_is_valid(new_name)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name); + const char *details[] = { + "image", image->name, + "verb", "rename", + "new_name", new_name, + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", - NULL, + details, false, UID_INVALID, &m->polkit_registry, @@ -155,11 +168,18 @@ int bus_image_method_clone( if (!image_name_is_valid(new_name)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Image name '%s' is invalid.", new_name); + const char *details[] = { + "image", image->name, + "verb", "clone", + "new_name", new_name, + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", - NULL, + details, false, UID_INVALID, &m->polkit_registry, @@ -207,7 +227,7 @@ int bus_image_method_mark_read_only( Image *image = userdata; Manager *m = image->userdata; - int r, read_only; + int read_only, r; assert(message); @@ -215,11 +235,18 @@ int bus_image_method_mark_read_only( if (r < 0) return r; + const char *details[] = { + "image", image->name, + "verb", "mark_read_only", + "read_only", one_zero(read_only), + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", - NULL, + details, false, UID_INVALID, &m->polkit_registry, @@ -254,11 +281,17 @@ int bus_image_method_set_limit( if (!FILE_SIZE_VALID_OR_INFINITY(limit)) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range"); + const char *details[] = { + "machine", image->name, + "verb", "set_limit", + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-images", - NULL, + details, false, UID_INVALID, &m->polkit_registry, diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 8f11afd65..7baca67f1 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -73,11 +73,17 @@ int bus_machine_method_unregister(sd_bus_message *message, void *userdata, sd_bu assert(message); assert(m); + const char *details[] = { + "machine", m->name, + "verb", "unregister", + NULL + }; + r = bus_verify_polkit_async( message, CAP_KILL, "org.freedesktop.machine1.manage-machines", - NULL, + details, false, UID_INVALID, &m->manager->polkit_registry, @@ -101,11 +107,17 @@ int bus_machine_method_terminate(sd_bus_message *message, void *userdata, sd_bus assert(message); assert(m); + const char *details[] = { + "machine", m->name, + "verb", "terminate", + NULL + }; + r = bus_verify_polkit_async( message, CAP_KILL, "org.freedesktop.machine1.manage-machines", - NULL, + details, false, UID_INVALID, &m->manager->polkit_registry, @@ -147,11 +159,17 @@ int bus_machine_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro if (!SIGNAL_VALID(signo)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo); + const char *details[] = { + "machine", m->name, + "verb", "kill", + NULL + }; + r = bus_verify_polkit_async( message, CAP_KILL, "org.freedesktop.machine1.manage-machines", - NULL, + details, false, UID_INVALID, &m->manager->polkit_registry, @@ -439,11 +457,16 @@ int bus_machine_method_open_pty(sd_bus_message *message, void *userdata, sd_bus_ assert(message); assert(m); + const char *details[] = { + "machine", m->name, + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-open-pty" : "org.freedesktop.machine1.open-pty", - NULL, + details, false, UID_INVALID, &m->manager->polkit_registry, @@ -526,11 +549,17 @@ int bus_machine_method_open_login(sd_bus_message *message, void *userdata, sd_bu assert(message); assert(m); + const char *details[] = { + "machine", m->name, + "verb", "login", + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, m->class == MACHINE_HOST ? "org.freedesktop.machine1.host-login" : "org.freedesktop.machine1.login", - NULL, + details, false, UID_INVALID, &m->manager->polkit_registry, @@ -835,11 +864,19 @@ int bus_machine_method_bind_mount(sd_bus_message *message, void *userdata, sd_bu else if (!path_is_absolute(dest) || !path_is_normalized(dest)) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute and normalized."); + const char *details[] = { + "machine", m->name, + "verb", "bind", + "src", src, + "dest", dest, + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", - NULL, + details, false, UID_INVALID, &m->manager->polkit_registry, @@ -899,11 +936,19 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro else if (!path_is_absolute(dest)) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Destination path must be absolute."); + const char *details[] = { + "machine", m->name, + "verb", "copy", + "src", src, + "dest", dest, + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", - NULL, + details, false, UID_INVALID, &m->manager->polkit_registry, @@ -1013,11 +1058,17 @@ int bus_machine_method_open_root_directory(sd_bus_message *message, void *userda assert(message); assert(m); + const char *details[] = { + "machine", m->name, + "verb", "open_root_directory", + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", - NULL, + details, false, UID_INVALID, &m->manager->polkit_registry, diff --git a/src/machine/machine.c b/src/machine/machine.c index 80f73da94..b202220b8 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -265,12 +265,10 @@ int machine_load(Machine *m) { "REALTIME", &realtime, "MONOTONIC", &monotonic, "NETIF", &netif); - if (r < 0) { - if (r == -ENOENT) - return 0; - + if (r == -ENOENT) + return 0; + if (r < 0) return log_error_errno(r, "Failed to read %s: %m", m->state_file); - } if (id) sd_id128_from_string(id, &m->id); diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 2f552a938..64233b89c 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -850,11 +850,11 @@ static void print_image_status_info(sd_bus *bus, ImageStatusInfo *i) { i->read_only ? "read-only" : "writable", i->read_only ? ansi_normal() : ""); - if (i->crtime > 0 && i->crtime < USEC_INFINITY) + if (timestamp_is_set(i->crtime)) printf("\t Created: %s; %s\n", FORMAT_TIMESTAMP(i->crtime), FORMAT_TIMESTAMP_RELATIVE(i->crtime)); - if (i->mtime > 0 && i->mtime < USEC_INFINITY) + if (timestamp_is_set(i->mtime)) printf("\tModified: %s; %s\n", FORMAT_TIMESTAMP(i->mtime), FORMAT_TIMESTAMP_RELATIVE(i->mtime)); diff --git a/src/machine/machined-dbus.c b/src/machine/machined-dbus.c index 342b18a8d..ee9ad9925 100644 --- a/src/machine/machined-dbus.c +++ b/src/machine/machined-dbus.c @@ -714,11 +714,17 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err else return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown mode '%s'.", mm); + const char *details[] = { + "verb", "clean_pool", + "mode", mm, + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", - NULL, + details, false, UID_INVALID, &m->polkit_registry, @@ -844,11 +850,16 @@ static int method_set_pool_limit(sd_bus_message *message, void *userdata, sd_bus if (!FILE_SIZE_VALID_OR_INFINITY(limit)) return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "New limit out of range"); + const char *details[] = { + "verb", "set_pool_limit", + NULL + }; + r = bus_verify_polkit_async( message, CAP_SYS_ADMIN, "org.freedesktop.machine1.manage-machines", - NULL, + details, false, UID_INVALID, &m->polkit_registry, diff --git a/src/machine/machined.c b/src/machine/machined.c index a433afb1a..9ecba8720 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -127,7 +127,7 @@ static int manager_add_host_machine(Manager *m) { t->root_directory = TAKE_PTR(rd); t->unit = TAKE_PTR(unit); - dual_timestamp_from_boottime_or_monotonic(&t->timestamp, 0); + dual_timestamp_from_boottime(&t->timestamp, 0); m->host_machine = t; diff --git a/src/machine/meson.build b/src/machine/meson.build index 4357491ee..629776511 100644 --- a/src/machine/meson.build +++ b/src/machine/meson.build @@ -37,7 +37,7 @@ if conf.get('ENABLE_MACHINED') == 1 endif tests += [ - [['src/machine/test-machine-tables.c'], + [files('test-machine-tables.c'), [libmachine_core, libshared], [threads]], diff --git a/src/modules-load/modules-load.c b/src/modules-load/modules-load.c index eda847cb4..9e20d5938 100644 --- a/src/modules-load/modules-load.c +++ b/src/modules-load/modules-load.c @@ -194,7 +194,6 @@ static int run(int argc, char *argv[]) { } else { _cleanup_strv_free_ char **files = NULL; - char **fn, **i; STRV_FOREACH(i, arg_proc_cmdline_modules) { k = module_load_and_warn(ctx, *i, true); diff --git a/src/network/generator/network-generator.c b/src/network/generator/network-generator.c index c081ec673..a1b551133 100644 --- a/src/network/generator/network-generator.c +++ b/src/network/generator/network-generator.c @@ -47,7 +47,7 @@ static const char * const dracut_dhcp_type_table[_DHCP_TYPE_MAX] = { [DHCP_TYPE_OFF] = "off", [DHCP_TYPE_ON] = "on", [DHCP_TYPE_ANY] = "any", - [DHCP_TYPE_DHCP] = "dhcp", + [DHCP_TYPE_DHCP4] = "dhcp", [DHCP_TYPE_DHCP6] = "dhcp6", [DHCP_TYPE_AUTO6] = "auto6", [DHCP_TYPE_EITHER6] = "either6", @@ -62,7 +62,7 @@ static const char * const networkd_dhcp_type_table[_DHCP_TYPE_MAX] = { [DHCP_TYPE_OFF] = "no", [DHCP_TYPE_ON] = "yes", [DHCP_TYPE_ANY] = "yes", - [DHCP_TYPE_DHCP] = "ipv4", + [DHCP_TYPE_DHCP4] = "ipv4", [DHCP_TYPE_DHCP6] = "ipv6", [DHCP_TYPE_AUTO6] = "no", /* TODO: enable other setting? */ [DHCP_TYPE_EITHER6] = "ipv6", /* TODO: enable other setting? */ @@ -1033,7 +1033,6 @@ int parse_cmdline_item(const char *key, const char *value, void *data) { int context_merge_networks(Context *context) { Network *all, *network; - Route *route; int r; assert(context); @@ -1129,10 +1128,7 @@ static int route_dump(Route *route, FILE *f) { } void network_dump(Network *network, FILE *f) { - Address *address; - Route *route; const char *dhcp; - char **dns; assert(network); assert(f); diff --git a/src/network/generator/network-generator.h b/src/network/generator/network-generator.h index dd0a58738..0e0da2a57 100644 --- a/src/network/generator/network-generator.h +++ b/src/network/generator/network-generator.h @@ -13,7 +13,7 @@ typedef enum DHCPType { DHCP_TYPE_OFF, DHCP_TYPE_ON, DHCP_TYPE_ANY, - DHCP_TYPE_DHCP, + DHCP_TYPE_DHCP4, DHCP_TYPE_DHCP6, DHCP_TYPE_AUTO6, DHCP_TYPE_EITHER6, diff --git a/src/network/meson.build b/src/network/meson.build index 48d185195..6e50b9525 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -11,16 +11,26 @@ sources = files( 'netdev/bridge.h', 'netdev/dummy.c', 'netdev/dummy.h', + 'netdev/fou-tunnel.c', + 'netdev/fou-tunnel.h', 'netdev/ifb.c', 'netdev/ifb.h', 'netdev/ipoib.c', 'netdev/ipoib.h', 'netdev/ipvlan.c', 'netdev/ipvlan.h', + 'netdev/l2tp-tunnel.c', + 'netdev/l2tp-tunnel.h', + 'netdev/macsec.c', + 'netdev/macsec.h', 'netdev/macvlan.c', 'netdev/macvlan.h', + 'netdev/netdev-util.c', + 'netdev/netdev-util.h', 'netdev/netdev.c', 'netdev/netdev.h', + 'netdev/netdevsim.c', + 'netdev/netdevsim.h', 'netdev/nlmon.c', 'netdev/nlmon.h', 'netdev/tunnel.c', @@ -43,14 +53,8 @@ sources = files( 'netdev/vxcan.h', 'netdev/wireguard.c', 'netdev/wireguard.h', - 'netdev/netdevsim.c', - 'netdev/netdevsim.h', - 'netdev/fou-tunnel.c', - 'netdev/fou-tunnel.h', - 'netdev/l2tp-tunnel.c', - 'netdev/l2tp-tunnel.h', - 'netdev/macsec.c', - 'netdev/macsec.h', + 'netdev/wlan.c', + 'netdev/wlan.h', 'netdev/xfrm.c', 'netdev/xfrm.h', 'networkd-address-generation.c', @@ -91,6 +95,8 @@ sources = files( 'networkd-ipv4ll.h', 'networkd-ipv6-proxy-ndp.c', 'networkd-ipv6-proxy-ndp.h', + 'networkd-ipv6ll.c', + 'networkd-ipv6ll.h', 'networkd-json.c', 'networkd-json.h', 'networkd-link-bus.c', @@ -139,6 +145,8 @@ sources = files( 'networkd-util.h', 'networkd-wifi.c', 'networkd-wifi.h', + 'networkd-wiphy.c', + 'networkd-wiphy.h', 'tc/cake.c', 'tc/cake.h', 'tc/codel.c', @@ -261,14 +269,14 @@ if conf.get('ENABLE_NETWORKD') == 1 endif fuzzers += [ - [['src/network/fuzz-netdev-parser.c'], + [files('fuzz-netdev-parser.c'), [libnetworkd_core, libsystemd_network, networkd_link_with], [threads], network_includes], - [['src/network/fuzz-network-parser.c'], + [files('fuzz-network-parser.c'), [libnetworkd_core, libsystemd_network, networkd_link_with], @@ -277,37 +285,37 @@ fuzzers += [ ] tests += [ - [['src/network/test-networkd-address.c'], + [files('test-networkd-address.c'), [libnetworkd_core, libsystemd_network], [], network_includes], - [['src/network/test-networkd-conf.c'], + [files('test-networkd-conf.c'), [libnetworkd_core, libsystemd_network], [], network_includes], - [['src/network/test-networkd-util.c'], + [files('test-networkd-util.c'), [libnetworkd_core, libsystemd_network], [], network_includes], - [['src/network/test-network.c'], + [files('test-network.c'), [libnetworkd_core, libsystemd_network], [threads], network_includes], - [['src/network/test-network-tables.c'], + [files('test-network-tables.c'), [libnetworkd_core, libsystemd_network], [threads], network_includes], - [['src/network/generator/test-network-generator.c', - 'src/network/generator/network-generator.c', - 'src/network/generator/network-generator.h']], + [files('generator/test-network-generator.c', + 'generator/network-generator.c', + 'generator/network-generator.h')], ] diff --git a/src/network/netdev/bareudp.c b/src/network/netdev/bareudp.c index 8ff0eb136..24d3afb87 100644 --- a/src/network/netdev/bareudp.c +++ b/src/network/netdev/bareudp.c @@ -33,11 +33,11 @@ static int netdev_bare_udp_fill_message_create(NetDev *netdev, Link *link, sd_ne r = sd_netlink_message_append_u16(m, IFLA_BAREUDP_ETHERTYPE, htobe16(u->iftype)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BAREUDP_ETHERTYPE attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, IFLA_BAREUDP_PORT, htobe16(u->dest_port)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BAREUDP_PORT attribute: %m"); + return r; return 0; } diff --git a/src/network/netdev/batadv.c b/src/network/netdev/batadv.c index 15f3aee3a..7e9761965 100644 --- a/src/network/netdev/batadv.c +++ b/src/network/netdev/batadv.c @@ -114,64 +114,73 @@ static int netdev_batman_set_handler(sd_netlink *rtnl, sd_netlink_message *m, Ne return 1; } -static int netdev_batadv_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) { +static int netdev_batadv_post_create_message(NetDev *netdev, sd_netlink_message *message) { BatmanAdvanced *b; int r; - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; - assert(netdev); - - b = BATADV(netdev); - assert(b); - - r = sd_genl_message_new(netdev->manager->genl, BATADV_NL_NAME, BATADV_CMD_SET_MESH, &message); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m"); + assert_se(b = BATADV(netdev)); r = sd_netlink_message_append_u32(message, BATADV_ATTR_MESH_IFINDEX, netdev->ifindex); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to set ifindex: %m"); + return r; r = sd_netlink_message_append_u8(message, BATADV_ATTR_GW_MODE, b->gateway_mode); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to set gateway_mode: %m"); + return r; r = sd_netlink_message_append_u8(message, BATADV_ATTR_AGGREGATED_OGMS_ENABLED, b->aggregation); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to set aggregation: %m"); + return r; r = sd_netlink_message_append_u8(message, BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED, b->bridge_loop_avoidance); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to set bridge_loop_avoidance: %m"); + return r; r = sd_netlink_message_append_u8(message, BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED, b->distributed_arp_table); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to set distributed_arp_table: %m"); + return r; r = sd_netlink_message_append_u8(message, BATADV_ATTR_FRAGMENTATION_ENABLED, b->fragmentation); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to set fragmentation: %m"); + return r; r = sd_netlink_message_append_u8(message, BATADV_ATTR_HOP_PENALTY, b->hop_penalty); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to set hop_penalty: %m"); + return r; r = sd_netlink_message_append_u32(message, BATADV_ATTR_ORIG_INTERVAL, DIV_ROUND_UP(b->originator_interval, USEC_PER_MSEC)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to set orig_interval: %m"); + return r; r = sd_netlink_message_append_u32(message, BATADV_ATTR_GW_BANDWIDTH_DOWN, b->gateway_bandwidth_down); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to set gateway_bandwidth_down: %m"); + return r; r = sd_netlink_message_append_u32(message, BATADV_ATTR_GW_BANDWIDTH_UP, b->gateway_bandwidth_up); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to set gateway_bandwidth_up: %m"); + return r; + + return 0; +} + +static int netdev_batadv_post_create(NetDev *netdev, Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; + int r; + + assert(netdev); + + r = sd_genl_message_new(netdev->manager->genl, BATADV_NL_NAME, BATADV_CMD_SET_MESH, &message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not allocate netlink message: %m"); + + r = netdev_batadv_post_create_message(netdev, message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not create netlink message: %m"); r = netlink_call_async(netdev->manager->genl, NULL, message, netdev_batman_set_handler, netdev_destroy_callback, netdev); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not send batman device message: %m"); + return log_netdev_error_errno(netdev, r, "Could not send netlink message: %m"); netdev_ref(netdev); @@ -190,9 +199,9 @@ static int netdev_batadv_fill_message_create(NetDev *netdev, Link *link, sd_netl r = sd_netlink_message_append_string(m, IFLA_BATADV_ALGO_NAME, batadv_routing_algorithm_kernel_to_string(b->routing_algorithm)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BATADV_ALGO_NAME attribute: %m"); + return r; - return r; + return 0; } const NetDevVTable batadv_vtable = { @@ -201,7 +210,7 @@ const NetDevVTable batadv_vtable = { .sections = NETDEV_COMMON_SECTIONS "BatmanAdvanced\0", .fill_message_create = netdev_batadv_fill_message_create, .post_create = netdev_batadv_post_create, - .create_type = NETDEV_CREATE_MASTER, + .create_type = NETDEV_CREATE_INDEPENDENT, .iftype = ARPHRD_ETHER, .generate_mac = true, }; diff --git a/src/network/netdev/bond.c b/src/network/netdev/bond.c index 5d94aa1d6..601bff0a9 100644 --- a/src/network/netdev/bond.c +++ b/src/network/netdev/bond.c @@ -71,50 +71,50 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlin if (b->mode != _NETDEV_BOND_MODE_INVALID) { r = sd_netlink_message_append_u8(m, IFLA_BOND_MODE, b->mode); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_MODE attribute: %m"); + return r; } if (b->xmit_hash_policy != _NETDEV_BOND_XMIT_HASH_POLICY_INVALID) { r = sd_netlink_message_append_u8(m, IFLA_BOND_XMIT_HASH_POLICY, b->xmit_hash_policy); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_XMIT_HASH_POLICY attribute: %m"); + return r; } if (b->lacp_rate != _NETDEV_BOND_LACP_RATE_INVALID && b->mode == NETDEV_BOND_MODE_802_3AD) { r = sd_netlink_message_append_u8(m, IFLA_BOND_AD_LACP_RATE, b->lacp_rate); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_LACP_RATE attribute: %m"); + return r; } if (b->miimon != 0) { r = sd_netlink_message_append_u32(m, IFLA_BOND_MIIMON, b->miimon / USEC_PER_MSEC); if (r < 0) - log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_BOND_MIIMON attribute: %m"); + return r; } if (b->downdelay != 0) { r = sd_netlink_message_append_u32(m, IFLA_BOND_DOWNDELAY, b->downdelay / USEC_PER_MSEC); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_DOWNDELAY attribute: %m"); + return r; } if (b->updelay != 0) { r = sd_netlink_message_append_u32(m, IFLA_BOND_UPDELAY, b->updelay / USEC_PER_MSEC); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_UPDELAY attribute: %m"); + return r; } if (b->arp_interval != 0) { r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_INTERVAL, b->arp_interval / USEC_PER_MSEC); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_INTERVAL attribute: %m"); + return r; if (b->lp_interval >= LEARNING_PACKETS_INTERVAL_MIN_SEC && b->lp_interval <= LEARNING_PACKETS_INTERVAL_MAX_SEC) { r = sd_netlink_message_append_u32(m, IFLA_BOND_LP_INTERVAL, b->lp_interval / USEC_PER_SEC); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_LP_INTERVAL attribute: %m"); + return r; } } @@ -122,85 +122,85 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlin b->mode == NETDEV_BOND_MODE_802_3AD) { r = sd_netlink_message_append_u8(m, IFLA_BOND_AD_SELECT, b->ad_select); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_SELECT attribute: %m"); + return r; } if (b->fail_over_mac != _NETDEV_BOND_FAIL_OVER_MAC_INVALID && b->mode == NETDEV_BOND_MODE_ACTIVE_BACKUP) { r = sd_netlink_message_append_u8(m, IFLA_BOND_FAIL_OVER_MAC, b->fail_over_mac); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_FAIL_OVER_MAC attribute: %m"); + return r; } if (b->arp_validate != _NETDEV_BOND_ARP_VALIDATE_INVALID) { r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_VALIDATE, b->arp_validate); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_VALIDATE attribute: %m"); + return r; } if (b->arp_all_targets != _NETDEV_BOND_ARP_ALL_TARGETS_INVALID) { r = sd_netlink_message_append_u32(m, IFLA_BOND_ARP_ALL_TARGETS, b->arp_all_targets); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_ALL_TARGETS attribute: %m"); + return r; } if (b->primary_reselect != _NETDEV_BOND_PRIMARY_RESELECT_INVALID) { r = sd_netlink_message_append_u8(m, IFLA_BOND_PRIMARY_RESELECT, b->primary_reselect); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_PRIMARY_RESELECT attribute: %m"); + return r; } if (b->resend_igmp <= RESEND_IGMP_MAX) { r = sd_netlink_message_append_u32(m, IFLA_BOND_RESEND_IGMP, b->resend_igmp); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_RESEND_IGMP attribute: %m"); + return r; } if (b->packets_per_slave <= PACKETS_PER_SLAVE_MAX && b->mode == NETDEV_BOND_MODE_BALANCE_RR) { r = sd_netlink_message_append_u32(m, IFLA_BOND_PACKETS_PER_SLAVE, b->packets_per_slave); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_PACKETS_PER_SLAVE attribute: %m"); + return r; } if (b->num_grat_arp <= GRATUITOUS_ARP_MAX) { r = sd_netlink_message_append_u8(m, IFLA_BOND_NUM_PEER_NOTIF, b->num_grat_arp); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_NUM_PEER_NOTIF attribute: %m"); + return r; } if (b->min_links != 0) { r = sd_netlink_message_append_u32(m, IFLA_BOND_MIN_LINKS, b->min_links); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_MIN_LINKS attribute: %m"); + return r; } if (b->ad_actor_sys_prio != 0) { r = sd_netlink_message_append_u16(m, IFLA_BOND_AD_ACTOR_SYS_PRIO, b->ad_actor_sys_prio); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_ACTOR_SYS_PRIO attribute: %m"); + return r; } if (b->ad_user_port_key != 0) { r = sd_netlink_message_append_u16(m, IFLA_BOND_AD_USER_PORT_KEY, b->ad_user_port_key); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_USER_PORT_KEY attribute: %m"); + return r; } if (!ether_addr_is_null(&b->ad_actor_system)) { r = sd_netlink_message_append_ether_addr(m, IFLA_BOND_AD_ACTOR_SYSTEM, &b->ad_actor_system); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_ACTOR_SYSTEM attribute: %m"); + return r; } r = sd_netlink_message_append_u8(m, IFLA_BOND_ALL_SLAVES_ACTIVE, b->all_slaves_active); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ALL_SLAVES_ACTIVE attribute: %m"); + return r; if (b->tlb_dynamic_lb >= 0) { r = sd_netlink_message_append_u8(m, IFLA_BOND_TLB_DYNAMIC_LB, b->tlb_dynamic_lb); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_TLB_DYNAMIC_LB attribute: %m"); + return r; } if (b->arp_interval > 0 && !ordered_set_isempty(b->arp_ip_targets)) { @@ -209,17 +209,17 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlin r = sd_netlink_message_open_container(m, IFLA_BOND_ARP_IP_TARGET); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not open contaniner IFLA_BOND_ARP_IP_TARGET : %m"); + return r; ORDERED_SET_FOREACH(val, b->arp_ip_targets) { r = sd_netlink_message_append_u32(m, n++, PTR_TO_UINT32(val)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_ALL_TARGETS attribute: %m"); + return r; } r = sd_netlink_message_close_container(m); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not close contaniner IFLA_BOND_ARP_IP_TARGET : %m"); + return r; } return 0; @@ -451,7 +451,7 @@ const NetDevVTable bond_vtable = { .done = bond_done, .sections = NETDEV_COMMON_SECTIONS "Bond\0", .fill_message_create = netdev_bond_fill_message_create, - .create_type = NETDEV_CREATE_MASTER, + .create_type = NETDEV_CREATE_INDEPENDENT, .iftype = ARPHRD_ETHER, .generate_mac = true, }; diff --git a/src/network/netdev/bridge.c b/src/network/netdev/bridge.c index b974f2ae0..b65c3b49f 100644 --- a/src/network/netdev/bridge.c +++ b/src/network/netdev/bridge.c @@ -45,124 +45,132 @@ static int netdev_bridge_set_handler(sd_netlink *rtnl, sd_netlink_message *m, Ne return 1; } -static int netdev_bridge_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int netdev_bridge_post_create_message(NetDev *netdev, sd_netlink_message *req) { Bridge *b; int r; - assert(netdev); - - b = BRIDGE(netdev); - - assert(b); - - r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, netdev->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not allocate RTM_SETLINK message: %m"); - - r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK); - if (r < 0) - return log_link_error_errno(link, r, "Could not set netlink flags: %m"); + assert_se(b = BRIDGE(netdev)); r = sd_netlink_message_open_container(req, IFLA_LINKINFO); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); + return r; r = sd_netlink_message_open_container_union(req, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); + return r; /* convert to jiffes */ if (b->forward_delay != USEC_INFINITY) { r = sd_netlink_message_append_u32(req, IFLA_BR_FORWARD_DELAY, usec_to_jiffies(b->forward_delay)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_FORWARD_DELAY attribute: %m"); + return r; } if (b->hello_time > 0) { r = sd_netlink_message_append_u32(req, IFLA_BR_HELLO_TIME, usec_to_jiffies(b->hello_time)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_HELLO_TIME attribute: %m"); + return r; } if (b->max_age > 0) { r = sd_netlink_message_append_u32(req, IFLA_BR_MAX_AGE, usec_to_jiffies(b->max_age)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MAX_AGE attribute: %m"); + return r; } if (b->ageing_time != USEC_INFINITY) { r = sd_netlink_message_append_u32(req, IFLA_BR_AGEING_TIME, usec_to_jiffies(b->ageing_time)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_AGEING_TIME attribute: %m"); + return r; } if (b->priority > 0) { r = sd_netlink_message_append_u16(req, IFLA_BR_PRIORITY, b->priority); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_PRIORITY attribute: %m"); + return r; } if (b->group_fwd_mask > 0) { r = sd_netlink_message_append_u16(req, IFLA_BR_GROUP_FWD_MASK, b->group_fwd_mask); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_GROUP_FWD_MASK attribute: %m"); + return r; } if (b->default_pvid != VLANID_INVALID) { r = sd_netlink_message_append_u16(req, IFLA_BR_VLAN_DEFAULT_PVID, b->default_pvid); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_DEFAULT_PVID attribute: %m"); + return r; } if (b->mcast_querier >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_QUERIER, b->mcast_querier); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_QUERIER attribute: %m"); + return r; } if (b->mcast_snooping >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_SNOOPING, b->mcast_snooping); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_SNOOPING attribute: %m"); + return r; } if (b->vlan_filtering >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BR_VLAN_FILTERING, b->vlan_filtering); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_FILTERING attribute: %m"); + return r; } if (b->vlan_protocol >= 0) { r = sd_netlink_message_append_u16(req, IFLA_BR_VLAN_PROTOCOL, htobe16(b->vlan_protocol)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_VLAN_PROTOCOL attribute: %m"); + return r; } if (b->stp >= 0) { r = sd_netlink_message_append_u32(req, IFLA_BR_STP_STATE, b->stp); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_STP_STATE attribute: %m"); + return r; } if (b->igmp_version > 0) { r = sd_netlink_message_append_u8(req, IFLA_BR_MCAST_IGMP_VERSION, b->igmp_version); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BR_MCAST_IGMP_VERSION attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); + return r; r = sd_netlink_message_close_container(req); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); + return r; + + return 0; +} + +static int netdev_bridge_post_create(NetDev *netdev, Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(netdev); + + r = sd_rtnl_message_new_link(netdev->manager->rtnl, &req, RTM_NEWLINK, netdev->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not allocate netlink message: %m"); + + r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK); + if (r < 0) + return log_link_error_errno(link, r, "Could not set netlink message flags: %m"); + + r = netdev_bridge_post_create_message(netdev, req); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not create netlink message: %m"); r = netlink_call_async(netdev->manager->rtnl, NULL, req, netdev_bridge_set_handler, netdev_destroy_callback, netdev); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); + return log_netdev_error_errno(netdev, r, "Could not send netlink message: %m"); netdev_ref(netdev); @@ -276,7 +284,7 @@ const NetDevVTable bridge_vtable = { .init = bridge_init, .sections = NETDEV_COMMON_SECTIONS "Bridge\0", .post_create = netdev_bridge_post_create, - .create_type = NETDEV_CREATE_MASTER, + .create_type = NETDEV_CREATE_INDEPENDENT, .iftype = ARPHRD_ETHER, .generate_mac = true, }; diff --git a/src/network/netdev/fou-tunnel.c b/src/network/netdev/fou-tunnel.c index bc4c108a2..cd8f2f808 100644 --- a/src/network/netdev/fou-tunnel.c +++ b/src/network/netdev/fou-tunnel.c @@ -24,30 +24,21 @@ DEFINE_STRING_TABLE_LOOKUP(fou_encap_type, FooOverUDPEncapType); DEFINE_CONFIG_PARSE_ENUM(config_parse_fou_encap_type, fou_encap_type, FooOverUDPEncapType, "Failed to parse Encapsulation="); -static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **ret) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; +static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message *m) { FouTunnel *t; uint8_t encap_type; int r; - assert(netdev); - - t = FOU(netdev); - - assert(t); - - r = sd_genl_message_new(netdev->manager->genl, FOU_GENL_NAME, FOU_CMD_ADD, &m); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m"); + assert_se(t = FOU(netdev)); r = sd_netlink_message_append_u16(m, FOU_ATTR_PORT, htobe16(t->port)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PORT attribute: %m"); + return r; if (IN_SET(t->peer_family, AF_INET, AF_INET6)) { r = sd_netlink_message_append_u16(m, FOU_ATTR_PEER_PORT, htobe16(t->peer_port)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_PORT attribute: %m"); + return r; } switch (t->fou_encap_type) { @@ -63,36 +54,53 @@ static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **r r = sd_netlink_message_append_u8(m, FOU_ATTR_TYPE, encap_type); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_TYPE attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, FOU_ATTR_AF, AF_INET); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_AF attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, FOU_ATTR_IPPROTO, t->fou_protocol); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_IPPROTO attribute: %m"); + return r; if (t->local_family == AF_INET) { r = sd_netlink_message_append_in_addr(m, FOU_ATTR_LOCAL_V4, &t->local.in); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_LOCAL_V4 attribute: %m"); + return r; } else if (t->local_family == AF_INET6) { r = sd_netlink_message_append_in6_addr(m, FOU_ATTR_LOCAL_V6, &t->local.in6); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_LOCAL_V6 attribute: %m"); + return r; } if (t->peer_family == AF_INET) { r = sd_netlink_message_append_in_addr(m, FOU_ATTR_PEER_V4, &t->peer.in); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_V4 attribute: %m"); + return r; } else if (t->peer_family == AF_INET6){ r = sd_netlink_message_append_in6_addr(m, FOU_ATTR_PEER_V6, &t->peer.in6); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_V6 attribute: %m"); + return r; } + return 0; +} + +static int netdev_create_fou_tunnel_message(NetDev *netdev, sd_netlink_message **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert(netdev); + + r = sd_genl_message_new(netdev->manager->genl, FOU_GENL_NAME, FOU_CMD_ADD, &m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not allocate netlink message: %m"); + + r = netdev_fill_fou_tunnel_message(netdev, m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not create netlink message: %m"); + *ret = TAKE_PTR(m); return 0; } @@ -124,7 +132,7 @@ static int netdev_fou_tunnel_create(NetDev *netdev) { assert(netdev); assert(FOU(netdev)); - r = netdev_fill_fou_tunnel_message(netdev, &m); + r = netdev_create_fou_tunnel_message(netdev, &m); if (r < 0) return r; diff --git a/src/network/netdev/geneve.c b/src/network/netdev/geneve.c index 224c17e97..777a32d75 100644 --- a/src/network/netdev/geneve.c +++ b/src/network/netdev/geneve.c @@ -39,7 +39,7 @@ static int netdev_geneve_fill_message_create(NetDev *netdev, Link *link, sd_netl if (v->id <= GENEVE_VID_MAX) { r = sd_netlink_message_append_u32(m, IFLA_GENEVE_ID, v->id); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_ID attribute: %m"); + return r; } if (in_addr_is_set(v->remote_family, &v->remote)) { @@ -48,51 +48,51 @@ static int netdev_geneve_fill_message_create(NetDev *netdev, Link *link, sd_netl else r = sd_netlink_message_append_in6_addr(m, IFLA_GENEVE_REMOTE6, &v->remote.in6); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_REMOTE/IFLA_GENEVE_REMOTE6 attribute: %m"); + return r; } if (v->inherit) { r = sd_netlink_message_append_u8(m, IFLA_GENEVE_TTL_INHERIT, 1); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_TTL_INHERIT attribute: %m"); + return r; } else { r = sd_netlink_message_append_u8(m, IFLA_GENEVE_TTL, v->ttl); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_TTL attribute: %m"); + return r; } r = sd_netlink_message_append_u8(m, IFLA_GENEVE_TOS, v->tos); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_TOS attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_GENEVE_UDP_CSUM, v->udpcsum); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_UDP_CSUM attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_TX, v->udp6zerocsumtx); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_UDP_ZERO_CSUM6_TX attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, v->udp6zerocsumrx); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_UDP_ZERO_CSUM6_RX attribute: %m"); + return r; if (v->dest_port != DEFAULT_GENEVE_DESTINATION_PORT) { r = sd_netlink_message_append_u16(m, IFLA_GENEVE_PORT, htobe16(v->dest_port)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_PORT attribute: %m"); + return r; } if (v->flow_label > 0) { r = sd_netlink_message_append_u32(m, IFLA_GENEVE_LABEL, htobe32(v->flow_label)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_LABEL attribute: %m"); + return r; } if (v->geneve_df != _NETDEV_GENEVE_DF_INVALID) { r = sd_netlink_message_append_u8(m, IFLA_GENEVE_DF, v->geneve_df); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_DF attribute: %m"); + return r; } return 0; diff --git a/src/network/netdev/ipoib.c b/src/network/netdev/ipoib.c index e0ff9e8c6..0e27f5f04 100644 --- a/src/network/netdev/ipoib.c +++ b/src/network/netdev/ipoib.c @@ -39,19 +39,19 @@ static int netdev_ipoib_fill_message_create(NetDev *netdev, Link *link, sd_netli if (ipoib->pkey > 0) { r = sd_netlink_message_append_u16(m, IFLA_IPOIB_PKEY, ipoib->pkey); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPOIB_PKEY attribute: %m"); + return r; } if (ipoib->mode >= 0) { r = sd_netlink_message_append_u16(m, IFLA_IPOIB_MODE, ipoib->mode); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPOIB_MODE attribute: %m"); + return r; } if (ipoib->umcast >= 0) { r = sd_netlink_message_append_u16(m, IFLA_IPOIB_UMCAST, ipoib->umcast); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPOIB_UMCAST attribute: %m"); + return r; } return 0; @@ -66,35 +66,35 @@ int ipoib_set_netlink_message(Link *link, sd_netlink_message *m) { r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK); if (r < 0) - return log_link_debug_errno(link, r, "Could not set netlink flags: %m"); + return r; r = sd_netlink_message_open_container(m, IFLA_LINKINFO); if (r < 0) - return log_link_debug_errno(link, r, "Failed to open IFLA_LINKINFO container: %m"); + return r; r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, link->kind); if (r < 0) - return log_link_debug_errno(link, r, "Could not open IFLA_INFO_DATA container: %m"); + return r; if (link->network->ipoib_mode >= 0) { r = sd_netlink_message_append_u16(m, IFLA_IPOIB_MODE, link->network->ipoib_mode); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_IPOIB_MODE attribute: %m"); + return r; } if (link->network->ipoib_umcast >= 0) { r = sd_netlink_message_append_u16(m, IFLA_IPOIB_UMCAST, link->network->ipoib_umcast); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_IPOIB_UMCAST attribute: %m"); + return r; } r = sd_netlink_message_close_container(m); if (r < 0) - return log_link_debug_errno(link, r, "Failed to close IFLA_INFO_DATA container: %m"); + return r; r = sd_netlink_message_close_container(m); if (r < 0) - return log_link_debug_errno(link, r, "Failed to close IFLA_LINKINFO container: %m"); + return r; return 0; } diff --git a/src/network/netdev/ipvlan.c b/src/network/netdev/ipvlan.c index d15766cd7..058eadebd 100644 --- a/src/network/netdev/ipvlan.c +++ b/src/network/netdev/ipvlan.c @@ -31,13 +31,13 @@ static int netdev_ipvlan_fill_message_create(NetDev *netdev, Link *link, sd_netl if (m->mode != _NETDEV_IPVLAN_MODE_INVALID) { r = sd_netlink_message_append_u16(req, IFLA_IPVLAN_MODE, m->mode); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPVLAN_MODE attribute: %m"); + return r; } if (m->flags != _NETDEV_IPVLAN_FLAGS_INVALID) { r = sd_netlink_message_append_u16(req, IFLA_IPVLAN_FLAGS, m->flags); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPVLAN_FLAGS attribute: %m"); + return r; } return 0; @@ -80,16 +80,13 @@ const NetDevVTable ipvtap_vtable = { }; IPVlanMode link_get_ipvlan_mode(Link *link) { - NetDev *netdev; + IPVlan *ipvlan; - if (!streq_ptr(link->kind, "ipvlan")) + assert(link); + + ipvlan = IPVLAN(link->netdev); + if (!ipvlan) return _NETDEV_IPVLAN_MODE_INVALID; - if (netdev_get(link->manager, link->ifname, &netdev) < 0) - return _NETDEV_IPVLAN_MODE_INVALID; - - if (netdev->kind != NETDEV_KIND_IPVLAN) - return _NETDEV_IPVLAN_MODE_INVALID; - - return IPVLAN(netdev)->mode; + return ipvlan->mode; } diff --git a/src/network/netdev/l2tp-tunnel.c b/src/network/netdev/l2tp-tunnel.c index cfe54f600..9724e7760 100644 --- a/src/network/netdev/l2tp-tunnel.c +++ b/src/network/netdev/l2tp-tunnel.c @@ -10,6 +10,7 @@ #include "netlink-util.h" #include "networkd-address.h" #include "networkd-manager.h" +#include "networkd-route-util.h" #include "parse-util.h" #include "socket-util.h" #include "string-table.h" @@ -46,15 +47,15 @@ static L2tpSession* l2tp_session_free(L2tpSession *s) { if (s->tunnel && s->section) ordered_hashmap_remove(s->tunnel->sessions_by_section, s->section); - network_config_section_free(s->section); + config_section_free(s->section); free(s->name); return mfree(s); } -DEFINE_NETWORK_SECTION_FUNCTIONS(L2tpSession, l2tp_session_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(L2tpSession, l2tp_session_free); static int l2tp_session_new_static(L2tpTunnel *t, const char *filename, unsigned section_line, L2tpSession **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(l2tp_session_freep) L2tpSession *s = NULL; int r; @@ -63,7 +64,7 @@ static int l2tp_session_new_static(L2tpTunnel *t, const char *filename, unsigned assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -83,7 +84,7 @@ static int l2tp_session_new_static(L2tpTunnel *t, const char *filename, unsigned .section = TAKE_PTR(n), }; - r = ordered_hashmap_ensure_put(&t->sessions_by_section, &network_config_hash_ops, s->section, s); + r = ordered_hashmap_ensure_put(&t->sessions_by_section, &config_section_hash_ops, s->section, s); if (r < 0) return r; @@ -91,7 +92,7 @@ static int l2tp_session_new_static(L2tpTunnel *t, const char *filename, unsigned return 0; } -static int netdev_l2tp_fill_message_tunnel(NetDev *netdev, union in_addr_union *local_address, sd_netlink_message **ret) { +static int netdev_l2tp_create_message_tunnel(NetDev *netdev, union in_addr_union *local_address, sd_netlink_message **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; uint16_t encap_type; L2tpTunnel *t; @@ -99,28 +100,25 @@ static int netdev_l2tp_fill_message_tunnel(NetDev *netdev, union in_addr_union * assert(netdev); assert(local_address); - - t = L2TP(netdev); - - assert(t); + assert_se(t = L2TP(netdev)); r = sd_genl_message_new(netdev->manager->genl, L2TP_GENL_NAME, L2TP_CMD_TUNNEL_CREATE, &m); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m"); + return r; r = sd_netlink_message_append_u32(m, L2TP_ATTR_CONN_ID, t->tunnel_id); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_CONN_ID attribute: %m"); + return r; r = sd_netlink_message_append_u32(m, L2TP_ATTR_PEER_CONN_ID, t->peer_tunnel_id); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_PEER_CONN_ID attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, L2TP_ATTR_PROTO_VERSION, 3); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_PROTO_VERSION attribute: %m"); + return r; - switch(t->l2tp_encap_type) { + switch (t->l2tp_encap_type) { case NETDEV_L2TP_ENCAPTYPE_IP: encap_type = L2TP_ENCAPTYPE_IP; break; @@ -132,51 +130,51 @@ static int netdev_l2tp_fill_message_tunnel(NetDev *netdev, union in_addr_union * r = sd_netlink_message_append_u16(m, L2TP_ATTR_ENCAP_TYPE, encap_type); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_ENCAP_TYPE attribute: %m"); + return r; if (t->family == AF_INET) { r = sd_netlink_message_append_in_addr(m, L2TP_ATTR_IP_SADDR, &local_address->in); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_IP_SADDR attribute: %m"); + return r; r = sd_netlink_message_append_in_addr(m, L2TP_ATTR_IP_DADDR, &t->remote.in); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_IP_DADDR attribute: %m"); + return r; } else { r = sd_netlink_message_append_in6_addr(m, L2TP_ATTR_IP6_SADDR, &local_address->in6); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_IP6_SADDR attribute: %m"); + return r; r = sd_netlink_message_append_in6_addr(m, L2TP_ATTR_IP6_DADDR, &t->remote.in6); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_IP6_DADDR attribute: %m"); + return r; } if (encap_type == L2TP_ENCAPTYPE_UDP) { r = sd_netlink_message_append_u16(m, L2TP_ATTR_UDP_SPORT, t->l2tp_udp_sport); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_UDP_SPORT, attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, L2TP_ATTR_UDP_DPORT, t->l2tp_udp_dport); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_UDP_DPORT attribute: %m"); + return r; if (t->udp_csum) { r = sd_netlink_message_append_u8(m, L2TP_ATTR_UDP_CSUM, t->udp_csum); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_UDP_CSUM attribute: %m"); + return r; } if (t->udp6_csum_tx) { r = sd_netlink_message_append_flag(m, L2TP_ATTR_UDP_ZERO_CSUM6_TX); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_UDP_ZERO_CSUM6_TX attribute: %m"); + return r; } if (t->udp6_csum_rx) { r = sd_netlink_message_append_flag(m, L2TP_ATTR_UDP_ZERO_CSUM6_RX); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_UDP_ZERO_CSUM6_RX attribute: %m"); + return r; } } @@ -185,7 +183,7 @@ static int netdev_l2tp_fill_message_tunnel(NetDev *netdev, union in_addr_union * return 0; } -static int netdev_l2tp_fill_message_session(NetDev *netdev, L2tpSession *session, sd_netlink_message **ret) { +static int netdev_l2tp_create_message_session(NetDev *netdev, L2tpSession *session, sd_netlink_message **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; uint16_t l2_spec_len; uint8_t l2_spec_type; @@ -197,27 +195,27 @@ static int netdev_l2tp_fill_message_session(NetDev *netdev, L2tpSession *session r = sd_genl_message_new(netdev->manager->genl, L2TP_GENL_NAME, L2TP_CMD_SESSION_CREATE, &m); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m"); + return r; r = sd_netlink_message_append_u32(m, L2TP_ATTR_CONN_ID, session->tunnel->tunnel_id); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_CONN_ID attribute: %m"); + return r; r = sd_netlink_message_append_u32(m, L2TP_ATTR_PEER_CONN_ID, session->tunnel->peer_tunnel_id); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_PEER_CONN_ID attribute: %m"); + return r; r = sd_netlink_message_append_u32(m, L2TP_ATTR_SESSION_ID, session->session_id); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_SESSION_ID attribute: %m"); + return r; r = sd_netlink_message_append_u32(m, L2TP_ATTR_PEER_SESSION_ID, session->peer_session_id); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_PEER_SESSION_ID attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, L2TP_ATTR_PW_TYPE, L2TP_PWTYPE_ETH); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_PW_TYPE attribute: %m"); + return r; switch (session->l2tp_l2spec_type) { case NETDEV_L2TP_L2SPECTYPE_NONE: @@ -233,59 +231,129 @@ static int netdev_l2tp_fill_message_session(NetDev *netdev, L2tpSession *session r = sd_netlink_message_append_u8(m, L2TP_ATTR_L2SPEC_TYPE, l2_spec_type); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_L2SPEC_TYPE attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, L2TP_ATTR_L2SPEC_LEN, l2_spec_len); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_L2SPEC_LEN attribute: %m"); + return r; r = sd_netlink_message_append_string(m, L2TP_ATTR_IFNAME, session->name); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_IFNAME attribute: %m"); + return r; *ret = TAKE_PTR(m); return 0; } -static int l2tp_acquire_local_address_one(L2tpTunnel *t, Address *a, union in_addr_union *ret) { - if (a->family != t->family) - return -EINVAL; - - if (in_addr_is_set(a->family, &a->in_addr_peer)) - return -EINVAL; - - if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_STATIC && - !FLAGS_SET(a->flags, IFA_F_PERMANENT)) - return -EINVAL; - - if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_DYNAMIC && - FLAGS_SET(a->flags, IFA_F_PERMANENT)) - return -EINVAL; - - *ret = a->in_addr; - return 0; -} - -static int l2tp_acquire_local_address(L2tpTunnel *t, Link *link, union in_addr_union *ret) { +static int link_get_l2tp_local_address(Link *link, L2tpTunnel *t, union in_addr_union *ret) { Address *a; - assert(t); assert(link); - assert(ret); - assert(IN_SET(t->family, AF_INET, AF_INET6)); + assert(t); + + SET_FOREACH(a, link->addresses) { + if (!address_is_ready(a)) + continue; + + if (a->family != t->family) + continue; + + if (in_addr_is_set(a->family, &a->in_addr_peer)) + continue; + + if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_STATIC && + !FLAGS_SET(a->flags, IFA_F_PERMANENT)) + continue; + + if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_DYNAMIC && + FLAGS_SET(a->flags, IFA_F_PERMANENT)) + continue; + + if (ret) + *ret = a->in_addr; + } + + return -ENOENT; +} + +static int l2tp_get_local_address(NetDev *netdev, union in_addr_union *ret) { + Link *link = NULL; + L2tpTunnel *t; + Address *a; + int r; + + assert(netdev); + assert(netdev->manager); + assert_se(t = L2TP(netdev)); + + if (t->local_ifname) { + r = link_get_by_name(netdev->manager, t->local_ifname, &link); + if (r < 0) + return r; + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return -EBUSY; + } + + if (netdev->manager->manage_foreign_routes) { + /* First, check if the remote address is accessible. */ + if (link) + r = link_address_is_reachable(link, t->family, &t->remote, &t->local, &a); + else + r = manager_address_is_reachable(netdev->manager, t->family, &t->remote, &t->local, &a); + if (r < 0) + return r; + } if (in_addr_is_set(t->family, &t->local)) { /* local address is explicitly specified. */ - *ret = t->local; + + if (!a) { + if (link) + r = link_get_address(link, t->family, &t->local, 0, &a); + else + r = manager_get_address(netdev->manager, t->family, &t->local, 0, &a); + if (r < 0) + return r; + + if (!address_is_ready(a)) + return -EBUSY; + } + + if (ret) + *ret = a->in_addr; + return 0; } - SET_FOREACH(a, link->addresses) - if (l2tp_acquire_local_address_one(t, a, ret) >= 0) - return 1; + if (a) { + if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_STATIC && + !FLAGS_SET(a->flags, IFA_F_PERMANENT)) + return -EINVAL; - return -ENODATA; + if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_DYNAMIC && + FLAGS_SET(a->flags, IFA_F_PERMANENT)) + return -EINVAL; + + if (ret) + *ret = a->in_addr; + + return 0; + } + + if (link) + return link_get_l2tp_local_address(link, t, ret); + + HASHMAP_FOREACH(link, netdev->manager->links_by_index) { + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + continue; + + if (link_get_l2tp_local_address(link, t, ret) >= 0) + return 0; + } + + return -ENOENT; } static void l2tp_session_destroy_callback(L2tpSession *session) { @@ -321,9 +389,9 @@ static int l2tp_create_session(NetDev *netdev, L2tpSession *session) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *n = NULL; int r; - r = netdev_l2tp_fill_message_session(netdev, session, &n); + r = netdev_l2tp_create_message_session(netdev, session, &n); if (r < 0) - return r; + return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m"); r = netlink_call_async(netdev->manager->genl, NULL, n, l2tp_create_session_handler, l2tp_session_destroy_callback, session); @@ -364,32 +432,29 @@ static int l2tp_create_tunnel_handler(sd_netlink *rtnl, sd_netlink_message *m, N return 1; } -static int l2tp_create_tunnel(NetDev *netdev, Link *link) { +static int l2tp_create_tunnel(NetDev *netdev) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; union in_addr_union local_address; L2tpTunnel *t; int r; assert(netdev); + assert_se(t = L2TP(netdev)); - t = L2TP(netdev); - - assert(t); - - r = l2tp_acquire_local_address(t, link, &local_address); + r = l2tp_get_local_address(netdev, &local_address); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not find local address."); - if (r > 0 && DEBUG_LOGGING) { + if (t->local_address_type >= 0 && DEBUG_LOGGING) { _cleanup_free_ char *str = NULL; (void) in_addr_to_string(t->family, &local_address, &str); log_netdev_debug(netdev, "Local address %s acquired.", strna(str)); } - r = netdev_l2tp_fill_message_tunnel(netdev, &local_address, &m); + r = netdev_l2tp_create_message_tunnel(netdev, &local_address, &m); if (r < 0) - return r; + return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m"); r = netlink_call_async(netdev->manager->genl, NULL, m, l2tp_create_tunnel_handler, netdev_destroy_callback, netdev); @@ -401,7 +466,95 @@ static int l2tp_create_tunnel(NetDev *netdev, Link *link) { return 0; } -int config_parse_l2tp_tunnel_address( +static int netdev_l2tp_is_ready_to_create(NetDev *netdev, Link *link) { + return l2tp_get_local_address(netdev, NULL) >= 0; +} + +int config_parse_l2tp_tunnel_local_address( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_free_ char *addr_or_type = NULL, *ifname = NULL; + L2tpLocalAddressType type; + L2tpTunnel *t = userdata; + const char *p = rvalue; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(t); + + if (isempty(rvalue)) { + t->local_ifname = mfree(t->local_ifname); + t->local_address_type = NETDEV_L2TP_LOCAL_ADDRESS_AUTO; + t->local = IN_ADDR_NULL; + + if (!in_addr_is_set(t->family, &t->remote)) + /* If Remote= is not specified yet, then also clear family. */ + t->family = AF_UNSPEC; + + return 0; + } + + r = extract_first_word(&p, &addr_or_type, "@", 0); + if (r < 0) + return log_oom(); + if (r == 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid L2TP Tunnel address specified in %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + if (!isempty(p)) { + if (!ifname_valid_full(p, IFNAME_VALID_ALTERNATIVE)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid interface name specified in %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + ifname = strdup(p); + if (!ifname) + return log_oom(); + } + + type = l2tp_local_address_type_from_string(rvalue); + if (type >= 0) { + free_and_replace(t->local_ifname, ifname); + t->local_address_type = type; + t->local = IN_ADDR_NULL; + + if (!in_addr_is_set(t->family, &t->remote)) + /* If Remote= is not specified yet, then also clear family. */ + t->family = AF_UNSPEC; + + return 0; + } + + if (t->family == AF_UNSPEC) + r = in_addr_from_string_auto(rvalue, &t->family, &t->local); + else + r = in_addr_from_string(t->family, rvalue, &t->local); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Invalid L2TP Tunnel address specified in %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + free_and_replace(t->local_ifname, ifname); + t->local_address_type = _NETDEV_L2TP_LOCAL_ADDRESS_INVALID; + return 0; +} + +int config_parse_l2tp_tunnel_remote_address( const char *unit, const char *filename, unsigned line, @@ -414,41 +567,30 @@ int config_parse_l2tp_tunnel_address( void *userdata) { L2tpTunnel *t = userdata; - union in_addr_union *addr = data; int r; assert(filename); assert(lvalue); assert(rvalue); - assert(data); + assert(t); - if (streq(lvalue, "Local")) { - L2tpLocalAddressType addr_type; + if (isempty(rvalue)) { + t->remote = IN_ADDR_NULL; - if (isempty(rvalue)) - addr_type = NETDEV_L2TP_LOCAL_ADDRESS_AUTO; - else - addr_type = l2tp_local_address_type_from_string(rvalue); + if (!in_addr_is_set(t->family, &t->local)) + /* If Local= is not specified yet, then also clear family. */ + t->family = AF_UNSPEC; - if (addr_type >= 0) { - if (!in_addr_is_set(t->family, &t->remote)) - /* If Remote= is not specified yet, then also clear family. */ - t->family = AF_UNSPEC; - - t->local = IN_ADDR_NULL; - t->local_address_type = addr_type; - - return 0; - } + return 0; } if (t->family == AF_UNSPEC) - r = in_addr_from_string_auto(rvalue, &t->family, addr); + r = in_addr_from_string_auto(rvalue, &t->family, &t->remote); else - r = in_addr_from_string(t->family, rvalue, addr); + r = in_addr_from_string(t->family, rvalue, &t->remote); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, - "Invalid L2TP Tunnel address specified in %s='%s', ignoring assignment: %m", lvalue, rvalue); + "Invalid L2TP Tunnel address specified in %s=, ignoring assignment: %s", lvalue, rvalue); return 0; } @@ -705,14 +847,16 @@ static void l2tp_tunnel_done(NetDev *netdev) { assert(t); ordered_hashmap_free_with_destructor(t->sessions_by_section, l2tp_session_free); + free(t->local_ifname); } const NetDevVTable l2tptnl_vtable = { .object_size = sizeof(L2tpTunnel), .init = l2tp_tunnel_init, .sections = NETDEV_COMMON_SECTIONS "L2TP\0L2TPSession\0", - .create_after_configured = l2tp_create_tunnel, + .create = l2tp_create_tunnel, .done = l2tp_tunnel_done, - .create_type = NETDEV_CREATE_AFTER_CONFIGURED, + .create_type = NETDEV_CREATE_INDEPENDENT, + .is_ready_to_create = netdev_l2tp_is_ready_to_create, .config_verify = netdev_l2tp_tunnel_verify, }; diff --git a/src/network/netdev/l2tp-tunnel.h b/src/network/netdev/l2tp-tunnel.h index a884d2100..6028b3520 100644 --- a/src/network/netdev/l2tp-tunnel.h +++ b/src/network/netdev/l2tp-tunnel.h @@ -34,7 +34,7 @@ typedef struct L2tpTunnel L2tpTunnel; typedef struct L2tpSession { L2tpTunnel *tunnel; - NetworkConfigSection *section; + ConfigSection *section; char *name; @@ -58,6 +58,7 @@ struct L2tpTunnel { bool udp6_csum_rx; bool udp6_csum_tx; + char *local_ifname; L2tpLocalAddressType local_address_type; union in_addr_union local; union in_addr_union remote; @@ -70,7 +71,8 @@ struct L2tpTunnel { DEFINE_NETDEV_CAST(L2TP, L2tpTunnel); extern const NetDevVTable l2tptnl_vtable; -CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_tunnel_address); +CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_tunnel_local_address); +CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_tunnel_remote_address); CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_tunnel_id); CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_encap_type); CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_session_l2spec); diff --git a/src/network/netdev/macsec.c b/src/network/netdev/macsec.c index f1a566a9c..ac626495b 100644 --- a/src/network/netdev/macsec.c +++ b/src/network/netdev/macsec.c @@ -43,16 +43,16 @@ static ReceiveAssociation* macsec_receive_association_free(ReceiveAssociation *c if (c->macsec && c->section) ordered_hashmap_remove(c->macsec->receive_associations_by_section, c->section); - network_config_section_free(c->section); + config_section_free(c->section); security_association_clear(&c->sa); return mfree(c); } -DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveAssociation, macsec_receive_association_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(ReceiveAssociation, macsec_receive_association_free); static int macsec_receive_association_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveAssociation **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(macsec_receive_association_freep) ReceiveAssociation *c = NULL; int r; @@ -61,7 +61,7 @@ static int macsec_receive_association_new_static(MACsec *s, const char *filename assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -82,7 +82,7 @@ static int macsec_receive_association_new_static(MACsec *s, const char *filename security_association_init(&c->sa); - r = ordered_hashmap_ensure_put(&s->receive_associations_by_section, &network_config_hash_ops, c->section, c); + r = ordered_hashmap_ensure_put(&s->receive_associations_by_section, &config_section_hash_ops, c->section, c); if (r < 0) return r; @@ -103,12 +103,12 @@ static ReceiveChannel* macsec_receive_channel_free(ReceiveChannel *c) { ordered_hashmap_remove(c->macsec->receive_channels_by_section, c->section); } - network_config_section_free(c->section); + config_section_free(c->section); return mfree(c); } -DEFINE_NETWORK_SECTION_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(ReceiveChannel, macsec_receive_channel_free); static int macsec_receive_channel_new(MACsec *s, uint64_t sci, ReceiveChannel **ret) { ReceiveChannel *c; @@ -129,7 +129,7 @@ static int macsec_receive_channel_new(MACsec *s, uint64_t sci, ReceiveChannel ** } static int macsec_receive_channel_new_static(MACsec *s, const char *filename, unsigned section_line, ReceiveChannel **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(macsec_receive_channel_freep) ReceiveChannel *c = NULL; int r; @@ -138,7 +138,7 @@ static int macsec_receive_channel_new_static(MACsec *s, const char *filename, un assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -154,7 +154,7 @@ static int macsec_receive_channel_new_static(MACsec *s, const char *filename, un c->section = TAKE_PTR(n); - r = ordered_hashmap_ensure_put(&s->receive_channels_by_section, &network_config_hash_ops, c->section, c); + r = ordered_hashmap_ensure_put(&s->receive_channels_by_section, &config_section_hash_ops, c->section, c); if (r < 0) return r; @@ -170,16 +170,16 @@ static TransmitAssociation* macsec_transmit_association_free(TransmitAssociation if (a->macsec && a->section) ordered_hashmap_remove(a->macsec->transmit_associations_by_section, a->section); - network_config_section_free(a->section); + config_section_free(a->section); security_association_clear(&a->sa); return mfree(a); } -DEFINE_NETWORK_SECTION_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(TransmitAssociation, macsec_transmit_association_free); static int macsec_transmit_association_new_static(MACsec *s, const char *filename, unsigned section_line, TransmitAssociation **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(macsec_transmit_association_freep) TransmitAssociation *a = NULL; int r; @@ -188,7 +188,7 @@ static int macsec_transmit_association_new_static(MACsec *s, const char *filenam assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -209,7 +209,7 @@ static int macsec_transmit_association_new_static(MACsec *s, const char *filenam security_association_init(&a->sa); - r = ordered_hashmap_ensure_put(&s->transmit_associations_by_section, &network_config_hash_ops, a->section, a); + r = ordered_hashmap_ensure_put(&s->transmit_associations_by_section, &config_section_hash_ops, a->section, a); if (r < 0) return r; @@ -218,7 +218,7 @@ static int macsec_transmit_association_new_static(MACsec *s, const char *filenam return 0; } -static int netdev_macsec_fill_message(NetDev *netdev, int command, sd_netlink_message **ret) { +static int netdev_macsec_create_message(NetDev *netdev, int command, sd_netlink_message **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; @@ -227,11 +227,11 @@ static int netdev_macsec_fill_message(NetDev *netdev, int command, sd_netlink_me r = sd_genl_message_new(netdev->manager->genl, MACSEC_GENL_NAME, command, &m); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m"); + return r; r = sd_netlink_message_append_u32(m, MACSEC_ATTR_IFINDEX, netdev->ifindex); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_IFINDEX attribute: %m"); + return r; *ret = TAKE_PTR(m); @@ -247,15 +247,15 @@ static int netdev_macsec_fill_message_sci(NetDev *netdev, MACsecSCI *sci, sd_net r = sd_netlink_message_open_container(m, MACSEC_ATTR_RXSC_CONFIG); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_RXSC_CONFIG attribute: %m"); + return r; r = sd_netlink_message_append_u64(m, MACSEC_RXSC_ATTR_SCI, sci->as_uint64); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append MACSEC_RXSC_ATTR_SCI attribute: %m"); + return r; r = sd_netlink_message_close_container(m); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_RXSC_CONFIG attribute: %m"); + return r; return 0; } @@ -269,37 +269,37 @@ static int netdev_macsec_fill_message_sa(NetDev *netdev, SecurityAssociation *a, r = sd_netlink_message_open_container(m, MACSEC_ATTR_SA_CONFIG); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_SA_CONFIG attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, MACSEC_SA_ATTR_AN, a->association_number); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_AN attribute: %m"); + return r; if (a->packet_number > 0) { r = sd_netlink_message_append_u32(m, MACSEC_SA_ATTR_PN, a->packet_number); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_PN attribute: %m"); + return r; } if (a->key_len > 0) { r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEYID, a->key_id, MACSEC_KEYID_LEN); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEYID attribute: %m"); + return r; r = sd_netlink_message_append_data(m, MACSEC_SA_ATTR_KEY, a->key, a->key_len); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_KEY attribute: %m"); + return r; } if (a->activate >= 0) { r = sd_netlink_message_append_u8(m, MACSEC_SA_ATTR_ACTIVE, a->activate); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append MACSEC_SA_ATTR_ACTIVE attribute: %m"); + return r; } r = sd_netlink_message_close_container(m); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append MACSEC_ATTR_SA_CONFIG attribute: %m"); + return r; return 0; } @@ -313,8 +313,7 @@ static int macsec_receive_association_handler(sd_netlink *rtnl, sd_netlink_messa r = sd_netlink_message_get_errno(m); if (r == -EEXIST) log_netdev_info(netdev, - "MACsec receive secure association exists, " - "using existing without changing its parameters"); + "MACsec receive secure association exists, using it without changing parameters"); else if (r < 0) { log_netdev_warning_errno(netdev, r, "Failed to add receive secure association: %m"); @@ -335,17 +334,17 @@ static int netdev_macsec_configure_receive_association(NetDev *netdev, ReceiveAs assert(netdev); assert(a); - r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_RXSA, &m); + r = netdev_macsec_create_message(netdev, MACSEC_CMD_ADD_RXSA, &m); if (r < 0) - return r; + return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m"); r = netdev_macsec_fill_message_sa(netdev, &a->sa, m); if (r < 0) - return r; + return log_netdev_error_errno(netdev, r, "Failed to fill netlink message: %m"); r = netdev_macsec_fill_message_sci(netdev, &a->sci, m); if (r < 0) - return r; + return log_netdev_error_errno(netdev, r, "Failed to fill netlink message: %m"); r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_association_handler, netdev_destroy_callback, netdev); @@ -371,8 +370,7 @@ static int macsec_receive_channel_handler(sd_netlink *rtnl, sd_netlink_message * r = sd_netlink_message_get_errno(m); if (r == -EEXIST) log_netdev_debug(netdev, - "MACsec receive channel exists, " - "using existing without changing its parameters"); + "MACsec receive channel exists, using it without changing parameters"); else if (r < 0) { log_netdev_warning_errno(netdev, r, "Failed to add receive secure channel: %m"); @@ -410,13 +408,13 @@ static int netdev_macsec_configure_receive_channel(NetDev *netdev, ReceiveChanne assert(netdev); assert(c); - r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_RXSC, &m); + r = netdev_macsec_create_message(netdev, MACSEC_CMD_ADD_RXSC, &m); if (r < 0) - return r; + return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m"); r = netdev_macsec_fill_message_sci(netdev, &c->sci, m); if (r < 0) - return r; + return log_netdev_error_errno(netdev, r, "Failed to fill netlink message: %m"); r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_receive_channel_handler, receive_channel_destroy_callback, c); @@ -437,8 +435,7 @@ static int macsec_transmit_association_handler(sd_netlink *rtnl, sd_netlink_mess r = sd_netlink_message_get_errno(m); if (r == -EEXIST) log_netdev_info(netdev, - "MACsec transmit secure association exists, " - "using existing without changing its parameters"); + "MACsec transmit secure association exists, using it without changing parameters"); else if (r < 0) { log_netdev_warning_errno(netdev, r, "Failed to add transmit secure association: %m"); @@ -459,13 +456,13 @@ static int netdev_macsec_configure_transmit_association(NetDev *netdev, Transmit assert(netdev); assert(a); - r = netdev_macsec_fill_message(netdev, MACSEC_CMD_ADD_TXSA, &m); + r = netdev_macsec_create_message(netdev, MACSEC_CMD_ADD_TXSA, &m); if (r < 0) - return r; + return log_netdev_error_errno(netdev, r, "Failed to create netlink message: %m"); r = netdev_macsec_fill_message_sa(netdev, &a->sa, m); if (r < 0) - return r; + return log_netdev_error_errno(netdev, r, "Failed to fill netlink message: %m"); r = netlink_call_async(netdev->manager->genl, NULL, m, macsec_transmit_association_handler, netdev_destroy_callback, netdev); @@ -477,7 +474,7 @@ static int netdev_macsec_configure_transmit_association(NetDev *netdev, Transmit return 0; } -static int netdev_macsec_configure(NetDev *netdev, Link *link, sd_netlink_message *m) { +static int netdev_macsec_configure(NetDev *netdev, Link *link) { TransmitAssociation *a; ReceiveChannel *c; MACsec *s; @@ -516,20 +513,20 @@ static int netdev_macsec_fill_message_create(NetDev *netdev, Link *link, sd_netl if (v->port > 0) { r = sd_netlink_message_append_u16(m, IFLA_MACSEC_PORT, v->port); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_PORT attribute: %m"); + return r; } if (v->encrypt >= 0) { r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCRYPT, v->encrypt); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_ENCRYPT attribute: %m"); + return r; } r = sd_netlink_message_append_u8(m, IFLA_MACSEC_ENCODING_SA, v->encoding_an); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACSEC_ENCODING_SA attribute: %m"); + return r; - return r; + return 0; } int config_parse_macsec_port( diff --git a/src/network/netdev/macsec.h b/src/network/netdev/macsec.h index 4d88e4951..17bb1ca3f 100644 --- a/src/network/netdev/macsec.h +++ b/src/network/netdev/macsec.h @@ -39,14 +39,14 @@ typedef struct SecurityAssociation { typedef struct TransmitAssociation { MACsec *macsec; - NetworkConfigSection *section; + ConfigSection *section; SecurityAssociation sa; } TransmitAssociation; typedef struct ReceiveAssociation { MACsec *macsec; - NetworkConfigSection *section; + ConfigSection *section; MACsecSCI sci; SecurityAssociation sa; @@ -54,7 +54,7 @@ typedef struct ReceiveAssociation { typedef struct ReceiveChannel { MACsec *macsec; - NetworkConfigSection *section; + ConfigSection *section; MACsecSCI sci; ReceiveAssociation *rxsa[MACSEC_MAX_ASSOCIATION_NUMBER]; diff --git a/src/network/netdev/macvlan.c b/src/network/netdev/macvlan.c index c41be6e78..aca05f0f7 100644 --- a/src/network/netdev/macvlan.c +++ b/src/network/netdev/macvlan.c @@ -33,40 +33,40 @@ static int netdev_macvlan_fill_message_create(NetDev *netdev, Link *link, sd_net r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_MACADDR_MODE, MACVLAN_MACADDR_SET); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_MACADDR_MODE attribute: %m"); + return r; r = sd_netlink_message_open_container(req, IFLA_MACVLAN_MACADDR_DATA); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not open IFLA_MACVLAN_MACADDR_DATA container: %m"); + return r; SET_FOREACH(mac_addr, m->match_source_mac) { r = sd_netlink_message_append_ether_addr(req, IFLA_MACVLAN_MACADDR, mac_addr); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_MACADDR attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not close IFLA_MACVLAN_MACADDR_DATA container: %m"); + return r; } if (m->mode != _NETDEV_MACVLAN_MODE_INVALID) { r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_MODE, m->mode); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_MODE attribute: %m"); + return r; } /* set the nopromisc flag if Promiscuous= of the link is explicitly set to false */ if (m->mode == NETDEV_MACVLAN_MODE_PASSTHRU && link->network->promiscuous == 0) { r = sd_netlink_message_append_u16(req, IFLA_MACVLAN_FLAGS, MACVLAN_FLAG_NOPROMISC); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_FLAGS attribute: %m"); + return r; } if (m->bc_queue_length != UINT32_MAX) { r = sd_netlink_message_append_u32(req, IFLA_MACVLAN_BC_QUEUE_LEN, m->bc_queue_length); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MACVLAN_BC_QUEUE_LEN attribute: %m"); + return r; } return 0; diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index a948ec2c8..77140be40 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -27,6 +27,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "vxcan.h" #include "vxlan.h" #include "wireguard.h" +#include "wlan.h" #include "xfrm.h" %} struct ConfigPerfItem; @@ -68,8 +69,8 @@ IPVLAN.Mode, config_parse_ipvlan_mode, IPVLAN.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags) IPVTAP.Mode, config_parse_ipvlan_mode, 0, offsetof(IPVlan, mode) IPVTAP.Flags, config_parse_ipvlan_flags, 0, offsetof(IPVlan, flags) -Tunnel.Local, config_parse_tunnel_address, 0, offsetof(Tunnel, local) -Tunnel.Remote, config_parse_tunnel_address, 0, offsetof(Tunnel, remote) +Tunnel.Local, config_parse_tunnel_local_address, 0, 0 +Tunnel.Remote, config_parse_tunnel_remote_address, 0, 0 Tunnel.TOS, config_parse_unsigned, 0, offsetof(Tunnel, tos) Tunnel.TTL, config_parse_unsigned, 0, offsetof(Tunnel, ttl) Tunnel.Key, config_parse_tunnel_key, 0, offsetof(Tunnel, key) @@ -77,9 +78,9 @@ Tunnel.InputKey, config_parse_tunnel_key, Tunnel.OutputKey, config_parse_tunnel_key, 0, offsetof(Tunnel, okey) Tunnel.DiscoverPathMTU, config_parse_bool, 0, offsetof(Tunnel, pmtudisc) Tunnel.Mode, config_parse_ip6tnl_mode, 0, offsetof(Tunnel, ip6tnl_mode) -Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, offsetof(Tunnel, ipv6_flowlabel) +Tunnel.IPv6FlowLabel, config_parse_ipv6_flowlabel, 0, 0 Tunnel.CopyDSCP, config_parse_bool, 0, offsetof(Tunnel, copy_dscp) -Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, offsetof(Tunnel, encap_limit) +Tunnel.EncapsulationLimit, config_parse_encap_limit, 0, 0 Tunnel.Independent, config_parse_bool, 0, offsetof(Tunnel, independent) Tunnel.AssignToLoopback, config_parse_bool, 0, offsetof(Tunnel, assign_to_loopback) Tunnel.AllowLocalRemote, config_parse_tristate, 0, offsetof(Tunnel, allow_localremote) @@ -91,6 +92,7 @@ Tunnel.IPv6RapidDeploymentPrefix, config_parse_6rd_prefix, Tunnel.ERSPANIndex, config_parse_uint32, 0, offsetof(Tunnel, erspan_index) Tunnel.SerializeTunneledPackets, config_parse_tristate, 0, offsetof(Tunnel, gre_erspan_sequence) Tunnel.ISATAP, config_parse_tristate, 0, offsetof(Tunnel, isatap) +Tunnel.External, config_parse_bool, 0, offsetof(Tunnel, external) FooOverUDP.Protocol, config_parse_ip_protocol, 0, offsetof(FouTunnel, fou_protocol) FooOverUDP.Encapsulation, config_parse_fou_encap_type, 0, offsetof(FouTunnel, fou_encap_type) FooOverUDP.Port, config_parse_ip_port, 0, offsetof(FouTunnel, port) @@ -101,8 +103,8 @@ L2TP.TunnelId, config_parse_l2tp_tunnel_id, L2TP.PeerTunnelId, config_parse_l2tp_tunnel_id, 0, offsetof(L2tpTunnel, peer_tunnel_id) L2TP.UDPSourcePort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_sport) L2TP.UDPDestinationPort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_dport) -L2TP.Local, config_parse_l2tp_tunnel_address, 0, offsetof(L2tpTunnel, local) -L2TP.Remote, config_parse_l2tp_tunnel_address, 0, offsetof(L2tpTunnel, remote) +L2TP.Local, config_parse_l2tp_tunnel_local_address, 0, 0 +L2TP.Remote, config_parse_l2tp_tunnel_remote_address, 0, 0 L2TP.EncapsulationType, config_parse_l2tp_encap_type, 0, offsetof(L2tpTunnel, l2tp_encap_type) L2TP.UDPCheckSum, config_parse_bool, 0, offsetof(L2tpTunnel, udp_csum) L2TP.UDP6CheckSumRx, config_parse_bool, 0, offsetof(L2tpTunnel, udp6_csum_rx) @@ -177,14 +179,14 @@ Tun.OneQueue, config_parse_warn_compat, Tun.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) Tun.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) Tun.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr) -Tun.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tun.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Tun.User, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(TunTap, user_name) +Tun.Group, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(TunTap, group_name) Tap.OneQueue, config_parse_warn_compat, DISABLED_LEGACY, 0 Tap.MultiQueue, config_parse_bool, 0, offsetof(TunTap, multi_queue) Tap.PacketInfo, config_parse_bool, 0, offsetof(TunTap, packet_info) Tap.VNetHeader, config_parse_bool, 0, offsetof(TunTap, vnet_hdr) -Tap.User, config_parse_string, 0, offsetof(TunTap, user_name) -Tap.Group, config_parse_string, 0, offsetof(TunTap, group_name) +Tap.User, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(TunTap, user_name) +Tap.Group, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(TunTap, group_name) Bond.Mode, config_parse_bond_mode, 0, offsetof(Bond, mode) Bond.TransmitHashPolicy, config_parse_bond_xmit_hash_policy, 0, offsetof(Bond, xmit_hash_policy) Bond.LACPTransmitRate, config_parse_bond_lacp_rate, 0, offsetof(Bond, lacp_rate) @@ -257,3 +259,6 @@ BatmanAdvanced.RoutingAlgorithm, config_parse_batadv_routing_algorithm, IPoIB.PartitionKey, config_parse_ipoib_pkey, 0, offsetof(IPoIB, pkey) IPoIB.Mode, config_parse_ipoib_mode, 0, offsetof(IPoIB, mode) IPoIB.IgnoreUserspaceMulticastGroups, config_parse_tristate, 0, offsetof(IPoIB, umcast) +WLAN.PhysicalDevice, config_parse_wiphy, 0, 0 +WLAN.Type, config_parse_wlan_iftype, 0, offsetof(WLan, iftype) +WLAN.WDS, config_parse_tristate, 0, offsetof(WLan, wds) diff --git a/src/network/netdev/netdev-util.c b/src/network/netdev/netdev-util.c new file mode 100644 index 000000000..06028855a --- /dev/null +++ b/src/network/netdev/netdev-util.c @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "netdev-util.h" +#include "networkd-address.h" +#include "networkd-link.h" +#include "string-table.h" + +static const char * const netdev_local_address_type_table[_NETDEV_LOCAL_ADDRESS_TYPE_MAX] = { + [NETDEV_LOCAL_ADDRESS_IPV4LL] = "ipv4_link_local", + [NETDEV_LOCAL_ADDRESS_IPV6LL] = "ipv6_link_local", + [NETDEV_LOCAL_ADDRESS_DHCP4] = "dhcp4", + [NETDEV_LOCAL_ADDRESS_DHCP6] = "dhcp6", + [NETDEV_LOCAL_ADDRESS_SLAAC] = "slaac", +}; + +DEFINE_STRING_TABLE_LOOKUP(netdev_local_address_type, NetDevLocalAddressType); + +int link_get_local_address( + Link *link, + NetDevLocalAddressType type, + int family, + int *ret_family, + union in_addr_union *ret_address) { + + Address *a; + + assert(link); + + switch (type) { + case NETDEV_LOCAL_ADDRESS_IPV4LL: + assert(IN_SET(family, AF_UNSPEC, AF_INET)); + family = AF_INET; + break; + case NETDEV_LOCAL_ADDRESS_IPV6LL: + assert(IN_SET(family, AF_UNSPEC, AF_INET6)); + family = AF_INET6; + break; + case NETDEV_LOCAL_ADDRESS_DHCP4: + assert(IN_SET(family, AF_UNSPEC, AF_INET)); + family = AF_INET; + break; + case NETDEV_LOCAL_ADDRESS_DHCP6: + assert(IN_SET(family, AF_UNSPEC, AF_INET6)); + family = AF_INET6; + break; + case NETDEV_LOCAL_ADDRESS_SLAAC: + assert(IN_SET(family, AF_UNSPEC, AF_INET6)); + family = AF_INET6; + break; + default: + assert_not_reached(); + } + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return -EBUSY; + + SET_FOREACH(a, link->addresses) { + if (!address_is_ready(a)) + continue; + + if (a->family != family) + continue; + + if (in_addr_is_set(a->family, &a->in_addr_peer)) + continue; + + switch (type) { + case NETDEV_LOCAL_ADDRESS_IPV4LL: + if (a->source != NETWORK_CONFIG_SOURCE_IPV4LL) + continue; + break; + case NETDEV_LOCAL_ADDRESS_IPV6LL: + if (!in6_addr_is_link_local(&a->in_addr.in6)) + continue; + break; + case NETDEV_LOCAL_ADDRESS_DHCP4: + if (a->source != NETWORK_CONFIG_SOURCE_DHCP4) + continue; + break; + case NETDEV_LOCAL_ADDRESS_DHCP6: + if (a->source != NETWORK_CONFIG_SOURCE_DHCP6) + continue; + break; + case NETDEV_LOCAL_ADDRESS_SLAAC: + if (a->source != NETWORK_CONFIG_SOURCE_NDISC) + continue; + break; + default: + assert_not_reached(); + } + + if (ret_family) + *ret_family = a->family; + if (ret_address) + *ret_address = a->in_addr; + return 1; + } + + return -ENXIO; +} diff --git a/src/network/netdev/netdev-util.h b/src/network/netdev/netdev-util.h new file mode 100644 index 000000000..02b07e367 --- /dev/null +++ b/src/network/netdev/netdev-util.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "in-addr-util.h" +#include "macro.h" + +typedef struct Link Link; + +typedef enum NetDevLocalAddressType { + NETDEV_LOCAL_ADDRESS_IPV4LL, + NETDEV_LOCAL_ADDRESS_IPV6LL, + NETDEV_LOCAL_ADDRESS_DHCP4, + NETDEV_LOCAL_ADDRESS_DHCP6, + NETDEV_LOCAL_ADDRESS_SLAAC, + _NETDEV_LOCAL_ADDRESS_TYPE_MAX, + _NETDEV_LOCAL_ADDRESS_TYPE_INVALID = -EINVAL, +} NetDevLocalAddressType; + +const char *netdev_local_address_type_to_string(NetDevLocalAddressType t) _const_; +NetDevLocalAddressType netdev_local_address_type_from_string(const char *s) _pure_; + +int link_get_local_address( + Link *link, + NetDevLocalAddressType type, + int family, + int *ret_family, + union in_addr_union *ret_address); diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 8e7fe11c1..af5a0a9ff 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -47,6 +47,7 @@ #include "vxcan.h" #include "vxlan.h" #include "wireguard.h" +#include "wlan.h" #include "xfrm.h" const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { @@ -86,6 +87,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_VXCAN] = &vxcan_vtable, [NETDEV_KIND_VXLAN] = &vxlan_vtable, [NETDEV_KIND_WIREGUARD] = &wireguard_vtable, + [NETDEV_KIND_WLAN] = &wlan_vtable, [NETDEV_KIND_XFRM] = &xfrm_vtable, }; @@ -126,6 +128,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { [NETDEV_KIND_VXCAN] = "vxcan", [NETDEV_KIND_VXLAN] = "vxlan", [NETDEV_KIND_WIREGUARD] = "wireguard", + [NETDEV_KIND_WLAN] = "wlan", [NETDEV_KIND_XFRM] = "xfrm", }; @@ -141,7 +144,7 @@ bool netdev_is_managed(NetDev *netdev) { static bool netdev_is_stacked_and_independent(NetDev *netdev) { assert(netdev); - if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED)) + if (netdev_get_create_type(netdev) != NETDEV_CREATE_STACKED) return false; switch (netdev->kind) { @@ -177,7 +180,7 @@ static bool netdev_is_stacked_and_independent(NetDev *netdev) { static bool netdev_is_stacked(NetDev *netdev) { assert(netdev); - if (!IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_STACKED, NETDEV_CREATE_AFTER_CONFIGURED)) + if (netdev_get_create_type(netdev) != NETDEV_CREATE_STACKED) return false; if (netdev_is_stacked_and_independent(netdev)) @@ -250,10 +253,8 @@ int netdev_get(Manager *manager, const char *name, NetDev **ret) { assert(ret); netdev = hashmap_get(manager->netdevs, name); - if (!netdev) { - *ret = NULL; + if (!netdev) return -ENOENT; - } *ret = netdev; @@ -276,7 +277,7 @@ static int netdev_enter_ready(NetDev *netdev) { log_netdev_info(netdev, "netdev ready"); if (NETDEV_VTABLE(netdev)->post_create) - NETDEV_VTABLE(netdev)->post_create(netdev, NULL, NULL); + NETDEV_VTABLE(netdev)->post_create(netdev, NULL); return 0; } @@ -352,37 +353,40 @@ int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *message) { return -EINVAL; } - r = sd_netlink_message_enter_container(message, IFLA_LINKINFO); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not get LINKINFO: %m"); + if (!NETDEV_VTABLE(netdev)->skip_netdev_kind_check) { - r = sd_netlink_message_read_string(message, IFLA_INFO_KIND, &received_kind); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not get KIND: %m"); + r = sd_netlink_message_enter_container(message, IFLA_LINKINFO); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not get LINKINFO: %m"); - r = sd_netlink_message_exit_container(message); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not exit container: %m"); + r = sd_netlink_message_read_string(message, IFLA_INFO_KIND, &received_kind); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not get KIND: %m"); - if (netdev->kind == NETDEV_KIND_TAP) - /* the kernel does not distinguish between tun and tap */ - kind = "tun"; - else { - kind = netdev_kind_to_string(netdev->kind); - if (!kind) { - log_netdev_error(netdev, "Could not get kind"); + r = sd_netlink_message_exit_container(message); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not exit container: %m"); + + if (netdev->kind == NETDEV_KIND_TAP) + /* the kernel does not distinguish between tun and tap */ + kind = "tun"; + else { + kind = netdev_kind_to_string(netdev->kind); + if (!kind) { + log_netdev_error(netdev, "Could not get kind"); + netdev_enter_failed(netdev); + return -EINVAL; + } + } + + if (!streq(kind, received_kind)) { + log_netdev_error(netdev, "Received newlink with wrong KIND %s, expected %s", + received_kind, kind); netdev_enter_failed(netdev); return -EINVAL; } } - if (!streq(kind, received_kind)) { - log_netdev_error(netdev, "Received newlink with wrong KIND %s, expected %s", - received_kind, kind); - netdev_enter_failed(netdev); - return -EINVAL; - } - netdev->ifindex = ifindex; log_netdev_debug(netdev, "netdev has index %d", netdev->ifindex); @@ -478,18 +482,74 @@ finalize: return 0; } -static int netdev_create(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; +static int netdev_create_message(NetDev *netdev, Link *link, sd_netlink_message *m) { + int r; + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, netdev->ifname); + if (r < 0) + return r; + struct hw_addr_data hw_addr; + r = netdev_generate_hw_addr(netdev, link, netdev->ifname, &netdev->hw_addr, &hw_addr); + if (r < 0) + return r; + + if (hw_addr.length > 0) { + log_netdev_debug(netdev, "Using MAC address: %s", HW_ADDR_TO_STR(&hw_addr)); + r = netlink_message_append_hw_addr(m, IFLA_ADDRESS, &hw_addr); + if (r < 0) + return r; + } + + if (netdev->mtu != 0) { + r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu); + if (r < 0) + return r; + } + + if (link) { + r = sd_netlink_message_append_u32(m, IFLA_LINK, link->ifindex); + if (r < 0) + return r; + } + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return r; + + if (NETDEV_VTABLE(netdev)->fill_message_create) { + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind)); + if (r < 0) + return r; + + r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + } else { + r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, netdev_kind_to_string(netdev->kind)); + if (r < 0) + return r; + } + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + + return 0; +} + +static int independent_netdev_create(NetDev *netdev) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert(netdev); - assert(!link || callback); /* create netdev */ if (NETDEV_VTABLE(netdev)->create) { - assert(!link); - r = NETDEV_VTABLE(netdev)->create(netdev); if (r < 0) return r; @@ -500,165 +560,99 @@ static int netdev_create(NetDev *netdev, Link *link, link_netlink_message_handle r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not allocate RTM_NEWLINK message: %m"); + return r; - r = sd_netlink_message_append_string(m, IFLA_IFNAME, netdev->ifname); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IFNAME, attribute: %m"); - - r = netdev_generate_hw_addr(netdev, link, netdev->ifname, &netdev->hw_addr, &hw_addr); + r = netdev_create_message(netdev, NULL, m); if (r < 0) return r; - if (hw_addr.length > 0) { - log_netdev_debug(netdev, "Using MAC address: %s", HW_ADDR_TO_STR(&hw_addr)); - r = netlink_message_append_hw_addr(m, IFLA_ADDRESS, &hw_addr); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m"); - } - - if (netdev->mtu != 0) { - r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MTU attribute: %m"); - } - - if (link) { - r = sd_netlink_message_append_u32(m, IFLA_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINK attribute: %m"); - } - - r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + r = netlink_call_async(netdev->manager->rtnl, NULL, m, netdev_create_handler, + netdev_destroy_callback, netdev); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); + return r; - if (NETDEV_VTABLE(netdev)->fill_message_create) { - r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, netdev_kind_to_string(netdev->kind)); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); - - r = NETDEV_VTABLE(netdev)->fill_message_create(netdev, link, m); - if (r < 0) - return r; - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); - } else { - r = sd_netlink_message_append_string(m, IFLA_INFO_KIND, netdev_kind_to_string(netdev->kind)); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_KIND attribute: %m"); - } - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_LINKINFO attribute: %m"); - - if (link) { - r = netlink_call_async(netdev->manager->rtnl, NULL, m, callback, - link_netlink_destroy_callback, link); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - } else { - r = netlink_call_async(netdev->manager->rtnl, NULL, m, netdev_create_handler, - netdev_destroy_callback, netdev); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not send rtnetlink message: %m"); - - netdev_ref(netdev); - } + netdev_ref(netdev); netdev->state = NETDEV_STATE_CREATING; - log_netdev_debug(netdev, "Creating"); return 0; } -static int netdev_create_after_configured(NetDev *netdev, Link *link) { - assert(netdev); - assert(link); - assert(NETDEV_VTABLE(netdev)->create_after_configured); - - return NETDEV_VTABLE(netdev)->create_after_configured(netdev, link); -} - -int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) { +static int stacked_netdev_create(NetDev *netdev, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert(netdev); assert(netdev->manager); - assert(netdev->manager->rtnl); + assert(link); + assert(req); - switch (netdev_get_create_type(netdev)) { - case NETDEV_CREATE_STACKED: - r = netdev_create(netdev, link, callback); - if (r < 0) - return r; + r = sd_rtnl_message_new_link(netdev->manager->rtnl, &m, RTM_NEWLINK, 0); + if (r < 0) + return r; - break; - case NETDEV_CREATE_AFTER_CONFIGURED: - r = netdev_create_after_configured(netdev, link); - if (r < 0) - return r; - break; - default: - assert_not_reached(); - } + r = netdev_create_message(netdev, link, m); + if (r < 0) + return r; + r = request_call_netlink_async(netdev->manager->rtnl, m, req); + if (r < 0) + return r; + + netdev->state = NETDEV_STATE_CREATING; + log_netdev_debug(netdev, "Creating"); return 0; } -static bool netdev_is_ready_to_create(NetDev *netdev, Link *link) { +static int netdev_is_ready_to_create(NetDev *netdev, Link *link) { assert(netdev); - assert(link); if (netdev->state != NETDEV_STATE_LOADING) return false; - if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) - return false; + if (link) { + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return false; - if (netdev_get_create_type(netdev) == NETDEV_CREATE_AFTER_CONFIGURED && - link->state != LINK_STATE_CONFIGURED) - return false; + if (link->set_link_messages > 0) + return false; - if (link->set_link_messages > 0) - return false; + /* If stacked netdevs are created before the underlying interface being activated, then + * the activation policy for the netdevs are ignored. See issue #22593. */ + if (!link->activated) + return false; + } + + if (NETDEV_VTABLE(netdev)->is_ready_to_create) + return NETDEV_VTABLE(netdev)->is_ready_to_create(netdev, link); return true; } -int request_process_stacked_netdev(Request *req) { +static int stacked_netdev_process_request(Request *req, Link *link, void *userdata) { + NetDev *netdev = ASSERT_PTR(userdata); int r; assert(req); - assert(req->link); - assert(req->type == REQUEST_TYPE_STACKED_NETDEV); - assert(req->netdev); - assert(req->netlink_handler); + assert(link); - if (!netdev_is_ready_to_create(req->netdev, req->link)) - return 0; + r = netdev_is_ready_to_create(netdev, link); + if (r <= 0) + return r; - r = netdev_join(req->netdev, req->link, req->netlink_handler); + r = stacked_netdev_create(netdev, link, req); if (r < 0) - return log_link_error_errno(req->link, r, "Failed to create stacked netdev '%s': %m", req->netdev->ifname); + return log_netdev_warning_errno(netdev, r, "Failed to create netdev: %m"); return 1; } -static int link_create_stacked_netdev_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int create_stacked_netdev_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { int r; assert(m); assert(link); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 0; - r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { log_link_message_warning_errno(link, m, r, "Could not create stacked netdev"); @@ -666,18 +660,6 @@ static int link_create_stacked_netdev_handler_internal(sd_netlink *rtnl, sd_netl return 0; } - return 1; -} - -static int link_create_stacked_netdev_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - assert(link); - assert(link->create_stacked_netdev_messages > 0); - - link->create_stacked_netdev_messages--; - - if (link_create_stacked_netdev_handler_internal(rtnl, m, link) <= 0) - return 0; - if (link->create_stacked_netdev_messages == 0) { link->stacked_netdevs_created = true; log_link_debug(link, "Stacked netdevs created."); @@ -687,23 +669,6 @@ static int link_create_stacked_netdev_handler(sd_netlink *rtnl, sd_netlink_messa return 0; } -static int link_create_stacked_netdev_after_configured_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - assert(link); - assert(link->create_stacked_netdev_after_configured_messages > 0); - - link->create_stacked_netdev_after_configured_messages--; - - if (link_create_stacked_netdev_handler_internal(rtnl, m, link) <= 0) - return 0; - - if (link->create_stacked_netdev_after_configured_messages == 0) { - link->stacked_netdevs_after_configured_created = true; - log_link_debug(link, "Stacked netdevs created."); - } - - return 0; -} - int link_request_stacked_netdev(Link *link, NetDev *netdev) { int r; @@ -716,19 +681,13 @@ int link_request_stacked_netdev(Link *link, NetDev *netdev) { if (!IN_SET(netdev->state, NETDEV_STATE_LOADING, NETDEV_STATE_FAILED) || netdev->ifindex > 0) return 0; /* Already created. */ - if (netdev_get_create_type(netdev) == NETDEV_CREATE_STACKED) { - link->stacked_netdevs_created = false; - r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false, - &link->create_stacked_netdev_messages, - link_create_stacked_netdev_handler, - NULL); - } else { - link->stacked_netdevs_after_configured_created = false; - r = link_queue_request(link, REQUEST_TYPE_STACKED_NETDEV, netdev, false, - &link->create_stacked_netdev_after_configured_messages, - link_create_stacked_netdev_after_configured_handler, - NULL); - } + link->stacked_netdevs_created = false; + r = link_queue_request_full(link, REQUEST_TYPE_NETDEV_STACKED, + netdev_ref(netdev), (mfree_func_t) netdev_unref, + trivial_hash_func, trivial_compare_func, + stacked_netdev_process_request, + &link->create_stacked_netdev_messages, + create_stacked_netdev_handler, NULL); if (r < 0) return log_link_error_errno(link, r, "Failed to request stacked netdev '%s': %m", netdev->ifname); @@ -737,6 +696,50 @@ int link_request_stacked_netdev(Link *link, NetDev *netdev) { return 0; } +static int independent_netdev_process_request(Request *req, Link *link, void *userdata) { + NetDev *netdev = ASSERT_PTR(userdata); + int r; + + assert(!link); + + r = netdev_is_ready_to_create(netdev, NULL); + if (r <= 0) + return r; + + r = independent_netdev_create(netdev); + if (r < 0) + return log_netdev_warning_errno(netdev, r, "Failed to create netdev: %m"); + + return 1; +} + +static int netdev_request_to_create(NetDev *netdev) { + int r; + + assert(netdev); + + if (netdev_is_stacked(netdev)) + return 0; + + r = netdev_is_ready_to_create(netdev, NULL); + if (r < 0) + return r; + if (r > 0) { + /* If the netdev has no dependency, then create it now. */ + r = independent_netdev_create(netdev); + if (r < 0) + return log_netdev_warning_errno(netdev, r, "Failed to create netdev: %m"); + + } else { + /* Otherwise, wait for the dependencies being resolved. */ + r = netdev_queue_request(netdev, independent_netdev_process_request, NULL); + if (r < 0) + return log_netdev_warning_errno(netdev, r, "Failed to request to create netdev: %m"); + } + + return 0; +} + int netdev_load_one(Manager *manager, const char *filename) { _cleanup_(netdev_unrefp) NetDev *netdev_raw = NULL, *netdev = NULL; const char *dropin_dirname; @@ -847,26 +850,16 @@ int netdev_load_one(Manager *manager, const char *filename) { log_netdev_debug(netdev, "loaded %s", netdev_kind_to_string(netdev->kind)); - if (IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_MASTER, NETDEV_CREATE_INDEPENDENT)) { - r = netdev_create(netdev, NULL, NULL); - if (r < 0) - return r; - } - - if (netdev_is_stacked_and_independent(netdev)) { - r = netdev_create(netdev, NULL, NULL); - if (r < 0) - return r; - } - - netdev = NULL; + r = netdev_request_to_create(netdev); + if (r < 0) + return r; + TAKE_PTR(netdev); return 0; } int netdev_load(Manager *manager, bool reload) { _cleanup_strv_free_ char **files = NULL; - char **f; int r; assert(manager); diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index c7262f550..1c7dc0f7e 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -41,6 +41,7 @@ "-VRF\0" \ "-VXCAN\0" \ "-VXLAN\0" \ + "-WLAN\0" \ "-WireGuard\0" \ "-WireGuardPeer\0" \ "-Xfrm\0" @@ -82,6 +83,7 @@ typedef enum NetDevKind { NETDEV_KIND_VXCAN, NETDEV_KIND_VXLAN, NETDEV_KIND_WIREGUARD, + NETDEV_KIND_WLAN, NETDEV_KIND_XFRM, _NETDEV_KIND_MAX, _NETDEV_KIND_TUNNEL, /* Used by config_parse_stacked_netdev() */ @@ -100,16 +102,13 @@ typedef enum NetDevState { typedef enum NetDevCreateType { NETDEV_CREATE_INDEPENDENT, - NETDEV_CREATE_MASTER, NETDEV_CREATE_STACKED, - NETDEV_CREATE_AFTER_CONFIGURED, _NETDEV_CREATE_MAX, _NETDEV_CREATE_INVALID = -EINVAL, } NetDevCreateType; typedef struct Manager Manager; typedef struct Condition Condition; -typedef struct Request Request; typedef struct NetDev { Manager *manager; @@ -153,14 +152,14 @@ typedef struct NetDevVTable { /* specifies if netdev is independent, or a master device or a stacked device */ NetDevCreateType create_type; + /* This is used for stacked netdev. Return true when the underlying link is ready. */ + int (*is_ready_to_create)(NetDev *netdev, Link *link); + /* create netdev, if not done via rtnl */ int (*create)(NetDev *netdev); - /* create netdev after link is fully configured */ - int (*create_after_configured)(NetDev *netdev, Link *link); - /* perform additional configuration after netdev has been createad */ - int (*post_create)(NetDev *netdev, Link *link, sd_netlink_message *message); + int (*post_create)(NetDev *netdev, Link *link); /* verify that compulsory configuration options were specified */ int (*config_verify)(NetDev *netdev, const char *filename); @@ -170,6 +169,9 @@ typedef struct NetDevVTable { /* Generate MAC address when MACAddress= is not specified. */ bool generate_mac; + + /* When assigning ifindex to the netdev, skip to check if the netdev kind matches. */ + bool skip_netdev_kind_check; } NetDevVTable; extern const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX]; @@ -205,9 +207,7 @@ int netdev_get(Manager *manager, const char *name, NetDev **ret); int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink); int netdev_generate_hw_addr(NetDev *netdev, Link *link, const char *name, const struct hw_addr_data *hw_addr, struct hw_addr_data *ret); -int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t cb); -int request_process_stacked_netdev(Request *req); int link_request_stacked_netdev(Link *link, NetDev *netdev); const char *netdev_kind_to_string(NetDevKind d) _const_; diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index 97e534fe9..3ba4484b6 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -86,10 +86,68 @@ int dhcp4_pd_create_6rd_tunnel_name(Link *link, char **ret) { return 0; } +static int dhcp4_pd_create_6rd_tunnel_message( + Link *link, + sd_netlink_message *m, + const struct in_addr *ipv4address, + uint8_t ipv4masklen, + const struct in6_addr *sixrd_prefix, + uint8_t sixrd_prefixlen) { + int r; + + r = sd_netlink_message_append_string(m, IFLA_IFNAME, link->dhcp4_6rd_tunnel_name); + if (r < 0) + return r; + + r = sd_netlink_message_open_container(m, IFLA_LINKINFO); + if (r < 0) + return r; + + r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "sit"); + if (r < 0) + return r; + + r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, ipv4address); + if (r < 0) + return r; + + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, 64); + if (r < 0) + return r; + + r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_6RD_PREFIX, sixrd_prefix); + if (r < 0) + return r; + + r = sd_netlink_message_append_u16(m, IFLA_IPTUN_6RD_PREFIXLEN, sixrd_prefixlen); + if (r < 0) + return r; + + struct in_addr relay_prefix = *ipv4address; + (void) in4_addr_mask(&relay_prefix, ipv4masklen); + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_6RD_RELAY_PREFIX, relay_prefix.s_addr); + if (r < 0) + return r; + + r = sd_netlink_message_append_u16(m, IFLA_IPTUN_6RD_RELAY_PREFIXLEN, ipv4masklen); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(m); + if (r < 0) + return r; + + return 0; +} + int dhcp4_pd_create_6rd_tunnel(Link *link, link_netlink_message_handler_t callback) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; uint8_t ipv4masklen, sixrd_prefixlen; - struct in_addr ipv4address, relay_prefix; + struct in_addr ipv4address; struct in6_addr sixrd_prefix; int r; @@ -110,65 +168,38 @@ int dhcp4_pd_create_6rd_tunnel(Link *link, link_netlink_message_handler_t callba r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, 0); if (r < 0) - return log_link_debug_errno(link, r, "Could not allocate RTM_NEWLINK message: %m"); + return log_link_debug_errno(link, r, "Failed to create netlink message: %m"); - r = sd_netlink_message_append_string(m, IFLA_IFNAME, link->dhcp4_6rd_tunnel_name); + r = dhcp4_pd_create_6rd_tunnel_message(link, m, + &ipv4address, ipv4masklen, + &sixrd_prefix, sixrd_prefixlen); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_IFNAME, attribute: %m"); - - r = sd_netlink_message_open_container(m, IFLA_LINKINFO); - if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m"); - - r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, "sit"); - if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m"); - - r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &ipv4address); - if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, 64); - if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_TTL attribute: %m"); - - r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_6RD_PREFIX, &sixrd_prefix); - if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_6RD_PREFIX attribute: %m"); - - r = sd_netlink_message_append_u16(m, IFLA_IPTUN_6RD_PREFIXLEN, sixrd_prefixlen); - if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_6RD_PREFIXLEN attribute: %m"); - - relay_prefix = ipv4address; - (void) in4_addr_mask(&relay_prefix, ipv4masklen); - r = sd_netlink_message_append_u32(m, IFLA_IPTUN_6RD_RELAY_PREFIX, relay_prefix.s_addr); - if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_6RD_RELAY_PREFIX attribute: %m"); - - r = sd_netlink_message_append_u16(m, IFLA_IPTUN_6RD_RELAY_PREFIXLEN, ipv4masklen); - if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_IPTUN_6RD_RELAY_PREFIXLEN attribute: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_INFO_DATA attribute: %m"); - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_LINKINFO attribute: %m"); + return log_link_debug_errno(link, r, "Failed to fill netlink message: %m"); r = netlink_call_async(link->manager->rtnl, NULL, m, callback, link_netlink_destroy_callback, link); if (r < 0) - return log_link_debug_errno(link, r, "Could not send rtnetlink message: %m"); + return log_link_debug_errno(link, r, "Could not send netlink message: %m"); link_ref(link); return 0; } +static int tunnel_get_local_address(Tunnel *t, Link *link, union in_addr_union *ret) { + assert(t); + + if (t->local_type < 0) { + if (ret) + *ret = t->local; + return 0; + } + + return link_get_local_address(link, t->local_type, t->family, NULL, ret); +} + static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + union in_addr_union local; Tunnel *t; int r; @@ -182,54 +213,67 @@ static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_ne assert(t); + if (t->external) { + r = sd_netlink_message_append_flag(m, IFLA_IPTUN_COLLECT_METADATA); + if (r < 0) + return r; + + /* If external mode is enabled, then the following settings should not be appended. */ + return 0; + } + if (link || t->assign_to_loopback) { r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link ? link->ifindex : LOOPBACK_IFINDEX); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); + return r; } - r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in); + r = tunnel_get_local_address(t, link, &local); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); + return log_netdev_error_errno(netdev, r, "Could not find local address: %m"); + + r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &local.in); + if (r < 0) + return r; r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PMTUDISC attribute: %m"); + return r; if (t->fou_tunnel) { r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_TYPE, t->fou_encap_type); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_TYPE attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_SPORT, htobe16(t->encap_src_port)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_SPORT attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, IFLA_IPTUN_ENCAP_DPORT, htobe16(t->fou_destination_port)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_DPORT attribute: %m"); + return r; } if (netdev->kind == NETDEV_KIND_SIT) { if (t->sixrd_prefixlen > 0) { r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_6RD_PREFIX, &t->sixrd_prefix); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_6RD_PREFIX attribute: %m"); + return r; /* u16 is deliberate here, even though we're passing a netmask that can never be >128. The kernel is * expecting to receive the prefixlen as a u16. */ r = sd_netlink_message_append_u16(m, IFLA_IPTUN_6RD_PREFIXLEN, t->sixrd_prefixlen); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_6RD_PREFIXLEN attribute: %m"); + return r; } if (t->isatap >= 0) { @@ -239,14 +283,15 @@ static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_ne r = sd_netlink_message_append_u16(m, IFLA_IPTUN_FLAGS, flags); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLAGS attribute: %m"); + return r; } } - return r; + return 0; } static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + union in_addr_union local; uint32_t ikey = 0; uint32_t okey = 0; uint16_t iflags = 0; @@ -273,37 +318,50 @@ static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_ assert(t); + if (t->external) { + r = sd_netlink_message_append_flag(m, IFLA_GRE_COLLECT_METADATA); + if (r < 0) + return r; + + /* If external mode is enabled, then the following settings should not be appended. */ + return 0; + } + if (link || t->assign_to_loopback) { r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link ? link->ifindex : LOOPBACK_IFINDEX); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m"); + return r; } if (netdev->kind == NETDEV_KIND_ERSPAN) { r = sd_netlink_message_append_u32(m, IFLA_GRE_ERSPAN_INDEX, t->erspan_index); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_ERSPAN_INDEX attribute: %m"); + return r; } - r = sd_netlink_message_append_in_addr(m, IFLA_GRE_LOCAL, &t->local.in); + r = tunnel_get_local_address(t, link, &local); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m"); + return log_netdev_error_errno(netdev, r, "Could not find local address: %m"); + + r = sd_netlink_message_append_in_addr(m, IFLA_GRE_LOCAL, &local.in); + if (r < 0) + return r; r = sd_netlink_message_append_in_addr(m, IFLA_GRE_REMOTE, &t->remote.in); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_REMOTE attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_GRE_TTL, t->ttl); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TTL attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_GRE_TOS, t->tos); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TOS attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_GRE_PMTUDISC, t->pmtudisc); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_PMTUDISC attribute: %m"); + return r; if (t->key != 0) { ikey = okey = htobe32(t->key); @@ -331,38 +389,39 @@ static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_ r = sd_netlink_message_append_u32(m, IFLA_GRE_IKEY, ikey); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_IKEY attribute: %m"); + return r; r = sd_netlink_message_append_u32(m, IFLA_GRE_OKEY, okey); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_OKEY attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, IFLA_GRE_IFLAGS, iflags); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_IFLAGS attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, IFLA_GRE_OFLAGS, oflags); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_OFLAGS, attribute: %m"); + return r; if (t->fou_tunnel) { r = sd_netlink_message_append_u16(m, IFLA_GRE_ENCAP_TYPE, t->fou_encap_type); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_ENCAP_TYPE attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, IFLA_GRE_ENCAP_SPORT, htobe16(t->encap_src_port)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_ENCAP_SPORT attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, IFLA_GRE_ENCAP_DPORT, htobe16(t->fou_destination_port)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_ENCAP_DPORT attribute: %m"); + return r; } - return r; + return 0; } static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + union in_addr_union local; uint32_t ikey = 0; uint32_t okey = 0; uint16_t iflags = 0; @@ -380,33 +439,46 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl assert(t); + if (t->external) { + r = sd_netlink_message_append_flag(m, IFLA_GRE_COLLECT_METADATA); + if (r < 0) + return r; + + /* If external mode is enabled, then the following settings should not be appended. */ + return 0; + } + if (link || t->assign_to_loopback) { r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link ? link->ifindex : LOOPBACK_IFINDEX); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m"); + return r; } - r = sd_netlink_message_append_in6_addr(m, IFLA_GRE_LOCAL, &t->local.in6); + r = tunnel_get_local_address(t, link, &local); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LOCAL attribute: %m"); + return log_netdev_error_errno(netdev, r, "Could not find local address: %m"); + + r = sd_netlink_message_append_in6_addr(m, IFLA_GRE_LOCAL, &local.in6); + if (r < 0) + return r; r = sd_netlink_message_append_in6_addr(m, IFLA_GRE_REMOTE, &t->remote.in6); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_REMOTE attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_GRE_TTL, t->ttl); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TTL attribute: %m"); + return r; if (t->ipv6_flowlabel != _NETDEV_IPV6_FLOWLABEL_INVALID) { r = sd_netlink_message_append_u32(m, IFLA_GRE_FLOWINFO, t->ipv6_flowlabel); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_FLOWINFO attribute: %m"); + return r; } r = sd_netlink_message_append_u32(m, IFLA_GRE_FLAGS, t->flags); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_FLAGS attribute: %m"); + return r; if (t->key != 0) { ikey = okey = htobe32(t->key); @@ -426,24 +498,25 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl r = sd_netlink_message_append_u32(m, IFLA_GRE_IKEY, ikey); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_IKEY attribute: %m"); + return r; r = sd_netlink_message_append_u32(m, IFLA_GRE_OKEY, okey); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_OKEY attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, IFLA_GRE_IFLAGS, iflags); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_IFLAGS attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, IFLA_GRE_OFLAGS, oflags); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_OFLAGS, attribute: %m"); + return r; - return r; + return 0; } static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + union in_addr_union local; uint32_t ikey, okey; Tunnel *t; int r; @@ -461,7 +534,7 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink if (link || t->assign_to_loopback) { r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link ? link->ifindex : LOOPBACK_IFINDEX); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_LINK attribute: %m"); + return r; } if (t->key != 0) @@ -473,24 +546,29 @@ static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink r = sd_netlink_message_append_u32(m, IFLA_VTI_IKEY, ikey); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_IKEY attribute: %m"); + return r; r = sd_netlink_message_append_u32(m, IFLA_VTI_OKEY, okey); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_OKEY attribute: %m"); + return r; - r = netlink_message_append_in_addr_union(m, IFLA_VTI_LOCAL, t->family, &t->local); + r = tunnel_get_local_address(t, link, &local); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_LOCAL attribute: %m"); + return log_netdev_error_errno(netdev, r, "Could not find local address: %m"); + + r = netlink_message_append_in_addr_union(m, IFLA_VTI_LOCAL, t->family, &local); + if (r < 0) + return r; r = netlink_message_append_in_addr_union(m, IFLA_VTI_REMOTE, t->family, &t->remote); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_REMOTE attribute: %m"); + return r; - return r; + return 0; } static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + union in_addr_union local; uint8_t proto; Tunnel *t; int r; @@ -502,46 +580,6 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl assert(t); - if (link || t->assign_to_loopback) { - r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link ? link->ifindex : LOOPBACK_IFINDEX); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); - } - - r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_LOCAL, &t->local.in6); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); - - r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in6); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m"); - - if (t->ipv6_flowlabel != _NETDEV_IPV6_FLOWLABEL_INVALID) { - r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLOWINFO, t->ipv6_flowlabel); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLOWINFO attribute: %m"); - } - - if (t->copy_dscp) - t->flags |= IP6_TNL_F_RCV_DSCP_COPY; - - if (t->allow_localremote >= 0) - SET_FLAG(t->flags, IP6_TNL_F_ALLOW_LOCAL_REMOTE, t->allow_localremote); - - if (t->encap_limit != 0) { - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_ENCAP_LIMIT, t->encap_limit); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_LIMIT attribute: %m"); - } - - r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLAGS, t->flags); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_FLAGS attribute: %m"); - switch (t->ip6tnl_mode) { case NETDEV_IP6_TNL_MODE_IP6IP6: proto = IPPROTO_IPV6; @@ -557,9 +595,77 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PROTO, proto); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PROTO attribute: %m"); + return r; - return r; + if (t->external) { + r = sd_netlink_message_append_flag(m, IFLA_IPTUN_COLLECT_METADATA); + if (r < 0) + return r; + + /* If external mode is enabled, then the following settings should not be appended. */ + return 0; + } + + if (link || t->assign_to_loopback) { + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link ? link->ifindex : LOOPBACK_IFINDEX); + if (r < 0) + return r; + } + + r = tunnel_get_local_address(t, link, &local); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not find local address: %m"); + + r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_LOCAL, &local.in6); + if (r < 0) + return r; + + r = sd_netlink_message_append_in6_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in6); + if (r < 0) + return r; + + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); + if (r < 0) + return r; + + if (t->ipv6_flowlabel != _NETDEV_IPV6_FLOWLABEL_INVALID) { + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLOWINFO, t->ipv6_flowlabel); + if (r < 0) + return r; + } + + if (t->copy_dscp) + t->flags |= IP6_TNL_F_RCV_DSCP_COPY; + + if (t->allow_localremote >= 0) + SET_FLAG(t->flags, IP6_TNL_F_ALLOW_LOCAL_REMOTE, t->allow_localremote); + + r = sd_netlink_message_append_u32(m, IFLA_IPTUN_FLAGS, t->flags); + if (r < 0) + return r; + + if (t->encap_limit != 0) { + r = sd_netlink_message_append_u8(m, IFLA_IPTUN_ENCAP_LIMIT, t->encap_limit); + if (r < 0) + return r; + } + + return 0; +} + +static int netdev_tunnel_is_ready_to_create(NetDev *netdev, Link *link) { + Tunnel *t; + + assert(netdev); + + t = TUNNEL(netdev); + + assert(t); + + if (t->independent) + return true; + + return tunnel_get_local_address(t, link, NULL) >= 0; } static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { @@ -572,6 +678,23 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { assert(t); + if (netdev->kind == NETDEV_KIND_IP6TNL && + t->ip6tnl_mode == _NETDEV_IP6_TNL_MODE_INVALID) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "ip6tnl without mode configured in %s. Ignoring", filename); + + if (t->external) { + if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_VTI6)) + log_netdev_debug(netdev, "vti/vti6 tunnel do not support external mode, ignoring."); + else { + /* tunnel with external mode does not require underlying interface. */ + t->independent = true; + + /* tunnel with external mode does not require any settings checked below. */ + return 0; + } + } + if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_SIT, NETDEV_KIND_GRE) && !IN_SET(t->family, AF_UNSPEC, AF_INET)) return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), @@ -592,11 +715,6 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "ip6gretap tunnel without a remote IPv6 address configured in %s. Ignoring", filename); - if (netdev->kind == NETDEV_KIND_IP6TNL && - t->ip6tnl_mode == _NETDEV_IP6_TNL_MODE_INVALID) - return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), - "ip6tnl without mode configured in %s. Ignoring", filename); - if (t->fou_tunnel && t->fou_destination_port <= 0) return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "FooOverUDP missing port configured in %s. Ignoring", filename); @@ -611,10 +729,15 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { if (t->assign_to_loopback) t->independent = true; + if (t->independent && t->local_type >= 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "The local address cannot be '%s' when Independent= or AssignToLoopback= is enabled, ignoring.", + strna(netdev_local_address_type_to_string(t->local_type))); + return 0; } -int config_parse_tunnel_address( +int config_parse_tunnel_local_address( const char *unit, const char *filename, unsigned line, @@ -626,28 +749,82 @@ int config_parse_tunnel_address( void *data, void *userdata) { + union in_addr_union buffer = IN_ADDR_NULL; + NetDevLocalAddressType type; Tunnel *t = userdata; - union in_addr_union *addr = data, buffer; int r, f; assert(filename); assert(lvalue); assert(rvalue); - assert(data); + assert(userdata); - /* This is used to parse addresses on both local and remote ends of the tunnel. - * Address families must match. - * - * "any" is a special value which means that the address is unspecified. - */ + if (isempty(rvalue) || streq(rvalue, "any")) { + /* Unset the previous assignment. */ + t->local = IN_ADDR_NULL; + t->local_type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID; - if (streq(rvalue, "any")) { - *addr = IN_ADDR_NULL; + /* If the remote address is not specified, also clear the address family. */ + if (!in_addr_is_set(t->family, &t->remote)) + t->family = AF_UNSPEC; + return 0; + } - /* As a special case, if both the local and remote addresses are - * unspecified, also clear the address family. */ - if (!in_addr_is_set(t->family, &t->local) && - !in_addr_is_set(t->family, &t->remote)) + type = netdev_local_address_type_from_string(rvalue); + if (IN_SET(type, NETDEV_LOCAL_ADDRESS_IPV4LL, NETDEV_LOCAL_ADDRESS_DHCP4)) + f = AF_INET; + else if (IN_SET(type, NETDEV_LOCAL_ADDRESS_IPV6LL, NETDEV_LOCAL_ADDRESS_DHCP6, NETDEV_LOCAL_ADDRESS_SLAAC)) + f = AF_INET6; + else { + type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID; + r = in_addr_from_string_auto(rvalue, &f, &buffer); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Tunnel address \"%s\" invalid, ignoring assignment: %m", rvalue); + return 0; + } + } + + if (t->family != AF_UNSPEC && t->family != f) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Address family does not match the previous assignment, ignoring assignment: %s", rvalue); + return 0; + } + + t->family = f; + t->local = buffer; + t->local_type = type; + return 0; +} + +int config_parse_tunnel_remote_address( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + union in_addr_union buffer; + Tunnel *t = userdata; + int r, f; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(userdata); + + if (isempty(rvalue) || streq(rvalue, "any")) { + /* Unset the previous assignment. */ + t->remote = IN_ADDR_NULL; + + /* If the local address is not specified, also clear the address family. */ + if (t->local_type == _NETDEV_LOCAL_ADDRESS_TYPE_INVALID && + !in_addr_is_set(t->family, &t->local)) t->family = AF_UNSPEC; return 0; } @@ -661,12 +838,12 @@ int config_parse_tunnel_address( if (t->family != AF_UNSPEC && t->family != f) { log_syntax(unit, LOG_WARNING, filename, line, 0, - "Tunnel addresses incompatible, ignoring assignment: %s", rvalue); + "Address family does not match the previous assignment, ignoring assignment: %s", rvalue); return 0; } t->family = f; - *addr = buffer; + t->remote = buffer; return 0; } @@ -682,15 +859,12 @@ int config_parse_tunnel_key( void *data, void *userdata) { + uint32_t *dest = ASSERT_PTR(data), k; union in_addr_union buffer; - Tunnel *t = userdata; - uint32_t k; int r; assert(filename); - assert(lvalue); assert(rvalue); - assert(data); r = in_addr_from_string(AF_INET, rvalue, &buffer); if (r < 0) { @@ -703,13 +877,7 @@ int config_parse_tunnel_key( } else k = be32toh(buffer.in.s_addr); - if (streq(lvalue, "Key")) - t->key = k; - else if (streq(lvalue, "InputKey")) - t->ikey = k; - else - t->okey = k; - + *dest = k; return 0; } @@ -725,32 +893,33 @@ int config_parse_ipv6_flowlabel( void *data, void *userdata) { - IPv6FlowLabel *ipv6_flowlabel = data; - Tunnel *t = userdata; - int k = 0; - int r; + Tunnel *t = ASSERT_PTR(userdata); + int k, r; assert(filename); - assert(lvalue); assert(rvalue); - assert(ipv6_flowlabel); if (streq(rvalue, "inherit")) { - *ipv6_flowlabel = IP6_FLOWINFO_FLOWLABEL; + t->ipv6_flowlabel = IP6_FLOWINFO_FLOWLABEL; t->flags |= IP6_TNL_F_USE_ORIG_FLOWLABEL; - } else { - r = config_parse_int(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &k, userdata); - if (r < 0) - return r; - - if (k > 0xFFFFF) - log_syntax(unit, LOG_WARNING, filename, line, 0, "Failed to parse IPv6 flowlabel option, ignoring: %s", rvalue); - else { - *ipv6_flowlabel = htobe32(k) & IP6_FLOWINFO_FLOWLABEL; - t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL; - } + return 0; } + r = safe_atoi(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse tunnel IPv6 flowlabel, ignoring assignment: %s", rvalue); + return 0; + } + + if (k > 0xFFFFF) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid tunnel IPv6 flowlabel, ignoring assignment: %s", rvalue); + return 0; + } + + t->ipv6_flowlabel = htobe32(k) & IP6_FLOWINFO_FLOWLABEL; + t->flags &= ~IP6_TNL_F_USE_ORIG_FLOWLABEL; return 0; } @@ -766,31 +935,33 @@ int config_parse_encap_limit( void *data, void *userdata) { - Tunnel *t = userdata; - int k = 0; - int r; + Tunnel *t = ASSERT_PTR(userdata); + int k, r; assert(filename); - assert(lvalue); assert(rvalue); - if (streq(rvalue, "none")) + if (streq(rvalue, "none")) { t->flags |= IP6_TNL_F_IGN_ENCAP_LIMIT; - else { - r = safe_atoi(rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse Tunnel Encapsulation Limit option, ignoring: %s", rvalue); - return 0; - } - - if (k > 255 || k < 0) - log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid Tunnel Encapsulation value, ignoring: %d", k); - else { - t->encap_limit = k; - t->flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; - } + t->encap_limit = 0; + return 0; } + r = safe_atoi(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse Tunnel Encapsulation Limit option, ignoring assignment: %s", rvalue); + return 0; + } + + if (k > 255 || k < 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid Tunnel Encapsulation value, ignoring assignment: %d", k); + return 0; + } + + t->encap_limit = k; + t->flags &= ~IP6_TNL_F_IGN_ENCAP_LIMIT; return 0; } @@ -840,6 +1011,7 @@ static void netdev_tunnel_init(NetDev *netdev) { assert(t); + t->local_type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID; t->pmtudisc = true; t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT; t->isatap = -1; @@ -859,6 +1031,7 @@ const NetDevVTable ipip_vtable = { .sections = NETDEV_COMMON_SECTIONS "Tunnel\0", .fill_message_create = netdev_ipip_sit_fill_message_create, .create_type = NETDEV_CREATE_STACKED, + .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, .iftype = ARPHRD_TUNNEL, }; @@ -869,6 +1042,7 @@ const NetDevVTable sit_vtable = { .sections = NETDEV_COMMON_SECTIONS "Tunnel\0", .fill_message_create = netdev_ipip_sit_fill_message_create, .create_type = NETDEV_CREATE_STACKED, + .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, .iftype = ARPHRD_SIT, }; @@ -879,6 +1053,7 @@ const NetDevVTable vti_vtable = { .sections = NETDEV_COMMON_SECTIONS "Tunnel\0", .fill_message_create = netdev_vti_fill_message_create, .create_type = NETDEV_CREATE_STACKED, + .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, .iftype = ARPHRD_TUNNEL, }; @@ -889,6 +1064,7 @@ const NetDevVTable vti6_vtable = { .sections = NETDEV_COMMON_SECTIONS "Tunnel\0", .fill_message_create = netdev_vti_fill_message_create, .create_type = NETDEV_CREATE_STACKED, + .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, .iftype = ARPHRD_TUNNEL6, }; @@ -899,6 +1075,7 @@ const NetDevVTable gre_vtable = { .sections = NETDEV_COMMON_SECTIONS "Tunnel\0", .fill_message_create = netdev_gre_erspan_fill_message_create, .create_type = NETDEV_CREATE_STACKED, + .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, .iftype = ARPHRD_IPGRE, }; @@ -909,6 +1086,7 @@ const NetDevVTable gretap_vtable = { .sections = NETDEV_COMMON_SECTIONS "Tunnel\0", .fill_message_create = netdev_gre_erspan_fill_message_create, .create_type = NETDEV_CREATE_STACKED, + .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, .iftype = ARPHRD_ETHER, .generate_mac = true, @@ -920,6 +1098,7 @@ const NetDevVTable ip6gre_vtable = { .sections = NETDEV_COMMON_SECTIONS "Tunnel\0", .fill_message_create = netdev_ip6gre_fill_message_create, .create_type = NETDEV_CREATE_STACKED, + .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, .iftype = ARPHRD_IP6GRE, }; @@ -930,6 +1109,7 @@ const NetDevVTable ip6gretap_vtable = { .sections = NETDEV_COMMON_SECTIONS "Tunnel\0", .fill_message_create = netdev_ip6gre_fill_message_create, .create_type = NETDEV_CREATE_STACKED, + .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, .iftype = ARPHRD_ETHER, .generate_mac = true, @@ -941,6 +1121,7 @@ const NetDevVTable ip6tnl_vtable = { .sections = NETDEV_COMMON_SECTIONS "Tunnel\0", .fill_message_create = netdev_ip6tnl_fill_message_create, .create_type = NETDEV_CREATE_STACKED, + .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, .iftype = ARPHRD_TUNNEL6, }; @@ -951,6 +1132,7 @@ const NetDevVTable erspan_vtable = { .sections = NETDEV_COMMON_SECTIONS "Tunnel\0", .fill_message_create = netdev_gre_erspan_fill_message_create, .create_type = NETDEV_CREATE_STACKED, + .is_ready_to_create = netdev_tunnel_is_ready_to_create, .config_verify = netdev_tunnel_verify, .iftype = ARPHRD_ETHER, .generate_mac = true, diff --git a/src/network/netdev/tunnel.h b/src/network/netdev/tunnel.h index b9e68759f..e25dfb215 100644 --- a/src/network/netdev/tunnel.h +++ b/src/network/netdev/tunnel.h @@ -5,6 +5,7 @@ #include "conf-parser.h" #include "fou-tunnel.h" +#include "netdev-util.h" #include "netdev.h" #include "networkd-link.h" @@ -42,6 +43,7 @@ typedef struct Tunnel { uint32_t okey; uint32_t erspan_index; + NetDevLocalAddressType local_type; union in_addr_union local; union in_addr_union remote; @@ -53,6 +55,7 @@ typedef struct Tunnel { bool independent; bool fou_tunnel; bool assign_to_loopback; + bool external; /* a.k.a collect metadata mode */ uint16_t encap_src_port; uint16_t fou_destination_port; @@ -119,7 +122,8 @@ const char *ip6tnl_mode_to_string(Ip6TnlMode d) _const_; Ip6TnlMode ip6tnl_mode_from_string(const char *d) _pure_; CONFIG_PARSER_PROTOTYPE(config_parse_ip6tnl_mode); -CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_address); +CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_local_address); +CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_remote_address); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_flowlabel); CONFIG_PARSER_PROTOTYPE(config_parse_encap_limit); CONFIG_PARSER_PROTOTYPE(config_parse_tunnel_key); diff --git a/src/network/netdev/veth.c b/src/network/netdev/veth.c index c946e81fc..fb00e6667 100644 --- a/src/network/netdev/veth.c +++ b/src/network/netdev/veth.c @@ -24,12 +24,12 @@ static int netdev_veth_fill_message_create(NetDev *netdev, Link *link, sd_netlin r = sd_netlink_message_open_container(m, VETH_INFO_PEER); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append VETH_INFO_PEER attribute: %m"); + return r; if (v->ifname_peer) { r = sd_netlink_message_append_string(m, IFLA_IFNAME, v->ifname_peer); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to add netlink interface name: %m"); + return r; } r = netdev_generate_hw_addr(netdev, NULL, v->ifname_peer, &v->hw_addr_peer, &hw_addr); @@ -40,20 +40,20 @@ static int netdev_veth_fill_message_create(NetDev *netdev, Link *link, sd_netlin log_netdev_debug(netdev, "Using MAC address for peer: %s", HW_ADDR_TO_STR(&hw_addr)); r = netlink_message_append_hw_addr(m, IFLA_ADDRESS, &hw_addr); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_ADDRESS attribute: %m"); + return r; } if (netdev->mtu != 0) { r = sd_netlink_message_append_u32(m, IFLA_MTU, netdev->mtu); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_MTU attribute: %m"); + return r; } r = sd_netlink_message_close_container(m); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_INFO_DATA attribute: %m"); + return r; - return r; + return 0; } static int netdev_veth_verify(NetDev *netdev, const char *filename) { diff --git a/src/network/netdev/vlan.c b/src/network/netdev/vlan.c index af3e77963..3f9f59475 100644 --- a/src/network/netdev/vlan.c +++ b/src/network/netdev/vlan.c @@ -24,12 +24,12 @@ static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlin r = sd_netlink_message_append_u16(req, IFLA_VLAN_ID, v->id); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VLAN_ID attribute: %m"); + return r; if (v->protocol >= 0) { r = sd_netlink_message_append_u16(req, IFLA_VLAN_PROTOCOL, htobe16(v->protocol)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VLAN_PROTOCOL attribute: %m"); + return r; } if (v->gvrp != -1) { @@ -54,24 +54,24 @@ static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlin r = sd_netlink_message_append_data(req, IFLA_VLAN_FLAGS, &flags, sizeof(struct ifla_vlan_flags)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VLAN_FLAGS attribute: %m"); + return r; if (!set_isempty(v->egress_qos_maps)) { struct ifla_vlan_qos_mapping *m; r = sd_netlink_message_open_container(req, IFLA_VLAN_EGRESS_QOS); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not open container IFLA_VLAN_EGRESS_QOS: %m"); + return r; SET_FOREACH(m, v->egress_qos_maps) { r = sd_netlink_message_append_data(req, IFLA_VLAN_QOS_MAPPING, m, sizeof(struct ifla_vlan_qos_mapping)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VLAN_QOS_MAPPING attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not close container IFLA_VLAN_EGRESS_QOS: %m"); + return r; } if (!set_isempty(v->ingress_qos_maps)) { @@ -79,17 +79,17 @@ static int netdev_vlan_fill_message_create(NetDev *netdev, Link *link, sd_netlin r = sd_netlink_message_open_container(req, IFLA_VLAN_INGRESS_QOS); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not open container IFLA_VLAN_INGRESS_QOS: %m"); + return r; SET_FOREACH(m, v->ingress_qos_maps) { r = sd_netlink_message_append_data(req, IFLA_VLAN_QOS_MAPPING, m, sizeof(struct ifla_vlan_qos_mapping)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VLAN_QOS_MAPPING attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not close container IFLA_VLAN_INGRESS_QOS: %m"); + return r; } return 0; diff --git a/src/network/netdev/vrf.c b/src/network/netdev/vrf.c index b1b670744..05ef3ff13 100644 --- a/src/network/netdev/vrf.c +++ b/src/network/netdev/vrf.c @@ -20,16 +20,16 @@ static int netdev_vrf_fill_message_create(NetDev *netdev, Link *link, sd_netlink r = sd_netlink_message_append_u32(m, IFLA_VRF_TABLE, v->table); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VRF_TABLE attribute: %m"); + return r; - return r; + return 0; } const NetDevVTable vrf_vtable = { .object_size = sizeof(Vrf), .sections = NETDEV_COMMON_SECTIONS "VRF\0", .fill_message_create = netdev_vrf_fill_message_create, - .create_type = NETDEV_CREATE_MASTER, + .create_type = NETDEV_CREATE_INDEPENDENT, .iftype = ARPHRD_ETHER, .generate_mac = true, }; diff --git a/src/network/netdev/vxcan.c b/src/network/netdev/vxcan.c index a0ba048eb..83269b070 100644 --- a/src/network/netdev/vxcan.c +++ b/src/network/netdev/vxcan.c @@ -19,19 +19,19 @@ static int netdev_vxcan_fill_message_create(NetDev *netdev, Link *link, sd_netli r = sd_netlink_message_open_container(m, VXCAN_INFO_PEER); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append VXCAN_INFO_PEER attribute: %m"); + return r; if (v->ifname_peer) { r = sd_netlink_message_append_string(m, IFLA_IFNAME, v->ifname_peer); if (r < 0) - return log_netdev_error_errno(netdev, r, "Failed to add vxcan netlink interface peer name: %m"); + return r; } r = sd_netlink_message_close_container(m); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append VXCAN_INFO_PEER attribute: %m"); + return r; - return r; + return 0; } static int netdev_vxcan_verify(NetDev *netdev, const char *filename) { @@ -44,10 +44,9 @@ static int netdev_vxcan_verify(NetDev *netdev, const char *filename) { assert(v); - if (!v->ifname_peer) { - log_netdev_warning(netdev, "VxCan NetDev without peer name configured in %s. Ignoring", filename); - return -EINVAL; - } + if (!v->ifname_peer) + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "VxCan NetDev without peer name configured in %s. Ignoring", filename); return 0; } diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c index 30b085559..d93084c2d 100644 --- a/src/network/netdev/vxlan.c +++ b/src/network/netdev/vxlan.c @@ -22,9 +22,24 @@ static const char* const df_table[_NETDEV_VXLAN_DF_MAX] = { DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(df, VxLanDF, NETDEV_VXLAN_DF_YES); DEFINE_CONFIG_PARSE_ENUM(config_parse_df, df, VxLanDF, "Failed to parse VXLAN IPDoNotFragment= setting"); +static int vxlan_get_local_address(VxLan *v, Link *link, int *ret_family, union in_addr_union *ret_address) { + assert(v); + + if (v->local_type < 0) { + if (ret_family) + *ret_family = v->local_family; + if (ret_address) + *ret_address = v->local; + return 0; + } + + return link_get_local_address(link, v->local_type, v->local_family, ret_family, ret_address); +} + static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + union in_addr_union local; + int local_family, r; VxLan *v; - int r; assert(netdev); assert(m); @@ -36,7 +51,7 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli if (v->vni <= VXLAN_VID_MAX) { r = sd_netlink_message_append_u32(m, IFLA_VXLAN_ID, v->vni); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_ID attribute: %m"); + return r; } if (in_addr_is_set(v->group_family, &v->group)) { @@ -45,100 +60,104 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli else r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_GROUP6, &v->group.in6); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GROUP attribute: %m"); + return r; } else if (in_addr_is_set(v->remote_family, &v->remote)) { if (v->remote_family == AF_INET) r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->remote.in); else r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_GROUP6, &v->remote.in6); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GROUP attribute: %m"); + return r; } - if (in_addr_is_set(v->local_family, &v->local)) { - if (v->local_family == AF_INET) - r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_LOCAL, &v->local.in); + r = vxlan_get_local_address(v, link, &local_family, &local); + if (r < 0) + return r; + + if (in_addr_is_set(local_family, &local)) { + if (local_family == AF_INET) + r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_LOCAL, &local.in); else - r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_LOCAL6, &v->local.in6); + r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_LOCAL6, &local.in6); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LOCAL attribute: %m"); + return r; } r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LINK, link ? link->ifindex : 0); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LINK attribute: %m"); + return r; if (v->inherit) { r = sd_netlink_message_append_flag(m, IFLA_VXLAN_TTL_INHERIT); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TTL_INHERIT attribute: %m"); + return r; } else { r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TTL, v->ttl); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TTL attribute: %m"); + return r; } if (v->tos != 0) { r = sd_netlink_message_append_u8(m, IFLA_VXLAN_TOS, v->tos); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_TOS attribute: %m"); + return r; } r = sd_netlink_message_append_u8(m, IFLA_VXLAN_LEARNING, v->learning); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LEARNING attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_VXLAN_RSC, v->route_short_circuit); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_RSC attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_VXLAN_PROXY, v->arp_proxy); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PROXY attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_VXLAN_L2MISS, v->l2miss); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_L2MISS attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_VXLAN_L3MISS, v->l3miss); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_L3MISS attribute: %m"); + return r; if (v->fdb_ageing != 0) { r = sd_netlink_message_append_u32(m, IFLA_VXLAN_AGEING, v->fdb_ageing / USEC_PER_SEC); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_AGEING attribute: %m"); + return r; } if (v->max_fdb != 0) { r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LIMIT, v->max_fdb); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LIMIT attribute: %m"); + return r; } r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_CSUM, v->udpcsum); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_CSUM attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_ZERO_CSUM6_TX, v->udp6zerocsumtx); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_ZERO_CSUM6_TX attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_VXLAN_UDP_ZERO_CSUM6_RX, v->udp6zerocsumrx); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_UDP_ZERO_CSUM6_RX attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_VXLAN_REMCSUM_TX, v->remote_csum_tx); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_REMCSUM_TX attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, IFLA_VXLAN_REMCSUM_RX, v->remote_csum_rx); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_REMCSUM_RX attribute: %m"); + return r; r = sd_netlink_message_append_u16(m, IFLA_VXLAN_PORT, htobe16(v->dest_port)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PORT attribute: %m"); + return r; if (v->port_range.low != 0 || v->port_range.high != 0) { struct ifla_vxlan_port_range port_range; @@ -148,32 +167,32 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli r = sd_netlink_message_append_data(m, IFLA_VXLAN_PORT_RANGE, &port_range, sizeof(port_range)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_PORT_RANGE attribute: %m"); + return r; } r = sd_netlink_message_append_u32(m, IFLA_VXLAN_LABEL, htobe32(v->flow_label)); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LABEL attribute: %m"); + return r; if (v->group_policy) { r = sd_netlink_message_append_flag(m, IFLA_VXLAN_GBP); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GBP attribute: %m"); + return r; } if (v->generic_protocol_extension) { r = sd_netlink_message_append_flag(m, IFLA_VXLAN_GPE); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_GPE attribute: %m"); + return r; } if (v->df != _NETDEV_VXLAN_DF_INVALID) { r = sd_netlink_message_append_u8(m, IFLA_VXLAN_DF, v->df); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_DF attribute: %m"); + return r; } - return r; + return 0; } int config_parse_vxlan_address( @@ -190,16 +209,45 @@ int config_parse_vxlan_address( VxLan *v = userdata; union in_addr_union *addr = data, buffer; - int r, f; + int *family, f, r; assert(filename); assert(lvalue); assert(rvalue); assert(data); + assert(userdata); + + if (streq(lvalue, "Local")) + family = &v->local_family; + else if (streq(lvalue, "Remote")) + family = &v->remote_family; + else if (streq(lvalue, "Group")) + family = &v->group_family; + else + assert_not_reached(); + + if (isempty(rvalue)) { + *addr = IN_ADDR_NULL; + *family = AF_UNSPEC; + return 0; + } + + if (streq(lvalue, "Local")) { + NetDevLocalAddressType t; + + t = netdev_local_address_type_from_string(rvalue); + if (t >= 0) { + v->local = IN_ADDR_NULL; + v->local_family = AF_UNSPEC; + v->local_type = t; + return 0; + } + } r = in_addr_from_string_auto(rvalue, &f, &buffer); if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "vxlan '%s' address is invalid, ignoring assignment: %s", lvalue, rvalue); + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); return 0; } @@ -207,24 +255,22 @@ int config_parse_vxlan_address( if (streq(lvalue, "Group")) { if (r <= 0) { - log_syntax(unit, LOG_WARNING, filename, line, 0, "vxlan %s invalid multicast address, ignoring assignment: %s", lvalue, rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, + "%s= must be a multicast address, ignoring assignment: %s", lvalue, rvalue); return 0; } - - v->group_family = f; } else { if (r > 0) { - log_syntax(unit, LOG_WARNING, filename, line, 0, "vxlan %s cannot be a multicast address, ignoring assignment: %s", lvalue, rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, + "%s= cannot be a multicast address, ignoring assignment: %s", lvalue, rvalue); return 0; } - - if (streq(lvalue, "Remote")) - v->remote_family = f; - else - v->local_family = f; } + if (streq(lvalue, "Local")) + v->local_type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID; *addr = buffer; + *family = f; return 0; } @@ -369,9 +415,29 @@ static int netdev_vxlan_verify(NetDev *netdev, const char *filename) { "%s: VXLAN both 'Group=' and 'Remote=' cannot be specified. Ignoring.", filename); + if (v->independent && v->local_type >= 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "The local address cannot be '%s' when Independent= is enabled, ignoring.", + strna(netdev_local_address_type_to_string(v->local_type))); + return 0; } +static int netdev_vxlan_is_ready_to_create(NetDev *netdev, Link *link) { + VxLan *v; + + assert(netdev); + + v = VXLAN(netdev); + + assert(v); + + if (v->independent) + return true; + + return vxlan_get_local_address(v, link, NULL, NULL) >= 0; +} + static void vxlan_init(NetDev *netdev) { VxLan *v; @@ -381,6 +447,7 @@ static void vxlan_init(NetDev *netdev) { assert(v); + v->local_type = _NETDEV_LOCAL_ADDRESS_TYPE_INVALID; v->vni = VXLAN_VID_MAX + 1; v->df = _NETDEV_VXLAN_DF_INVALID; v->learning = true; @@ -395,6 +462,7 @@ const NetDevVTable vxlan_vtable = { .sections = NETDEV_COMMON_SECTIONS "VXLAN\0", .fill_message_create = netdev_vxlan_fill_message_create, .create_type = NETDEV_CREATE_STACKED, + .is_ready_to_create = netdev_vxlan_is_ready_to_create, .config_verify = netdev_vxlan_verify, .iftype = ARPHRD_ETHER, .generate_mac = true, diff --git a/src/network/netdev/vxlan.h b/src/network/netdev/vxlan.h index 12ef46ef9..141ac4db4 100644 --- a/src/network/netdev/vxlan.h +++ b/src/network/netdev/vxlan.h @@ -6,6 +6,7 @@ typedef struct VxLan VxLan; #include #include "in-addr-util.h" +#include "netdev-util.h" #include "netdev.h" #define VXLAN_VID_MAX (1u << 24) - 1 @@ -30,8 +31,9 @@ struct VxLan { VxLanDF df; - union in_addr_union remote; + NetDevLocalAddressType local_type; union in_addr_union local; + union in_addr_union remote; union in_addr_union group; unsigned tos; diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index 88f668753..32525e620 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -47,7 +47,7 @@ static WireguardPeer* wireguard_peer_free(WireguardPeer *peer) { hashmap_remove(peer->wireguard->peers_by_section, peer->section); } - network_config_section_free(peer->section); + config_section_free(peer->section); while ((mask = peer->ipmasks)) { LIST_REMOVE(ipmasks, peer->ipmasks, mask); @@ -65,10 +65,10 @@ static WireguardPeer* wireguard_peer_free(WireguardPeer *peer) { return mfree(peer); } -DEFINE_NETWORK_SECTION_FUNCTIONS(WireguardPeer, wireguard_peer_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(WireguardPeer, wireguard_peer_free); static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL; int r; @@ -77,7 +77,7 @@ static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigne assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -99,7 +99,7 @@ static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigne LIST_PREPEND(peers, w->peers, peer); - r = hashmap_ensure_put(&w->peers_by_section, &network_config_hash_ops, peer->section, peer); + r = hashmap_ensure_put(&w->peers_by_section, &config_section_hash_ops, peer->section, peer); if (r < 0) return r; @@ -147,7 +147,7 @@ cancel: } static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, const WireguardPeer *peer, uint16_t index, WireguardIPmask **mask_start) { - WireguardIPmask *mask, *start; + WireguardIPmask *start, *last = NULL; uint16_t j = 0; int r; @@ -196,8 +196,10 @@ static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, c r = wireguard_set_ipmask_one(netdev, message, mask, ++j); if (r < 0) return r; - if (r == 0) + if (r == 0) { + last = mask; break; + } } r = sd_netlink_message_close_container(message); @@ -208,8 +210,8 @@ static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, c if (r < 0) return log_netdev_error_errno(netdev, r, "Could not add wireguard peer: %m"); - *mask_start = mask; /* Start next cycle from this mask. */ - return !mask; + *mask_start = last; /* Start next cycle from this mask. */ + return !last; cancel: r = sd_netlink_message_cancel_array(message); @@ -222,7 +224,7 @@ cancel: static int wireguard_set_interface(NetDev *netdev) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL; WireguardIPmask *mask_start = NULL; - WireguardPeer *peer, *peer_start; + WireguardPeer *peer_start; bool sent_once = false; uint32_t serial; Wireguard *w; @@ -267,14 +269,17 @@ static int wireguard_set_interface(NetDev *netdev) { if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append wireguard peer attributes: %m"); + WireguardPeer *peer_last = NULL; LIST_FOREACH(peers, peer, peer_start) { r = wireguard_set_peer_one(netdev, message, peer, ++i, &mask_start); if (r < 0) return r; - if (r == 0) + if (r == 0) { + peer_last = peer; break; + } } - peer_start = peer; /* Start next cycle from this peer. */ + peer_start = peer_last; /* Start next cycle from this peer. */ r = sd_netlink_message_close_container(message); if (r < 0) @@ -370,7 +375,7 @@ static int wireguard_peer_resolve_handler( if (peer->n_retries > 0) { r = event_reset_time_relative(netdev->manager->event, &peer->resolve_retry_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, peer_next_resolve_usec(peer), 0, on_resolve_retry, peer, 0, "wireguard-resolve-retry", true); if (r < 0) @@ -424,7 +429,6 @@ static int peer_resolve_endpoint(WireguardPeer *peer) { } static void wireguard_resolve_endpoints(NetDev *netdev) { - WireguardPeer *peer; Wireguard *w; assert(netdev); @@ -437,7 +441,7 @@ static void wireguard_resolve_endpoints(NetDev *netdev) { break; } -static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) { +static int netdev_wireguard_post_create(NetDev *netdev, Link *link) { assert(netdev); assert(WIREGUARD(netdev)); @@ -1124,7 +1128,6 @@ static int wireguard_peer_verify(WireguardPeer *peer) { } static int wireguard_verify(NetDev *netdev, const char *filename) { - WireguardPeer *peer, *peer_next; Wireguard *w; int r; @@ -1143,9 +1146,7 @@ static int wireguard_verify(NetDev *netdev, const char *filename) { "%s: Missing PrivateKey= or PrivateKeyFile=, " "Ignoring network device.", filename); - LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers) { - WireguardIPmask *ipmask; - + LIST_FOREACH(peers, peer, w->peers) { if (wireguard_peer_verify(peer) < 0) { wireguard_peer_free(peer); continue; diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h index 29bacdc77..09dca88bb 100644 --- a/src/network/netdev/wireguard.h +++ b/src/network/netdev/wireguard.h @@ -24,7 +24,7 @@ typedef struct WireguardIPmask { typedef struct WireguardPeer { Wireguard *wireguard; - NetworkConfigSection *section; + ConfigSection *section; uint8_t public_key[WG_KEY_LEN]; uint8_t preshared_key[WG_KEY_LEN]; diff --git a/src/network/netdev/wlan.c b/src/network/netdev/wlan.c new file mode 100644 index 000000000..bf23ae3d3 --- /dev/null +++ b/src/network/netdev/wlan.c @@ -0,0 +1,260 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "sd-netlink.h" + +#include "netlink-util.h" +#include "networkd-manager.h" +#include "networkd-wiphy.h" +#include "parse-util.h" +#include "wifi-util.h" +#include "wlan.h" + +static void wlan_done(NetDev *netdev) { + WLan *w; + + assert(netdev); + + w = WLAN(netdev); + + assert(w); + + w->wiphy_name = mfree(w->wiphy_name); +} + +static void wlan_init(NetDev *netdev) { + WLan *w; + + assert(netdev); + + w = WLAN(netdev); + + assert(w); + + w->wiphy_index = UINT32_MAX; + w->wds = -1; +} + +static int wlan_get_wiphy(NetDev *netdev, Wiphy **ret) { + WLan *w; + + assert(netdev); + + w = WLAN(netdev); + + assert(w); + + if (w->wiphy_name) + return wiphy_get_by_name(netdev->manager, w->wiphy_name, ret); + + return wiphy_get_by_index(netdev->manager, w->wiphy_index, ret); +} + +static int wlan_is_ready_to_create(NetDev *netdev, Link *link) { + return wlan_get_wiphy(netdev, NULL) >= 0; +} + +static int wlan_fill_message(NetDev *netdev, sd_netlink_message *m) { + Wiphy *wiphy; + WLan *w; + int r; + + assert(netdev); + assert(m); + + w = WLAN(netdev); + + assert(w); + + r = wlan_get_wiphy(netdev, &wiphy); + if (r < 0) + return r; + + r = sd_netlink_message_append_u32(m, NL80211_ATTR_WIPHY, wiphy->index); + if (r < 0) + return r; + + r = sd_netlink_message_append_string(m, NL80211_ATTR_IFNAME, netdev->ifname); + if (r < 0) + return r; + + r = sd_netlink_message_append_u32(m, NL80211_ATTR_IFTYPE, w->iftype); + if (r < 0) + return r; + + if (!hw_addr_is_null(&netdev->hw_addr) && netdev->hw_addr.length == ETH_ALEN) { + r = sd_netlink_message_append_ether_addr(m, NL80211_ATTR_MAC, &netdev->hw_addr.ether); + if (r < 0) + return r; + } + + if (w->wds >= 0) { + r = sd_netlink_message_append_u8(m, NL80211_ATTR_4ADDR, w->wds); + if (r < 0) + return r; + } + + return 0; +} + +static int wlan_create_handler(sd_netlink *genl, sd_netlink_message *m, NetDev *netdev) { + int r; + + assert(netdev); + assert(netdev->state != _NETDEV_STATE_INVALID); + + r = sd_netlink_message_get_errno(m); + if (IN_SET(r, -EEXIST, -ENFILE)) + /* Unlike the other netdevs, the kernel may return -ENFILE. See dev_alloc_name(). */ + log_netdev_info(netdev, "WLAN interface exists, using existing without changing its parameters."); + else if (r < 0) { + log_netdev_warning_errno(netdev, r, "WLAN interface could not be created: %m"); + netdev_enter_failed(netdev); + + return 1; + } + + log_netdev_debug(netdev, "WLAN interface is created."); + return 1; +} + +static int wlan_create(NetDev *netdev) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert(netdev); + assert(netdev->manager); + assert(netdev->manager->genl); + + r = sd_genl_message_new(netdev->manager->genl, NL80211_GENL_NAME, NL80211_CMD_NEW_INTERFACE, &m); + if (r < 0) + return log_netdev_warning_errno(netdev, r, "Failed to allocate netlink message: %m"); + + r = wlan_fill_message(netdev, m); + if (r < 0) + return log_netdev_warning_errno(netdev, r, "Failed to fill netlink message: %m"); + + r = netlink_call_async(netdev->manager->genl, NULL, m, wlan_create_handler, + netdev_destroy_callback, netdev); + if (r < 0) + return log_netdev_warning_errno(netdev, r, "Failed to send netlink message: %m"); + + netdev_ref(netdev); + return 0; +} + +static int wlan_verify(NetDev *netdev, const char *filename) { + WLan *w; + + assert(netdev); + assert(filename); + + w = WLAN(netdev); + + assert(w); + + if (w->iftype == NL80211_IFTYPE_UNSPECIFIED) + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: WLAN interface type is not specified, ignoring.", + filename); + + if (w->wiphy_index == UINT32_MAX && isempty(w->wiphy_name)) + return log_netdev_warning_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: physical WLAN device is not specified, ignoring.", + filename); + + return 0; +} + +int config_parse_wiphy( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + WLan *w = userdata; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(userdata); + + if (isempty(rvalue)) { + w->wiphy_name = mfree(w->wiphy_name); + w->wiphy_index = UINT32_MAX; + return 0; + } + + r = safe_atou32(rvalue, &w->wiphy_index); + if (r >= 0) { + w->wiphy_name = mfree(w->wiphy_name); + return 0; + } + + r = free_and_strdup_warn(&w->wiphy_name, rvalue); + if (r < 0) + return r; + + w->wiphy_index = UINT32_MAX; + return 0; +} + +int config_parse_wlan_iftype( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + enum nl80211_iftype t, *iftype = data; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *iftype = NL80211_IFTYPE_UNSPECIFIED; + return 0; + } + + t = nl80211_iftype_from_string(rvalue); + /* We reuse the kernel provided enum which does not contain negative value. So, the cast + * below is mandatory. Otherwise, the check below always passes. */ + if ((int) t < 0) { + log_syntax(unit, LOG_WARNING, filename, line, t, + "Failed to parse wlan interface type, ignoring assignment: %s", + rvalue); + return 0; + } + + *iftype = t; + return 0; +} + +const NetDevVTable wlan_vtable = { + .object_size = sizeof(WLan), + .init = wlan_init, + .done = wlan_done, + .sections = NETDEV_COMMON_SECTIONS "WLAN\0", + .is_ready_to_create = wlan_is_ready_to_create, + .create = wlan_create, + .create_type = NETDEV_CREATE_INDEPENDENT, + .config_verify = wlan_verify, + .iftype = ARPHRD_ETHER, + .generate_mac = true, + .skip_netdev_kind_check = true, +}; diff --git a/src/network/netdev/wlan.h b/src/network/netdev/wlan.h new file mode 100644 index 000000000..bcc2dbcfd --- /dev/null +++ b/src/network/netdev/wlan.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "conf-parser.h" +#include "netdev.h" + +typedef struct WLan { + NetDev meta; + + char *wiphy_name; + uint32_t wiphy_index; + enum nl80211_iftype iftype; + int wds; /* tristate */ +} WLan; + +DEFINE_NETDEV_CAST(WLAN, WLan); +extern const NetDevVTable wlan_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_wiphy); +CONFIG_PARSER_PROTOTYPE(config_parse_wlan_iftype); diff --git a/src/network/netdev/xfrm.c b/src/network/netdev/xfrm.c index ef5e735b2..a961d8fef 100644 --- a/src/network/netdev/xfrm.c +++ b/src/network/netdev/xfrm.c @@ -19,11 +19,11 @@ static int xfrm_fill_message_create(NetDev *netdev, Link *link, sd_netlink_messa r = sd_netlink_message_append_u32(message, IFLA_XFRM_LINK, link ? link->ifindex : LOOPBACK_IFINDEX); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_XFRM_LINK: %m"); + return r; r = sd_netlink_message_append_u32(message, IFLA_XFRM_IF_ID, x->if_id); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_XFRM_IF_ID: %m"); + return r; return 0; } diff --git a/src/network/networkctl.c b/src/network/networkctl.c index 68dd4b185..d656027ec 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -79,17 +79,12 @@ static bool arg_full = false; static unsigned arg_lines = 10; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; -static int get_description(JsonVariant **ret) { +static int get_description(sd_bus *bus, JsonVariant **ret) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; const char *text = NULL; int r; - r = sd_bus_open_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to connect system bus: %m"); - r = bus_call_method(bus, bus_network_mgr, "Describe", &error, &reply, NULL); if (r < 0) return log_error_errno(r, "Failed to get description: %s", bus_error_message(&error, r)); @@ -105,11 +100,11 @@ static int get_description(JsonVariant **ret) { return 0; } -static int dump_manager_description(void) { +static int dump_manager_description(sd_bus *bus) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; int r; - r = get_description(&v); + r = get_description(bus, &v); if (r < 0) return r; @@ -117,14 +112,14 @@ static int dump_manager_description(void) { return 0; } -static int dump_link_description(char **patterns) { +static int dump_link_description(sd_bus *bus, char **patterns) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; _cleanup_free_ bool *matched_patterns = NULL; JsonVariant *i; size_t c = 0; int r; - r = get_description(&v); + r = get_description(bus, &v); if (r < 0) return r; @@ -268,7 +263,7 @@ typedef struct VxLanInfo { typedef struct LinkInfo { char name[IFNAMSIZ+1]; - char netdev_kind[NETDEV_KIND_MAX]; + char *netdev_kind; sd_device *sd_device; int ifindex; unsigned short iftype; @@ -369,6 +364,7 @@ static int link_info_compare(const LinkInfo *a, const LinkInfo *b) { static LinkInfo* link_info_array_free(LinkInfo *array) { for (unsigned i = 0; array && array[i].needs_freeing; i++) { sd_device_unref(array[i].sd_device); + free(array[i].netdev_kind); free(array[i].ssid); free(array[i].qdisc); strv_free(array[i].alternative_names); @@ -379,7 +375,6 @@ static LinkInfo* link_info_array_free(LinkInfo *array) { DEFINE_TRIVIAL_CLEANUP_FUNC(LinkInfo*, link_info_array_free); static int decode_netdev(sd_netlink_message *m, LinkInfo *info) { - const char *received_kind; int r; assert(m); @@ -389,15 +384,17 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) { if (r < 0) return r; - r = sd_netlink_message_read_string(m, IFLA_INFO_KIND, &received_kind); - if (r < 0) + r = sd_netlink_message_read_string_strdup(m, IFLA_INFO_KIND, &info->netdev_kind); + if (r < 0) { + (void) sd_netlink_message_exit_container(m); return r; + } r = sd_netlink_message_enter_container(m, IFLA_INFO_DATA); if (r < 0) return r; - if (streq(received_kind, "bridge")) { + if (streq(info->netdev_kind, "bridge")) { (void) sd_netlink_message_read_u32(m, IFLA_BR_FORWARD_DELAY, &info->forward_delay); (void) sd_netlink_message_read_u32(m, IFLA_BR_HELLO_TIME, &info->hello_time); (void) sd_netlink_message_read_u32(m, IFLA_BR_MAX_AGE, &info->max_age); @@ -407,12 +404,12 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) { (void) sd_netlink_message_read_u16(m, IFLA_BR_PRIORITY, &info->priority); (void) sd_netlink_message_read_u8(m, IFLA_BR_MCAST_IGMP_VERSION, &info->mcast_igmp_version); (void) sd_netlink_message_read_u8(m, IFLA_BRPORT_STATE, &info->port_state); - } if (streq(received_kind, "bond")) { + } if (streq(info->netdev_kind, "bond")) { (void) sd_netlink_message_read_u8(m, IFLA_BOND_MODE, &info->mode); (void) sd_netlink_message_read_u32(m, IFLA_BOND_MIIMON, &info->miimon); (void) sd_netlink_message_read_u32(m, IFLA_BOND_DOWNDELAY, &info->downdelay); (void) sd_netlink_message_read_u32(m, IFLA_BOND_UPDELAY, &info->updelay); - } else if (streq(received_kind, "vxlan")) { + } else if (streq(info->netdev_kind, "vxlan")) { (void) sd_netlink_message_read_u32(m, IFLA_VXLAN_ID, &info->vxlan_info.vni); r = sd_netlink_message_read_in_addr(m, IFLA_VXLAN_GROUP, &info->vxlan_info.group.in); @@ -442,12 +439,12 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) { (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_L2MISS, &info->vxlan_info.l2miss); (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_TOS, &info->vxlan_info.tos); (void) sd_netlink_message_read_u8(m, IFLA_VXLAN_TTL, &info->vxlan_info.ttl); - } else if (streq(received_kind, "vlan")) + } else if (streq(info->netdev_kind, "vlan")) (void) sd_netlink_message_read_u16(m, IFLA_VLAN_ID, &info->vlan_id); - else if (STR_IN_SET(received_kind, "ipip", "sit")) { + else if (STR_IN_SET(info->netdev_kind, "ipip", "sit")) { (void) sd_netlink_message_read_in_addr(m, IFLA_IPTUN_LOCAL, &info->local.in); (void) sd_netlink_message_read_in_addr(m, IFLA_IPTUN_REMOTE, &info->remote.in); - } else if (streq(received_kind, "geneve")) { + } else if (streq(info->netdev_kind, "geneve")) { (void) sd_netlink_message_read_u32(m, IFLA_GENEVE_ID, &info->vni); r = sd_netlink_message_read_in_addr(m, IFLA_GENEVE_REMOTE, &info->remote.in); @@ -465,27 +462,25 @@ static int decode_netdev(sd_netlink_message *m, LinkInfo *info) { (void) sd_netlink_message_read_u8(m, IFLA_GENEVE_UDP_ZERO_CSUM6_RX, &info->csum6_rx); (void) sd_netlink_message_read_u16(m, IFLA_GENEVE_PORT, &info->tunnel_port); (void) sd_netlink_message_read_u32(m, IFLA_GENEVE_LABEL, &info->label); - } else if (STR_IN_SET(received_kind, "gre", "gretap", "erspan")) { + } else if (STR_IN_SET(info->netdev_kind, "gre", "gretap", "erspan")) { (void) sd_netlink_message_read_in_addr(m, IFLA_GRE_LOCAL, &info->local.in); (void) sd_netlink_message_read_in_addr(m, IFLA_GRE_REMOTE, &info->remote.in); - } else if (STR_IN_SET(received_kind, "ip6gre", "ip6gretap", "ip6erspan")) { + } else if (STR_IN_SET(info->netdev_kind, "ip6gre", "ip6gretap", "ip6erspan")) { (void) sd_netlink_message_read_in6_addr(m, IFLA_GRE_LOCAL, &info->local.in6); (void) sd_netlink_message_read_in6_addr(m, IFLA_GRE_REMOTE, &info->remote.in6); - } else if (streq(received_kind, "vti")) { + } else if (streq(info->netdev_kind, "vti")) { (void) sd_netlink_message_read_in_addr(m, IFLA_VTI_LOCAL, &info->local.in); (void) sd_netlink_message_read_in_addr(m, IFLA_VTI_REMOTE, &info->remote.in); - } else if (streq(received_kind, "vti6")) { + } else if (streq(info->netdev_kind, "vti6")) { (void) sd_netlink_message_read_in6_addr(m, IFLA_VTI_LOCAL, &info->local.in6); (void) sd_netlink_message_read_in6_addr(m, IFLA_VTI_REMOTE, &info->remote.in6); - } else if (STR_IN_SET(received_kind, "macvlan", "macvtap")) + } else if (STR_IN_SET(info->netdev_kind, "macvlan", "macvtap")) (void) sd_netlink_message_read_u32(m, IFLA_MACVLAN_MODE, &info->macvlan_mode); - else if (streq(received_kind, "ipvlan")) { + else if (streq(info->netdev_kind, "ipvlan")) { (void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_MODE, &info->ipvlan_mode); (void) sd_netlink_message_read_u16(m, IFLA_IPVLAN_FLAGS, &info->ipvlan_flags); } - strncpy(info->netdev_kind, received_kind, IFNAMSIZ); - (void) sd_netlink_message_exit_container(m); (void) sd_netlink_message_exit_container(m); @@ -530,7 +525,6 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns, b if (!strv_fnmatch_full(patterns, str, 0, &pos) && !strv_fnmatch_full(patterns, name, 0, &pos)) { bool match = false; - char **p; STRV_FOREACH(p, altnames) if (strv_fnmatch_full(patterns, *p, 0, &pos)) { @@ -618,11 +612,12 @@ static int link_get_property( sd_bus_message **reply, const char *iface, const char *propname) { - _cleanup_free_ char *path = NULL, *ifindex_str = NULL; + + char ifindex_str[DECIMAL_STR_MAX(int)]; + _cleanup_free_ char *path = NULL; int r; - if (asprintf(&ifindex_str, "%i", link->ifindex) < 0) - return -ENOMEM; + xsprintf(ifindex_str, "%i", link->ifindex); r = sd_bus_path_encode("/org/freedesktop/network1/link", ifindex_str, &path); if (r < 0) @@ -790,6 +785,7 @@ static int acquire_link_info(sd_bus *bus, sd_netlink *rtnl, char **patterns, Lin } static int list_links(int argc, char *argv[], void *userdata) { + sd_bus *bus = ASSERT_PTR(userdata); _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; _cleanup_(link_info_array_freep) LinkInfo *links = NULL; _cleanup_(table_unrefp) Table *table = NULL; @@ -798,9 +794,9 @@ static int list_links(int argc, char *argv[], void *userdata) { if (arg_json_format_flags != JSON_FORMAT_OFF) { if (arg_all || argc <= 1) - return dump_manager_description(); + return dump_manager_description(bus); else - return dump_link_description(strv_skip(argv, 1)); + return dump_link_description(bus, strv_skip(argv, 1)); } r = sd_netlink_open(&rtnl); @@ -1223,12 +1219,10 @@ static int list_address_labels(int argc, char *argv[], void *userdata) { } static int open_lldp_neighbors(int ifindex, FILE **ret) { - _cleanup_free_ char *p = NULL; + char p[STRLEN("/run/systemd/netif/lldp/") + DECIMAL_STR_MAX(int)]; FILE *f; - if (asprintf(&p, "/run/systemd/netif/lldp/%i", ifindex) < 0) - return -ENOMEM; - + xsprintf(p, "/run/systemd/netif/lldp/%i", ifindex); f = fopen(p, "re"); if (!f) return -errno; @@ -1556,7 +1550,7 @@ static int link_status_one( _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **sip = NULL, **search_domains = NULL, **route_domains = NULL; _cleanup_free_ char *t = NULL, *network = NULL, *iaid = NULL, *duid = NULL, - *setup_state = NULL, *operational_state = NULL, *online_state = NULL, *lease_file = NULL, *activation_policy = NULL; + *setup_state = NULL, *operational_state = NULL, *online_state = NULL, *activation_policy = NULL; const char *driver = NULL, *path = NULL, *vendor = NULL, *model = NULL, *link = NULL, *on_color_operational, *off_color_operational, *on_color_setup, *off_color_setup, *on_color_online; _cleanup_free_ int *carrier_bound_to = NULL, *carrier_bound_by = NULL; @@ -1606,8 +1600,8 @@ static int link_status_one( (void) sd_network_link_get_carrier_bound_to(info->ifindex, &carrier_bound_to); (void) sd_network_link_get_carrier_bound_by(info->ifindex, &carrier_bound_by); - if (asprintf(&lease_file, "/run/systemd/netif/leases/%d", info->ifindex) < 0) - return log_oom(); + char lease_file[STRLEN("/run/systemd/netif/leases/") + DECIMAL_STR_MAX(int)]; + xsprintf(lease_file, "/run/systemd/netif/leases/%i", info->ifindex); (void) dhcp_lease_load(&lease, lease_file); @@ -1649,6 +1643,9 @@ static int link_status_one( TABLE_STRING, "Type:", TABLE_STRING, strna(t), TABLE_EMPTY, + TABLE_STRING, "Kind:", + TABLE_STRING, strna(info->netdev_kind), + TABLE_EMPTY, TABLE_STRING, "State:"); if (r < 0) return table_log_add_error(r); @@ -2383,7 +2380,7 @@ static int system_status(sd_netlink *rtnl, sd_hwdb *hwdb) { } static int link_status(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + sd_bus *bus = ASSERT_PTR(userdata); _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; _cleanup_(link_info_array_freep) LinkInfo *links = NULL; @@ -2391,17 +2388,13 @@ static int link_status(int argc, char *argv[], void *userdata) { if (arg_json_format_flags != JSON_FORMAT_OFF) { if (arg_all || argc <= 1) - return dump_manager_description(); + return dump_manager_description(bus); else - return dump_link_description(strv_skip(argv, 1)); + return dump_link_description(bus, strv_skip(argv, 1)); } pager_open(arg_pager_flags); - r = sd_bus_open_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to connect system bus: %m"); - r = sd_netlink_open(&rtnl); if (r < 0) return log_error_errno(r, "Failed to connect to netlink: %m"); @@ -2738,14 +2731,10 @@ static int link_renew_one(sd_bus *bus, int index, const char *name) { } static int link_renew(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + sd_bus *bus = ASSERT_PTR(userdata); _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; int index, k = 0, r; - r = sd_bus_open_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to connect system bus: %m"); - for (int i = 1; i < argc; i++) { index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]); if (index < 0) @@ -2772,14 +2761,10 @@ static int link_force_renew_one(sd_bus *bus, int index, const char *name) { } static int link_force_renew(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + sd_bus *bus = ASSERT_PTR(userdata); _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; int k = 0, r; - r = sd_bus_open_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to connect system bus: %m"); - for (int i = 1; i < argc; i++) { int index = rtnl_resolve_interface_or_warn(&rtnl, argv[i]); if (index < 0) @@ -2794,14 +2779,10 @@ static int link_force_renew(int argc, char *argv[], void *userdata) { } static int verb_reload(int argc, char *argv[], void *userdata) { + sd_bus *bus = ASSERT_PTR(userdata); _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; - r = sd_bus_open_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to connect system bus: %m"); - r = bus_call_method(bus, bus_network_mgr, "Reload", &error, NULL, NULL); if (r < 0) return log_error_errno(r, "Failed to reload network settings: %m"); @@ -2810,17 +2791,13 @@ static int verb_reload(int argc, char *argv[], void *userdata) { } static int verb_reconfigure(int argc, char *argv[], void *userdata) { + sd_bus *bus = ASSERT_PTR(userdata); _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; _cleanup_set_free_ Set *indexes = NULL; int index, r; void *p; - r = sd_bus_open_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to connect system bus: %m"); - indexes = set_new(NULL); if (!indexes) return log_oom(); @@ -2968,7 +2945,7 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int networkctl_main(int argc, char *argv[]) { +static int networkctl_main(sd_bus *bus, int argc, char *argv[]) { static const Verb verbs[] = { { "list", VERB_ANY, VERB_ANY, VERB_DEFAULT, list_links }, { "status", VERB_ANY, VERB_ANY, 0, link_status }, @@ -2984,20 +2961,15 @@ static int networkctl_main(int argc, char *argv[]) { {} }; - return dispatch_verb(argc, argv, verbs, NULL); + return dispatch_verb(argc, argv, verbs, bus); } -static int check_netns_match(void) { +static int check_netns_match(sd_bus *bus) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; struct stat st; uint64_t id; int r; - r = sd_bus_open_system(&bus); - if (r < 0) - return log_error_errno(r, "Failed to connect system bus: %m"); - r = sd_bus_get_property_trivial( bus, "org.freedesktop.network1", @@ -3035,6 +3007,7 @@ static void warn_networkd_missing(void) { } static int run(int argc, char* argv[]) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; int r; log_setup(); @@ -3043,13 +3016,17 @@ static int run(int argc, char* argv[]) { if (r <= 0) return r; - r = check_netns_match(); + r = sd_bus_open_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to connect system bus: %m"); + + r = check_netns_match(bus); if (r < 0) return r; warn_networkd_missing(); - return networkctl_main(argc, argv); + return networkctl_main(bus, argc, argv); } DEFINE_MAIN_FUNCTION(run); diff --git a/src/network/networkd-address-label.c b/src/network/networkd-address-label.c index 6f911b805..745b959f7 100644 --- a/src/network/networkd-address-label.c +++ b/src/network/networkd-address-label.c @@ -21,14 +21,14 @@ AddressLabel *address_label_free(AddressLabel *label) { hashmap_remove(label->network->address_labels_by_section, label->section); } - network_config_section_free(label->section); + config_section_free(label->section); return mfree(label); } -DEFINE_NETWORK_SECTION_FUNCTIONS(AddressLabel, address_label_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(AddressLabel, address_label_free); static int address_label_new_static(Network *network, const char *filename, unsigned section_line, AddressLabel **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(address_label_freep) AddressLabel *label = NULL; int r; @@ -37,7 +37,7 @@ static int address_label_new_static(Network *network, const char *filename, unsi assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -57,7 +57,7 @@ static int address_label_new_static(Network *network, const char *filename, unsi .label = UINT32_MAX, }; - r = hashmap_ensure_put(&network->address_labels_by_section, &network_config_hash_ops, label->section, label); + r = hashmap_ensure_put(&network->address_labels_by_section, &config_section_hash_ops, label->section, label); if (r < 0) return r; @@ -65,19 +65,17 @@ static int address_label_new_static(Network *network, const char *filename, unsi return 0; } -static int address_label_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int address_label_configure_handler( + sd_netlink *rtnl, + sd_netlink_message *m, + Request *req, + Link *link, + void *userdata) { + int r; - assert(rtnl); assert(m); assert(link); - assert(link->ifname); - assert(link->static_address_label_messages > 0); - - link->static_address_label_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) { @@ -95,8 +93,8 @@ static int address_label_configure_handler(sd_netlink *rtnl, sd_netlink_message return 1; } -static int address_label_configure(AddressLabel *label, Link *link, link_netlink_message_handler_t callback) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int address_label_configure(AddressLabel *label, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert(label); @@ -104,31 +102,41 @@ static int address_label_configure(AddressLabel *label, Link *link, link_netlink assert(link->ifindex > 0); assert(link->manager); assert(link->manager->rtnl); - assert(callback); + assert(req); - r = sd_rtnl_message_new_addrlabel(link->manager->rtnl, &req, RTM_NEWADDRLABEL, + r = sd_rtnl_message_new_addrlabel(link->manager->rtnl, &m, RTM_NEWADDRLABEL, link->ifindex, AF_INET6); if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_NEWADDR message: %m"); + return r; - r = sd_rtnl_message_addrlabel_set_prefixlen(req, label->prefixlen); + r = sd_rtnl_message_addrlabel_set_prefixlen(m, label->prefixlen); if (r < 0) - return log_link_error_errno(link, r, "Could not set prefixlen: %m"); + return r; - r = sd_netlink_message_append_u32(req, IFAL_LABEL, label->label); + r = sd_netlink_message_append_u32(m, IFAL_LABEL, label->label); if (r < 0) - return log_link_error_errno(link, r, "Could not append IFAL_LABEL attribute: %m"); + return r; - r = sd_netlink_message_append_in6_addr(req, IFA_ADDRESS, &label->prefix); + r = sd_netlink_message_append_in6_addr(m, IFA_ADDRESS, &label->prefix); if (r < 0) - return log_link_error_errno(link, r, "Could not append IFA_ADDRESS attribute: %m"); + return r; - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, - link_netlink_destroy_callback, link); + return request_call_netlink_async(link->manager->rtnl, m, req); +} + +static int address_label_process_request(Request *req, Link *link, void *userdata) { + AddressLabel *label = ASSERT_PTR(userdata); + int r; + + assert(req); + assert(link); + + if (!link_is_ready_to_configure(link, false)) + return 0; + + r = address_label_configure(label, link, req); if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); + return log_link_warning_errno(link, r, "Failed to configure address label: %m"); return 1; } @@ -143,8 +151,11 @@ int link_request_static_address_labels(Link *link) { link->static_address_labels_configured = false; HASHMAP_FOREACH(label, link->network->address_labels_by_section) { - r = link_queue_request(link, REQUEST_TYPE_ADDRESS_LABEL, label, false, - &link->static_address_label_messages, address_label_configure_handler, NULL); + r = link_queue_request_full(link, REQUEST_TYPE_ADDRESS_LABEL, + label, NULL, trivial_hash_func, trivial_compare_func, + address_label_process_request, + &link->static_address_label_messages, + address_label_configure_handler, NULL); if (r < 0) return log_link_warning_errno(link, r, "Failed to request address label: %m"); } @@ -160,18 +171,6 @@ int link_request_static_address_labels(Link *link) { return 0; } -int request_process_address_label(Request *req) { - assert(req); - assert(req->link); - assert(req->label); - assert(req->type == REQUEST_TYPE_ADDRESS_LABEL); - - if (!link_is_ready_to_configure(req->link, false)) - return 0; - - return address_label_configure(req->label, req->link, req->netlink_handler); -} - static int address_label_section_verify(AddressLabel *label) { assert(label); assert(label->section); diff --git a/src/network/networkd-address-label.h b/src/network/networkd-address-label.h index 0f975454d..1e2ee70de 100644 --- a/src/network/networkd-address-label.h +++ b/src/network/networkd-address-label.h @@ -9,11 +9,10 @@ typedef struct Link Link; typedef struct Network Network; -typedef struct Request Request; typedef struct AddressLabel { Network *network; - NetworkConfigSection *section; + ConfigSection *section; uint32_t label; struct in6_addr prefix; @@ -26,7 +25,6 @@ AddressLabel *address_label_free(AddressLabel *label); void network_drop_invalid_address_labels(Network *network); int link_request_static_address_labels(Link *link); -int request_process_address_label(Request *req); CONFIG_PARSER_PROTOTYPE(config_parse_address_label); CONFIG_PARSER_PROTOTYPE(config_parse_address_label_prefix); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 7df743efb..afcd53cbc 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -68,7 +68,6 @@ int address_new(Address **ret) { .lifetime_valid_usec = USEC_INFINITY, .lifetime_preferred_usec = USEC_INFINITY, .set_broadcast = -1, - .duplicate_address_detection = ADDRESS_FAMILY_IPV6, }; *ret = TAKE_PTR(address); @@ -77,7 +76,7 @@ int address_new(Address **ret) { } static int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(address_freep) Address *address = NULL; int r; @@ -86,7 +85,7 @@ static int address_new_static(Network *network, const char *filename, unsigned s assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -106,8 +105,10 @@ static int address_new_static(Network *network, const char *filename, unsigned s address->network = network; address->section = TAKE_PTR(n); address->source = NETWORK_CONFIG_SOURCE_STATIC; + /* This will be adjusted in address_section_verify(). */ + address->duplicate_address_detection = _ADDRESS_FAMILY_INVALID; - r = ordered_hashmap_ensure_put(&network->addresses_by_section, &network_config_hash_ops, address->section, address); + r = ordered_hashmap_ensure_put(&network->addresses_by_section, &config_section_hash_ops, address->section, address); if (r < 0) return r; @@ -134,7 +135,7 @@ Address *address_free(Address *address) { sd_ipv4acd_unref(address->acd); - network_config_section_free(address->section); + config_section_free(address->section); free(address->label); return mfree(address); } @@ -174,8 +175,9 @@ void link_mark_addresses(Link *link, NetworkConfigSource source, const struct in } } -static bool address_may_have_broadcast(const Address *a) { +static bool address_needs_to_set_broadcast(const Address *a, Link *link) { assert(a); + assert(link); if (a->family != AF_INET) return false; @@ -188,38 +190,26 @@ static bool address_may_have_broadcast(const Address *a) { if (a->prefixlen > 30) return false; + /* If explicitly configured, do not update the address. */ + if (in4_addr_is_set(&a->broadcast)) + return false; + if (a->set_broadcast >= 0) return a->set_broadcast; - return true; /* Defaults to true. */ + /* Defaults to true, except for wireguard, as typical configuration for wireguard does not set + * broadcast. */ + return !streq_ptr(link->kind, "wireguard"); } -void address_set_broadcast(Address *a) { - assert(a); - - if (!address_may_have_broadcast(a)) - return; - - /* If explicitly configured, do not update the address. */ - if (in4_addr_is_set(&a->broadcast)) - return; - - /* If Address= is 0.0.0.0, then the broadcast address will be set later in address_acquire(). */ - if (in4_addr_is_null(&a->in_addr.in)) - return; - - a->broadcast.s_addr = a->in_addr.in.s_addr | htobe32(UINT32_C(0xffffffff) >> a->prefixlen); -} - -static bool address_may_set_broadcast(const Address *a, const Link *link) { +void address_set_broadcast(Address *a, Link *link) { assert(a); assert(link); - if (!address_may_have_broadcast(a)) - return false; + if (!address_needs_to_set_broadcast(a, link)) + return; - /* Typical configuration for wireguard does not set broadcast. */ - return !streq_ptr(link->kind, "wireguard"); + a->broadcast.s_addr = a->in_addr.in.s_addr | htobe32(UINT32_C(0xffffffff) >> a->prefixlen); } static struct ifa_cacheinfo *address_set_cinfo(const Address *a, struct ifa_cacheinfo *cinfo) { @@ -228,7 +218,7 @@ static struct ifa_cacheinfo *address_set_cinfo(const Address *a, struct ifa_cach assert(a); assert(cinfo); - now_usec = now(clock_boottime_or_monotonic()); + now_usec = now(CLOCK_BOOTTIME); *cinfo = (struct ifa_cacheinfo) { .ifa_valid = MIN(usec_sub_unsigned(a->lifetime_valid_usec, now_usec) / USEC_PER_SEC, UINT32_MAX), @@ -244,7 +234,7 @@ static void address_set_lifetime(Address *a, const struct ifa_cacheinfo *cinfo) assert(a); assert(cinfo); - now_usec = now(clock_boottime_or_monotonic()); + now_usec = now(CLOCK_BOOTTIME); if (cinfo->ifa_valid == UINT32_MAX) a->lifetime_valid_usec = USEC_INFINITY; @@ -271,7 +261,7 @@ static uint32_t address_prefix(const Address *a) { return be32toh(a->in_addr.in.s_addr) >> (32 - a->prefixlen); } -void address_hash_func(const Address *a, struct siphash *state) { +static void address_kernel_hash_func(const Address *a, struct siphash *state) { assert(a); siphash24_compress(&a->family, sizeof(a->family), state); @@ -293,7 +283,7 @@ void address_hash_func(const Address *a, struct siphash *state) { } } -int address_compare_func(const Address *a1, const Address *a2) { +static int address_kernel_compare_func(const Address *a1, const Address *a2) { int r; r = CMP(a1->family, a2->family); @@ -322,10 +312,68 @@ int address_compare_func(const Address *a1, const Address *a2) { } DEFINE_PRIVATE_HASH_OPS( - address_hash_ops, + address_kernel_hash_ops, Address, - address_hash_func, - address_compare_func); + address_kernel_hash_func, + address_kernel_compare_func); + +static void address_hash_func(const Address *a, struct siphash *state) { + assert(a); + + siphash24_compress(&a->family, sizeof(a->family), state); + + /* treat any other address family as AF_UNSPEC */ + if (!IN_SET(a->family, AF_INET, AF_INET6)) + return; + + siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state); + siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state); + siphash24_compress(&a->in_addr_peer, FAMILY_ADDRESS_SIZE(a->family), state); + + if (a->family == AF_INET) { + /* On update, the kernel ignores the address label and broadcast address, hence we need + * to distinguish addresses with different labels or broadcast addresses. Otherwise, + * the label or broadcast address change will not be applied when we reconfigure the + * interface. */ + siphash24_compress_string(a->label, state); + siphash24_compress(&a->broadcast, sizeof(a->broadcast), state); + } +} + +int address_compare_func(const Address *a1, const Address *a2) { + int r; + + r = CMP(a1->family, a2->family); + if (r != 0) + return r; + + if (!IN_SET(a1->family, AF_INET, AF_INET6)) + return 0; + + r = CMP(a1->prefixlen, a2->prefixlen); + if (r != 0) + return r; + + r = memcmp(&a1->in_addr, &a2->in_addr, FAMILY_ADDRESS_SIZE(a1->family)); + if (r != 0) + return r; + + r = memcmp(&a1->in_addr_peer, &a2->in_addr_peer, FAMILY_ADDRESS_SIZE(a1->family)); + if (r != 0) + return r; + + if (a1->family == AF_INET) { + r = strcmp_ptr(a1->label, a2->label); + if (r != 0) + return r; + + r = CMP(a1->broadcast.s_addr, a2->broadcast.s_addr); + if (r != 0) + return r; + } + + return 0; +} DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( address_hash_ops_free, @@ -496,94 +544,94 @@ int address_get(Link *link, const Address *in, Address **ret) { return 0; } -int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret) { - _cleanup_(address_freep) Address *a = NULL; - int r; - - assert(link); - assert(address); - - r = address_new(&a); - if (r < 0) - return r; - - /* address_compare_func() only compares the local address for IPv6 case. So, it is enough to - * set only family and the address. */ - a->family = AF_INET6; - a->in_addr.in6 = *address; - - return address_get(link, a, ret); -} - -int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret) { - int r; - - assert(link); - assert(address); - - if (prefixlen != 0) { - _cleanup_(address_freep) Address *a = NULL; - - /* If prefixlen is set, then we can use address_get(). */ - - r = address_new(&a); - if (r < 0) - return r; - - a->family = AF_INET; - a->in_addr.in = *address; - a->prefixlen = prefixlen; - - return address_get(link, a, ret); - } else { - Address *a; - - SET_FOREACH(a, link->addresses) { - if (a->family != AF_INET) - continue; - - if (!in4_addr_equal(&a->in_addr.in, address)) - continue; - - if (ret) - *ret = a; - - return 0; - } - - return -ENOENT; - } -} - -int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) { +int link_get_address(Link *link, int family, const union in_addr_union *address, unsigned char prefixlen, Address **ret) { Address *a; - Link *link; int r; - assert(manager); + assert(link); assert(IN_SET(family, AF_INET, AF_INET6)); assert(address); - if (family == AF_INET) { - HASHMAP_FOREACH(link, manager->links_by_index) - if (link_get_ipv4_address(link, &address->in, 0, &a) >= 0) - return check_ready ? address_is_ready(a) : address_exists(a); - } else { + /* This find an Address object on the link which matches the given address and prefix length + * and does not have peer address. When the prefixlen is zero, then an Address object with an + * arbitrary prefixlen will be returned. */ + + if (prefixlen != 0) { _cleanup_(address_freep) Address *tmp = NULL; + /* If prefixlen is set, then we can use address_get(). */ + r = address_new(&tmp); if (r < 0) return r; tmp->family = family; tmp->in_addr = *address; + tmp->prefixlen = prefixlen; + address_set_broadcast(tmp, link); - HASHMAP_FOREACH(link, manager->links_by_index) - if (address_get(link, tmp, &a) >= 0) - return check_ready ? address_is_ready(a) : address_exists(a); + if (address_get(link, tmp, &a) >= 0) { + if (ret) + *ret = a; + + return 0; + } + + if (family == AF_INET6) + return -ENOENT; + + /* IPv4 addresses may have label and/or non-default broadcast address. + * Hence, we need to always fallback below. */ } - return false; + SET_FOREACH(a, link->addresses) { + if (a->family != family) + continue; + + if (!in_addr_equal(family, &a->in_addr, address)) + continue; + + if (in_addr_is_set(family, &a->in_addr_peer)) + continue; + + if (ret) + *ret = a; + + return 0; + } + + return -ENOENT; +} + +int manager_get_address(Manager *manager, int family, const union in_addr_union *address, unsigned char prefixlen, Address **ret) { + Link *link; + + assert(manager); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(address); + + HASHMAP_FOREACH(link, manager->links_by_index) { + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + continue; + + if (link_get_address(link, family, address, prefixlen, ret) >= 0) + return 0; + } + + return -ENOENT; +} + +bool manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) { + Address *a; + + assert(manager); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(address); + + if (manager_get_address(manager, family, address, 0, &a) < 0) + return false; + + return check_ready ? address_is_ready(a) : address_exists(a); } const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) { @@ -595,7 +643,7 @@ const char* format_lifetime(char *buf, size_t l, usec_t lifetime_usec) { sprintf(buf, "for "); /* format_timespan() never fails */ - assert_se(format_timespan(buf + 4, l - 4, usec_sub_unsigned(lifetime_usec, now(clock_boottime_or_monotonic())), USEC_PER_SEC)); + assert_se(format_timespan(buf + 4, l - 4, usec_sub_unsigned(lifetime_usec, now(CLOCK_BOOTTIME)), USEC_PER_SEC)); return buf; } @@ -625,36 +673,36 @@ static void log_address_debug(const Address *address, const char *str, const Lin strna(flags_str), strna(scope_str)); } -static int address_set_netlink_message(const Address *address, sd_netlink_message *req, Link *link) { +static int address_set_netlink_message(const Address *address, sd_netlink_message *m, Link *link) { uint32_t flags; int r; assert(address); - assert(req); + assert(m); assert(link); - r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); + r = sd_rtnl_message_addr_set_prefixlen(m, address->prefixlen); if (r < 0) - return log_link_error_errno(link, r, "Could not set prefixlen: %m"); + return r; /* On remove, only IFA_F_MANAGETEMPADDR flag for IPv6 addresses are used. But anyway, set all * flags except tentative flag here unconditionally. Without setting the flag, the template * addresses generated by kernel will not be removed automatically when the main address is * removed. */ flags = address->flags & ~IFA_F_TENTATIVE; - r = sd_rtnl_message_addr_set_flags(req, flags & 0xff); + r = sd_rtnl_message_addr_set_flags(m, flags & 0xff); if (r < 0) - return log_link_error_errno(link, r, "Could not set flags: %m"); + return r; if ((flags & ~0xff) != 0) { - r = sd_netlink_message_append_u32(req, IFA_FLAGS, flags); + r = sd_netlink_message_append_u32(m, IFA_FLAGS, flags); if (r < 0) - return log_link_error_errno(link, r, "Could not set extended flags: %m"); + return r; } - r = netlink_message_append_in_addr_union(req, IFA_LOCAL, address->family, &address->in_addr); + r = netlink_message_append_in_addr_union(m, IFA_LOCAL, address->family, &address->in_addr); if (r < 0) - return log_link_error_errno(link, r, "Could not append IFA_LOCAL attribute: %m"); + return r; return 0; } @@ -676,7 +724,7 @@ static int address_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link } int address_remove(Address *address) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; Link *link; int r; @@ -691,24 +739,28 @@ int address_remove(Address *address) { log_address_debug(address, "Removing", link); - r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR, + r = sd_rtnl_message_new_addr(link->manager->rtnl, &m, RTM_DELADDR, link->ifindex, address->family); if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_DELADDR message: %m"); + return log_link_warning_errno(link, r, "Could not allocate RTM_DELADDR message: %m"); - r = address_set_netlink_message(address, req, link); + r = address_set_netlink_message(address, m, link); if (r < 0) - return r; + return log_link_warning_errno(link, r, "Could not set netlink attributes: %m"); - r = netlink_call_async(link->manager->rtnl, NULL, req, + r = netlink_call_async(link->manager->rtnl, NULL, m, address_remove_handler, link_netlink_destroy_callback, link); if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + return log_link_warning_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); address_enter_removing(address); + + /* The operational state is determined by address state and carrier state. Hence, if we remove + * an address, the operational state may be changed. */ + link_update_operstate(link, true); return 0; } @@ -844,16 +896,16 @@ int link_drop_foreign_addresses(Link *link) { assert(link); assert(link->network); - /* Keep all addresses when KeepConfiguration=yes. */ - if (link->network->keep_configuration == KEEP_CONFIGURATION_YES) - return 0; - /* First, mark all addresses. */ SET_FOREACH(address, link->addresses) { /* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */ if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6)) continue; + /* Do not remove localhost address (127.0.0.1 and ::1) */ + if (link->flags & IFF_LOOPBACK && in_addr_is_localhost_one(address->family, &address->in_addr) > 0) + continue; + /* Ignore addresses we configured. */ if (address->source != NETWORK_CONFIG_SOURCE_FOREIGN) continue; @@ -891,22 +943,19 @@ int link_drop_foreign_addresses(Link *link) { return r; } -int link_drop_addresses(Link *link) { +int link_drop_managed_addresses(Link *link) { Address *address; int k, r = 0; assert(link); SET_FOREACH(address, link->addresses) { - /* Ignore addresses not assigned yet or already removing. */ - if (!address_exists(address)) + /* Do not touch addresses managed by kernel or other tools. */ + if (address->source == NETWORK_CONFIG_SOURCE_FOREIGN) continue; - /* Do not drop IPv6LL addresses assigned by the kernel here. They will be dropped in - * link_drop_ipv6ll_addresses() if IPv6LL addressing is disabled. */ - if (address->source == NETWORK_CONFIG_SOURCE_FOREIGN && - address->family == AF_INET6 && - in6_addr_is_link_local(&address->in_addr.in6)) + /* Ignore addresses not assigned yet or already removing. */ + if (!address_exists(address)) continue; k = address_remove(address); @@ -962,7 +1011,6 @@ static int address_acquire(Link *link, const Address *original, Address **ret) { return r; na->in_addr = in_addr; - address_set_broadcast(na); *ret = TAKE_PTR(na); return 1; @@ -976,9 +1024,6 @@ int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, assert(link); assert(error_msg); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 0; - r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { log_link_message_warning_errno(link, m, r, error_msg); @@ -989,12 +1034,8 @@ int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, return 1; } -static int address_configure( - const Address *address, - Link *link, - link_netlink_message_handler_t callback) { - - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int address_configure(const Address *address, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert(address); @@ -1003,93 +1044,82 @@ static int address_configure( assert(link->ifindex > 0); assert(link->manager); assert(link->manager->rtnl); - assert(callback); + assert(req); log_address_debug(address, "Configuring", link); - r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req, - link->ifindex, address->family); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_NEWADDR message: %m"); - - r = address_set_netlink_message(address, req, link); + r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &m, link->ifindex, address->family); if (r < 0) return r; - r = sd_rtnl_message_addr_set_scope(req, address->scope); + r = address_set_netlink_message(address, m, link); if (r < 0) - return log_link_error_errno(link, r, "Could not set scope: %m"); + return r; + + r = sd_rtnl_message_addr_set_scope(m, address->scope); + if (r < 0) + return r; if (in_addr_is_set(address->family, &address->in_addr_peer)) { - r = netlink_message_append_in_addr_union(req, IFA_ADDRESS, address->family, &address->in_addr_peer); + r = netlink_message_append_in_addr_union(m, IFA_ADDRESS, address->family, &address->in_addr_peer); if (r < 0) - return log_link_error_errno(link, r, "Could not append IFA_ADDRESS attribute: %m"); - } else if (address_may_set_broadcast(address, link)) { - r = sd_netlink_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast); + return r; + } else if (in4_addr_is_set(&address->broadcast)) { + r = sd_netlink_message_append_in_addr(m, IFA_BROADCAST, &address->broadcast); if (r < 0) - return log_link_error_errno(link, r, "Could not append IFA_BROADCAST attribute: %m"); + return r; } if (address->family == AF_INET && address->label) { - r = sd_netlink_message_append_string(req, IFA_LABEL, address->label); + r = sd_netlink_message_append_string(m, IFA_LABEL, address->label); if (r < 0) - return log_link_error_errno(link, r, "Could not append IFA_LABEL attribute: %m"); + return r; } - r = sd_netlink_message_append_cache_info(req, IFA_CACHEINFO, + r = sd_netlink_message_append_cache_info(m, IFA_CACHEINFO, address_set_cinfo(address, &(struct ifa_cacheinfo) {})); if (r < 0) - return log_link_error_errno(link, r, "Could not append IFA_CACHEINFO attribute: %m"); - - r = sd_netlink_message_append_u32(req, IFA_RT_PRIORITY, address->route_metric); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFA_RT_PRIORITY attribute: %m"); - - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - return 0; -} - -void address_cancel_request(Address *address) { - Request req; - - assert(address); - assert(address->link); - - if (!address_is_requesting(address)) - return; - - req = (Request) { - .link = address->link, - .type = REQUEST_TYPE_ADDRESS, - .address = address, - }; - - request_drop(ordered_set_get(address->link->manager->request_queue, &req)); - address_cancel_requesting(address); -} - -static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - int r; - - assert(link); - assert(link->static_address_messages > 0); - - link->static_address_messages--; - - r = address_configure_handler_internal(rtnl, m, link, "Failed to set static address"); - if (r <= 0) return r; - if (link->static_address_messages == 0) { - log_link_debug(link, "Addresses set"); - link->static_addresses_configured = true; - link_check_ready(link); - } + r = sd_netlink_message_append_u32(m, IFA_RT_PRIORITY, address->route_metric); + if (r < 0) + return r; + return request_call_netlink_async(link->manager->rtnl, m, req); +} + +static bool address_is_ready_to_configure(Link *link, const Address *address) { + assert(link); + assert(address); + + if (!link_is_ready_to_configure(link, false)) + return false; + + if (FLAGS_SET(address->state, NETWORK_CONFIG_STATE_PROBING)) + return false; + + /* Refuse adding more than the limit */ + if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) + return false; + + return true; +} + +static int address_process_request(Request *req, Link *link, Address *address) { + int r; + + assert(req); + assert(link); + assert(address); + + if (!address_is_ready_to_configure(link, address)) + return 0; + + r = address_configure(address, link, req); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure address: %m"); + + address_enter_configuring(address); return 1; } @@ -1098,7 +1128,7 @@ int link_request_address( Address *address, bool consume_object, unsigned *message_counter, - link_netlink_message_handler_t netlink_handler, + address_netlink_handler_t netlink_handler, Request **ret) { Address *acquired, *existing; @@ -1119,6 +1149,21 @@ int link_request_address( consume_object = true; } + if (address_needs_to_set_broadcast(address, link)) { + if (!consume_object) { + Address *a; + + r = address_dup(address, &a); + if (r < 0) + return r; + + address = a; + consume_object = true; + } + + address_set_broadcast(address, link); + } + if (address_get(link, address, &existing) < 0) { _cleanup_(address_freep) Address *tmp = NULL; @@ -1152,8 +1197,12 @@ int link_request_address( return r; log_address_debug(existing, "Requesting", link); - r = link_queue_request(link, REQUEST_TYPE_ADDRESS, existing, false, - message_counter, netlink_handler, ret); + r = link_queue_request_safe(link, REQUEST_TYPE_ADDRESS, + existing, NULL, + address_hash_func, + address_compare_func, + address_process_request, + message_counter, netlink_handler, ret); if (r < 0) return log_link_warning_errno(link, r, "Failed to request address: %m"); if (r == 0) @@ -1164,6 +1213,24 @@ int link_request_address( return 1; } +static int static_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) { + int r; + + assert(link); + + r = address_configure_handler_internal(rtnl, m, link, "Failed to set static address"); + if (r <= 0) + return r; + + if (link->static_address_messages == 0) { + log_link_debug(link, "Addresses set"); + link->static_addresses_configured = true; + link_check_ready(link); + } + + return 1; +} + int link_request_static_address(Link *link, Address *address, bool consume) { assert(link); assert(address); @@ -1207,49 +1274,25 @@ int link_request_static_addresses(Link *link) { return 0; } -static bool address_is_ready_to_configure(Link *link, const Address *address) { - assert(link); +void address_cancel_request(Address *address) { + Request req; + assert(address); + assert(address->link); - if (!link_is_ready_to_configure(link, false)) - return false; + if (!address_is_requesting(address)) + return; - if (FLAGS_SET(address->state, NETWORK_CONFIG_STATE_PROBING)) - return false; + req = (Request) { + .link = address->link, + .type = REQUEST_TYPE_ADDRESS, + .userdata = address, + .hash_func = (hash_func_t) address_hash_func, + .compare_func = (compare_func_t) address_compare_func, + }; - /* Refuse adding more than the limit */ - if (set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) - return false; - - return true; -} - -int request_process_address(Request *req) { - Address *existing; - Link *link; - int r; - - assert(req); - assert(req->link); - assert(req->address); - assert(req->type == REQUEST_TYPE_ADDRESS); - - link = req->link; - - r = address_get(link, req->address, &existing); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to get address: %m"); - - if (!address_is_ready_to_configure(link, existing)) - return 0; - - r = address_configure(req->address, link, req->netlink_handler); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to configure address: %m"); - - address_enter_configuring(existing); - - return 1; + request_detach(address->link->manager, &req); + address_cancel_requesting(address); } int manager_rtnl_process_address(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { @@ -1909,10 +1952,13 @@ static int address_section_verify(Address *address) { address->section->filename, address->section->line); } - if (address_may_have_broadcast(address)) - address_set_broadcast(address); - else if (address->broadcast.s_addr != 0) { - log_warning("%s: broadcast address is set for IPv6 address or IPv4 address with prefixlength larger than 30. " + assert(IN_SET(address->family, AF_INET, AF_INET6)); + + if (in4_addr_is_set(&address->broadcast) && + (address->family == AF_INET6 || address->prefixlen > 30 || + in_addr_is_set(address->family, &address->in_addr_peer))) { + log_warning("%s: broadcast address is set for an IPv6 address, " + "an IPv4 address with peer address, or with prefix length larger than 30. " "Ignoring Broadcast= setting in the [Address] section from line %u.", address->section->filename, address->section->line); @@ -1934,17 +1980,24 @@ static int address_section_verify(Address *address) { address->scope = RT_SCOPE_LINK; } + if (address->duplicate_address_detection < 0) { + if (address->family == AF_INET6) + address->duplicate_address_detection = ADDRESS_FAMILY_IPV6; + else if (in4_addr_is_link_local(&address->in_addr.in)) + address->duplicate_address_detection = ADDRESS_FAMILY_IPV4; + else + address->duplicate_address_detection = ADDRESS_FAMILY_NO; + } else if (address->duplicate_address_detection == ADDRESS_FAMILY_IPV6 && address->family == AF_INET) + log_warning("%s: DuplicateAddressDetection=ipv6 is specified for IPv4 address, ignoring.", + address->section->filename); + else if (address->duplicate_address_detection == ADDRESS_FAMILY_IPV4 && address->family == AF_INET6) + log_warning("%s: DuplicateAddressDetection=ipv4 is specified for IPv6 address, ignoring.", + address->section->filename); + if (address->family == AF_INET6 && !FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV6)) address->flags |= IFA_F_NODAD; - if (address->family == AF_INET && in4_addr_is_link_local(&address->in_addr.in) && - !FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) { - log_debug("%s: An IPv4 link-local address is specified, enabling IPv4 Address Conflict Detection (ACD).", - address->section->filename); - address->duplicate_address_detection |= ADDRESS_FAMILY_IPV4; - } - return 0; } @@ -1979,8 +2032,10 @@ int network_drop_invalid_addresses(Network *network) { address_free(dup); } - /* Do not use address_hash_ops_free here. Otherwise, all address settings will be freed. */ - r = set_ensure_put(&addresses, &address_hash_ops, address); + /* Use address_kernel_hash_ops here. The function address_kernel_compare_func() matches + * how kernel compares addresses, and is more lenient than address_compare_func(). + * Hence, the logic of dedup here is stricter than when address_hash_ops is used. */ + r = set_ensure_put(&addresses, &address_kernel_hash_ops, address); if (r < 0) return log_oom(); assert(r > 0); diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 41c4ce6fa..0237c1cb9 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -18,11 +18,17 @@ typedef struct Manager Manager; typedef struct Network Network; typedef struct Request Request; typedef int (*address_ready_callback_t)(Address *address); +typedef int (*address_netlink_handler_t)( + sd_netlink *rtnl, + sd_netlink_message *m, + Request *req, + Link *link, + Address *address); struct Address { Link *link; Network *network; - NetworkConfigSection *section; + ConfigSection *section; NetworkConfigSource source; NetworkConfigState state; union in_addr_union provider; /* DHCP server or router address */ @@ -47,6 +53,9 @@ struct Address { bool scope_set:1; bool ip_masquerade_done:1; + + /* duplicate_address_detection is only used by static or IPv4 dynamic addresses. + * To control DAD for IPv6 dynamic addresses, set IFA_F_NODAD to flags. */ AddressFamily duplicate_address_detection; sd_ipv4acd *acd; @@ -70,18 +79,26 @@ int address_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, int address_remove(Address *address); int address_dup(const Address *src, Address **ret); bool address_is_ready(const Address *a); -void address_set_broadcast(Address *a); +void address_set_broadcast(Address *a, Link *link); -DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(Address, address_free); -int link_drop_addresses(Link *link); +int link_drop_managed_addresses(Link *link); int link_drop_foreign_addresses(Link *link); int link_drop_ipv6ll_addresses(Link *link); void link_foreignize_addresses(Link *link); bool link_address_is_dynamic(const Link *link, const Address *address); -int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret); -int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret); -int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready); +int link_get_address(Link *link, int family, const union in_addr_union *address, unsigned char prefixlen, Address **ret); +static inline int link_get_ipv6_address(Link *link, const struct in6_addr *address, unsigned char prefixlen, Address **ret) { + assert(address); + return link_get_address(link, AF_INET6, &(union in_addr_union) { .in6 = *address }, prefixlen, ret); +} +static inline int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret) { + assert(address); + return link_get_address(link, AF_INET, &(union in_addr_union) { .in = *address }, prefixlen, ret); +} +int manager_get_address(Manager *manager, int family, const union in_addr_union *address, unsigned char prefixlen, Address **ret); +bool manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready); void address_cancel_request(Address *address); int link_request_address( @@ -89,17 +106,15 @@ int link_request_address( Address *address, bool consume_object, unsigned *message_counter, - link_netlink_message_handler_t netlink_handler, + address_netlink_handler_t netlink_handler, Request **ret); int link_request_static_address(Link *link, Address *address, bool consume); int link_request_static_addresses(Link *link); -int request_process_address(Request *req); int manager_rtnl_process_address(sd_netlink *nl, sd_netlink_message *message, Manager *m); int network_drop_invalid_addresses(Network *network); -void address_hash_func(const Address *a, struct siphash *state); int address_compare_func(const Address *a1, const Address *a2); DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(Address, address); diff --git a/src/network/networkd-bridge-fdb.c b/src/network/networkd-bridge-fdb.c index 1298727a7..803e27cda 100644 --- a/src/network/networkd-bridge-fdb.c +++ b/src/network/networkd-bridge-fdb.c @@ -32,13 +32,13 @@ BridgeFDB *bridge_fdb_free(BridgeFDB *fdb) { hashmap_remove(fdb->network->bridge_fdb_entries_by_section, fdb->section); } - network_config_section_free(fdb->section); + config_section_free(fdb->section); free(fdb->outgoing_ifname); return mfree(fdb); } -DEFINE_NETWORK_SECTION_FUNCTIONS(BridgeFDB, bridge_fdb_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(BridgeFDB, bridge_fdb_free); /* create a new FDB entry or get an existing one. */ static int bridge_fdb_new_static( @@ -47,7 +47,7 @@ static int bridge_fdb_new_static( unsigned section_line, BridgeFDB **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(bridge_fdb_freep) BridgeFDB *fdb = NULL; int r; @@ -56,7 +56,7 @@ static int bridge_fdb_new_static( assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -83,7 +83,7 @@ static int bridge_fdb_new_static( .ntf_flags = NEIGHBOR_CACHE_ENTRY_FLAGS_SELF, }; - r = hashmap_ensure_put(&network->bridge_fdb_entries_by_section, &network_config_hash_ops, fdb->section, fdb); + r = hashmap_ensure_put(&network->bridge_fdb_entries_by_section, &config_section_hash_ops, fdb->section, fdb); if (r < 0) return r; @@ -93,16 +93,11 @@ static int bridge_fdb_new_static( return 0; } -static int bridge_fdb_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int bridge_fdb_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { int r; + assert(m); assert(link); - assert(link->static_bridge_fdb_messages > 0); - - link->static_bridge_fdb_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 0; r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { @@ -121,97 +116,73 @@ static int bridge_fdb_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, } /* send a request to the kernel to add a FDB entry in its static MAC table. */ -static int bridge_fdb_configure(const BridgeFDB *fdb, Link *link, link_netlink_message_handler_t callback) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int bridge_fdb_configure_message(const BridgeFDB *fdb, Link *link, sd_netlink_message *req) { int r; assert(fdb); assert(link); - assert(link->manager); - assert(link->manager->rtnl); - assert(callback); - - /* create new RTM message */ - r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_BRIDGE); - if (r < 0) - return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m"); r = sd_rtnl_message_neigh_set_flags(req, fdb->ntf_flags); if (r < 0) - return log_link_error_errno(link, r, "Could not set neighbor flags: %m"); + return r; /* only NUD_PERMANENT state supported. */ r = sd_rtnl_message_neigh_set_state(req, NUD_NOARP | NUD_PERMANENT); if (r < 0) - return log_link_error_errno(link, r, "Could not set neighbor state: %m"); + return r; r = sd_netlink_message_append_data(req, NDA_LLADDR, &fdb->mac_addr, sizeof(fdb->mac_addr)); if (r < 0) - return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m"); + return r; /* VLAN Id is optional. We'll add VLAN Id only if it's specified. */ if (fdb->vlan_id > 0) { r = sd_netlink_message_append_u16(req, NDA_VLAN, fdb->vlan_id); if (r < 0) - return log_link_error_errno(link, r, "Could not append NDA_VLAN attribute: %m"); + return r; } if (fdb->outgoing_ifindex > 0) { r = sd_netlink_message_append_u32(req, NDA_IFINDEX, fdb->outgoing_ifindex); if (r < 0) - return log_link_error_errno(link, r, "Could not append NDA_IFINDEX attribute: %m"); + return r; } if (in_addr_is_set(fdb->family, &fdb->destination_addr)) { r = netlink_message_append_in_addr_union(req, NDA_DST, fdb->family, &fdb->destination_addr); if (r < 0) - return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); + return r; } if (fdb->vni <= VXLAN_VID_MAX) { r = sd_netlink_message_append_u32(req, NDA_VNI, fdb->vni); if (r < 0) - return log_link_error_errno(link, r, "Could not append NDA_VNI attribute: %m"); - } - - /* send message to the kernel to update its internal static MAC table. */ - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return 1; -} - -int link_request_static_bridge_fdb(Link *link) { - BridgeFDB *fdb; - int r; - - assert(link); - assert(link->network); - - link->static_bridge_fdb_configured = false; - - HASHMAP_FOREACH(fdb, link->network->bridge_fdb_entries_by_section) { - r = link_queue_request(link, REQUEST_TYPE_BRIDGE_FDB, fdb, false, - &link->static_bridge_fdb_messages, bridge_fdb_configure_handler, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Failed to request static bridge FDB entry: %m"); - } - - if (link->static_bridge_fdb_messages == 0) { - link->static_bridge_fdb_configured = true; - link_check_ready(link); - } else { - log_link_debug(link, "Setting bridge FDB entries"); - link_set_state(link, LINK_STATE_CONFIGURING); + return r; } return 0; } +static int bridge_fdb_configure(BridgeFDB *fdb, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert(fdb); + assert(link); + assert(link->manager); + assert(req); + + r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH, link->ifindex, AF_BRIDGE); + if (r < 0) + return r; + + r = bridge_fdb_configure_message(fdb, link, m); + if (r < 0) + return r; + + return request_call_netlink_async(link->manager->rtnl, m, req); +} + static bool bridge_fdb_is_ready_to_configure(BridgeFDB *fdb, Link *link) { Link *out = NULL; @@ -237,16 +208,54 @@ static bool bridge_fdb_is_ready_to_configure(BridgeFDB *fdb, Link *link) { return true; } -int request_process_bridge_fdb(Request *req) { - assert(req); - assert(req->link); - assert(req->fdb); - assert(req->type == REQUEST_TYPE_BRIDGE_FDB); +static int bridge_fdb_process_request(Request *req, Link *link, void *userdata) { + BridgeFDB *fdb = ASSERT_PTR(userdata); + int r; - if (!bridge_fdb_is_ready_to_configure(req->fdb, req->link)) + assert(req); + assert(link); + + if (!bridge_fdb_is_ready_to_configure(fdb, link)) return 0; - return bridge_fdb_configure(req->fdb, req->link, req->netlink_handler); + r = bridge_fdb_configure(fdb, link, req); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure bridge FDB: %m"); + + return 1; +} + +int link_request_static_bridge_fdb(Link *link) { + BridgeFDB *fdb; + int r; + + assert(link); + assert(link->network); + + link->static_bridge_fdb_configured = false; + + HASHMAP_FOREACH(fdb, link->network->bridge_fdb_entries_by_section) { + r = link_queue_request_full(link, REQUEST_TYPE_BRIDGE_FDB, + fdb, NULL, + trivial_hash_func, + trivial_compare_func, + bridge_fdb_process_request, + &link->static_bridge_fdb_messages, + bridge_fdb_configure_handler, + NULL); + if (r < 0) + return log_link_error_errno(link, r, "Failed to request static bridge FDB entry: %m"); + } + + if (link->static_bridge_fdb_messages == 0) { + link->static_bridge_fdb_configured = true; + link_check_ready(link); + } else { + log_link_debug(link, "Setting bridge FDB entries"); + link_set_state(link, LINK_STATE_CONFIGURING); + } + + return 0; } void network_drop_invalid_bridge_fdb_entries(Network *network) { diff --git a/src/network/networkd-bridge-fdb.h b/src/network/networkd-bridge-fdb.h index fae7da5bb..b59d673d0 100644 --- a/src/network/networkd-bridge-fdb.h +++ b/src/network/networkd-bridge-fdb.h @@ -14,7 +14,6 @@ typedef struct Link Link; typedef struct Network Network; -typedef struct Request Request; typedef enum NeighborCacheEntryFlags { NEIGHBOR_CACHE_ENTRY_FLAGS_USE = NTF_USE, @@ -27,7 +26,7 @@ typedef enum NeighborCacheEntryFlags { typedef struct BridgeFDB { Network *network; - NetworkConfigSection *section; + ConfigSection *section; uint32_t vni; @@ -47,8 +46,6 @@ void network_drop_invalid_bridge_fdb_entries(Network *network); int link_request_static_bridge_fdb(Link *link); -int request_process_bridge_fdb(Request *req); - CONFIG_PARSER_PROTOTYPE(config_parse_fdb_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_fdb_vlan_id); CONFIG_PARSER_PROTOTYPE(config_parse_fdb_destination); diff --git a/src/network/networkd-bridge-mdb.c b/src/network/networkd-bridge-mdb.c index 10025a97a..3af4afe4a 100644 --- a/src/network/networkd-bridge-mdb.c +++ b/src/network/networkd-bridge-mdb.c @@ -24,12 +24,12 @@ BridgeMDB *bridge_mdb_free(BridgeMDB *mdb) { hashmap_remove(mdb->network->bridge_mdb_entries_by_section, mdb->section); } - network_config_section_free(mdb->section); + config_section_free(mdb->section); return mfree(mdb); } -DEFINE_NETWORK_SECTION_FUNCTIONS(BridgeMDB, bridge_mdb_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(BridgeMDB, bridge_mdb_free); /* create a new MDB entry or get an existing one. */ static int bridge_mdb_new_static( @@ -38,7 +38,7 @@ static int bridge_mdb_new_static( unsigned section_line, BridgeMDB **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(bridge_mdb_freep) BridgeMDB *mdb = NULL; int r; @@ -47,7 +47,7 @@ static int bridge_mdb_new_static( assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -72,7 +72,7 @@ static int bridge_mdb_new_static( .section = TAKE_PTR(n), }; - r = hashmap_ensure_put(&network->bridge_mdb_entries_by_section, &network_config_hash_ops, mdb->section, mdb); + r = hashmap_ensure_put(&network->bridge_mdb_entries_by_section, &config_section_hash_ops, mdb->section, mdb); if (r < 0) return r; @@ -81,16 +81,11 @@ static int bridge_mdb_new_static( return 0; } -static int bridge_mdb_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int bridge_mdb_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { int r; + assert(m); assert(link); - assert(link->static_bridge_mdb_messages > 0); - - link->static_bridge_mdb_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; r = sd_netlink_message_get_errno(m); if (r == -EINVAL && streq_ptr(link->kind, "bridge") && link->master_ifindex <= 0) { @@ -114,16 +109,15 @@ static int bridge_mdb_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, } /* send a request to the kernel to add an MDB entry */ -static int bridge_mdb_configure(BridgeMDB *mdb, Link *link, link_netlink_message_handler_t callback) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int bridge_mdb_configure(BridgeMDB *mdb, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; struct br_mdb_entry entry; int r; assert(mdb); assert(link); - assert(link->network); assert(link->manager); - assert(callback); + assert(req); if (DEBUG_LOGGING) { _cleanup_free_ char *a = NULL; @@ -156,63 +150,23 @@ static int bridge_mdb_configure(BridgeMDB *mdb, Link *link, link_netlink_message assert_not_reached(); } - /* create new RTM message */ - r = sd_rtnl_message_new_mdb(link->manager->rtnl, &req, RTM_NEWMDB, + r = sd_rtnl_message_new_mdb(link->manager->rtnl, &m, RTM_NEWMDB, link->master_ifindex > 0 ? link->master_ifindex : link->ifindex); if (r < 0) - return log_link_error_errno(link, r, "Could not create RTM_NEWMDB message: %m"); + return r; - r = sd_netlink_message_append_data(req, MDBA_SET_ENTRY, &entry, sizeof(entry)); + r = sd_netlink_message_append_data(m, MDBA_SET_ENTRY, &entry, sizeof(entry)); if (r < 0) - return log_link_error_errno(link, r, "Could not append MDBA_SET_ENTRY attribute: %m"); + return r; - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return 1; -} - -int link_request_static_bridge_mdb(Link *link) { - BridgeMDB *mdb; - int r; - - assert(link); - assert(link->manager); - - link->static_bridge_mdb_configured = false; - - if (!link->network) - return 0; - - if (hashmap_isempty(link->network->bridge_mdb_entries_by_section)) - goto finish; - - HASHMAP_FOREACH(mdb, link->network->bridge_mdb_entries_by_section) { - r = link_queue_request(link, REQUEST_TYPE_BRIDGE_MDB, mdb, false, - &link->static_bridge_mdb_messages, bridge_mdb_configure_handler, NULL); - if (r < 0) - return log_link_error_errno(link, r, "Failed to request MDB entry to multicast group database: %m"); - } - -finish: - if (link->static_bridge_mdb_messages == 0) { - link->static_bridge_mdb_configured = true; - link_check_ready(link); - } else { - log_link_debug(link, "Setting bridge MDB entries."); - link_set_state(link, LINK_STATE_CONFIGURING); - } - - return 0; + return request_call_netlink_async(link->manager->rtnl, m, req); } static bool bridge_mdb_is_ready_to_configure(Link *link) { Link *master; + assert(link); + if (!link_is_ready_to_configure(link, false)) return false; @@ -240,16 +194,61 @@ static bool bridge_mdb_is_ready_to_configure(Link *link) { return true; } -int request_process_bridge_mdb(Request *req) { - assert(req); - assert(req->link); - assert(req->mdb); - assert(req->type == REQUEST_TYPE_BRIDGE_MDB); +static int bridge_mdb_process_request(Request *req, Link *link, void *userdata) { + BridgeMDB *mdb = ASSERT_PTR(userdata); + int r; - if (!bridge_mdb_is_ready_to_configure(req->link)) + assert(req); + assert(link); + + if (!bridge_mdb_is_ready_to_configure(link)) return 0; - return bridge_mdb_configure(req->mdb, req->link, req->netlink_handler); + r = bridge_mdb_configure(mdb, link, req); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure bridge MDB: %m"); + + return 1; +} + +int link_request_static_bridge_mdb(Link *link) { + BridgeMDB *mdb; + int r; + + assert(link); + assert(link->manager); + + link->static_bridge_mdb_configured = false; + + if (!link->network) + return 0; + + if (hashmap_isempty(link->network->bridge_mdb_entries_by_section)) + goto finish; + + HASHMAP_FOREACH(mdb, link->network->bridge_mdb_entries_by_section) { + r = link_queue_request_full(link, REQUEST_TYPE_BRIDGE_MDB, + mdb, NULL, + trivial_hash_func, + trivial_compare_func, + bridge_mdb_process_request, + &link->static_bridge_mdb_messages, + bridge_mdb_configure_handler, + NULL); + if (r < 0) + return log_link_error_errno(link, r, "Failed to request MDB entry to multicast group database: %m"); + } + +finish: + if (link->static_bridge_mdb_messages == 0) { + link->static_bridge_mdb_configured = true; + link_check_ready(link); + } else { + log_link_debug(link, "Setting bridge MDB entries."); + link_set_state(link, LINK_STATE_CONFIGURING); + } + + return 0; } static int bridge_mdb_verify(BridgeMDB *mdb) { diff --git a/src/network/networkd-bridge-mdb.h b/src/network/networkd-bridge-mdb.h index 9ca262e0c..edea25576 100644 --- a/src/network/networkd-bridge-mdb.h +++ b/src/network/networkd-bridge-mdb.h @@ -9,11 +9,10 @@ typedef struct Link Link; typedef struct Network Network; -typedef struct Request Request; typedef struct BridgeMDB { Network *network; - NetworkConfigSection *section; + ConfigSection *section; int family; union in_addr_union group_addr; @@ -25,7 +24,6 @@ BridgeMDB *bridge_mdb_free(BridgeMDB *mdb); void network_drop_invalid_bridge_mdb_entries(Network *network); int link_request_static_bridge_mdb(Link *link); -int request_process_bridge_mdb(Request *req); CONFIG_PARSER_PROTOTYPE(config_parse_mdb_group_address); CONFIG_PARSER_PROTOTYPE(config_parse_mdb_vlan_id); diff --git a/src/network/networkd-bridge-vlan.c b/src/network/networkd-bridge-vlan.c index 3153bf698..36e3610a8 100644 --- a/src/network/networkd-bridge-vlan.c +++ b/src/network/networkd-bridge-vlan.c @@ -65,11 +65,9 @@ int bridge_vlan_append_info( for (int k = 0; k < BRIDGE_VLAN_BITMAP_LEN; k++) { uint32_t untagged_map = br_untagged_bitmap[k]; uint32_t vid_map = br_vid_bitmap[k]; - unsigned base_bit; - int i; + unsigned base_bit = k * 32; + int i = -1; - base_bit = k * 32; - i = -1; done = false; do { int j = find_next_bit(i, vid_map); @@ -106,14 +104,14 @@ int bridge_vlan_append_info( r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m"); + return r; } else { br_vlan.vid = begin; br_vlan.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN; r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m"); + return r; br_vlan.vid = end; br_vlan.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; @@ -121,7 +119,7 @@ int bridge_vlan_append_info( r = sd_netlink_message_append_data(req, IFLA_BRIDGE_VLAN_INFO, &br_vlan, sizeof(br_vlan)); if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_BRIDGE_VLAN_INFO attribute: %m"); + return r; } if (done) diff --git a/src/network/networkd-can.c b/src/network/networkd-can.c index 7a0a6e552..205179d11 100644 --- a/src/network/networkd-can.c +++ b/src/network/networkd-can.c @@ -21,15 +21,15 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) { r = sd_netlink_message_set_flags(m, NLM_F_REQUEST | NLM_F_ACK); if (r < 0) - return log_link_debug_errno(link, r, "Could not set netlink flags: %m"); + return r; r = sd_netlink_message_open_container(m, IFLA_LINKINFO); if (r < 0) - return log_link_debug_errno(link, r, "Failed to open IFLA_LINKINFO container: %m"); + return r; r = sd_netlink_message_open_container_union(m, IFLA_INFO_DATA, link->kind); if (r < 0) - return log_link_debug_errno(link, r, "Could not open IFLA_INFO_DATA container: %m"); + return r; if (link->network->can_bitrate > 0) { struct can_bittiming bt = { @@ -46,7 +46,7 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) { r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt)); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m"); + return r; } else if (link->network->can_time_quanta_ns > 0) { struct can_bittiming bt = { .tq = link->network->can_time_quanta_ns, @@ -59,7 +59,7 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) { log_link_debug(link, "Setting time quanta = %"PRIu32" nsec", bt.tq); r = sd_netlink_message_append_data(m, IFLA_CAN_BITTIMING, &bt, sizeof(bt)); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_CAN_BITTIMING attribute: %m"); + return r; } if (link->network->can_data_bitrate > 0) { @@ -77,7 +77,7 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) { r = sd_netlink_message_append_data(m, IFLA_CAN_DATA_BITTIMING, &bt, sizeof(bt)); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_CAN_DATA_BITTIMING attribute: %m"); + return r; } else if (link->network->can_data_time_quanta_ns > 0) { struct can_bittiming bt = { .tq = link->network->can_data_time_quanta_ns, @@ -90,7 +90,7 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) { log_link_debug(link, "Setting data time quanta = %"PRIu32" nsec", bt.tq); r = sd_netlink_message_append_data(m, IFLA_CAN_DATA_BITTIMING, &bt, sizeof(bt)); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_CAN_DATA_BITTIMING attribute: %m"); + return r; } if (link->network->can_restart_us > 0) { @@ -104,7 +104,7 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) { log_link_debug(link, "Setting restart = %s", FORMAT_TIMESPAN(restart_ms * 1000, MSEC_PER_SEC)); r = sd_netlink_message_append_u32(m, IFLA_CAN_RESTART_MS, restart_ms); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_CAN_RESTART_MS attribute: %m"); + return r; } if (link->network->can_control_mode_mask != 0) { @@ -115,7 +115,7 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) { r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm)); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_CAN_CTRLMODE attribute: %m"); + return r; } if (link->network->can_termination_set) { @@ -123,16 +123,16 @@ int can_set_netlink_message(Link *link, sd_netlink_message *m) { r = sd_netlink_message_append_u16(m, IFLA_CAN_TERMINATION, link->network->can_termination); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_CAN_TERMINATION attribute: %m"); + return r; } r = sd_netlink_message_close_container(m); if (r < 0) - return log_link_debug_errno(link, r, "Failed to close IFLA_INFO_DATA container: %m"); + return r; r = sd_netlink_message_close_container(m); if (r < 0) - return log_link_debug_errno(link, r, "Failed to close IFLA_LINKINFO container: %m"); + return r; return 0; } diff --git a/src/network/networkd-dhcp-common.c b/src/network/networkd-dhcp-common.c index 7996960bd..f825c8b18 100644 --- a/src/network/networkd-dhcp-common.c +++ b/src/network/networkd-dhcp-common.c @@ -61,6 +61,13 @@ bool link_dhcp_enabled(Link *link, int family) { if (link->iftype == ARPHRD_CAN) return false; + if (!IN_SET(link->hw_addr.length, ETH_ALEN, INFINIBAND_ALEN) && + !streq_ptr(link->kind, "wwan")) + /* Currently, only interfaces whose MAC address length is ETH_ALEN or INFINIBAND_ALEN + * are supported. Note, wwan interfaces may be assigned MAC address slightly later. + * Hence, let's wait for a while.*/ + return false; + if (!link->network) return false; @@ -330,7 +337,7 @@ int config_parse_dhcp_or_ra_route_metric( return 0; } - switch(ltype) { + switch (ltype) { case AF_INET: network->dhcp_route_metric = metric; network->dhcp_route_metric_set = true; @@ -381,7 +388,7 @@ int config_parse_dhcp_use_dns( return 0; } - switch(ltype) { + switch (ltype) { case AF_INET: network->dhcp_use_dns = r; network->dhcp_use_dns_set = true; @@ -432,7 +439,7 @@ int config_parse_dhcp_use_domains( return 0; } - switch(ltype) { + switch (ltype) { case AF_INET: network->dhcp_use_domains = d; network->dhcp_use_domains_set = true; @@ -483,7 +490,7 @@ int config_parse_dhcp_use_ntp( return 0; } - switch(ltype) { + switch (ltype) { case AF_INET: network->dhcp_use_ntp = r; network->dhcp_use_ntp_set = true; @@ -535,7 +542,7 @@ int config_parse_dhcp_or_ra_route_table( return 0; } - switch(ltype) { + switch (ltype) { case AF_INET: network->dhcp_route_table = rt; network->dhcp_route_table_set = true; @@ -770,7 +777,7 @@ int config_parse_dhcp_send_option( return 0; } - switch(type) { + switch (type) { case DHCP_OPTION_DATA_UINT8:{ r = safe_atou8(p, &uint8_data); if (r < 0) { diff --git a/src/network/networkd-dhcp-prefix-delegation.c b/src/network/networkd-dhcp-prefix-delegation.c index 7be9713d4..d8d947563 100644 --- a/src/network/networkd-dhcp-prefix-delegation.c +++ b/src/network/networkd-dhcp-prefix-delegation.c @@ -187,7 +187,7 @@ int dhcp_pd_remove(Link *link, bool only_marked) { continue; if (link->radv) - (void) sd_radv_remove_prefix(link->radv, &route->dst.in6, 64); + sd_radv_remove_prefix(link->radv, &route->dst.in6, 64); link_remove_dhcp_pd_subnet_prefix(link, &route->dst.in6); @@ -212,7 +212,7 @@ int dhcp_pd_remove(Link *link, bool only_marked) { in6_addr_mask(&prefix, 64); if (link->radv) - (void) sd_radv_remove_prefix(link->radv, &prefix, 64); + sd_radv_remove_prefix(link->radv, &prefix, 64); link_remove_dhcp_pd_subnet_prefix(link, &prefix); @@ -288,13 +288,10 @@ static int dhcp_pd_check_ready(Link *link) { return 1; } -static int dhcp_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int dhcp_pd_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) { int r; assert(link); - assert(link->dhcp_pd_messages > 0); - - link->dhcp_pd_messages--; r = route_configure_handler_internal(rtnl, m, link, "Failed to add prefix route for DHCP delegated subnet prefix"); if (r <= 0) @@ -344,13 +341,10 @@ static int dhcp_pd_request_route(Link *link, const struct in6_addr *prefix, usec return 0; } -static int dhcp_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int dhcp_pd_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) { int r; assert(link); - assert(link->dhcp_pd_messages > 0); - - link->dhcp_pd_messages--; r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCP-PD address"); if (r <= 0) @@ -487,20 +481,13 @@ static int dhcp_pd_get_preferred_subnet_prefix( "subnet id %" PRIu64 " is out of range. Only have %" PRIu64 " subnets.", link->network->dhcp_pd_subnet_id, UINT64_C(1) << (64 - pd_prefix_len)); - if (link_get_by_dhcp_pd_subnet_prefix(link->manager, &prefix, &assigned_link) >= 0 && - assigned_link != link) { - _cleanup_free_ char *assigned_buf = NULL; - - (void) in6_addr_to_string(&prefix, &assigned_buf); - return log_link_warning_errno(link, SYNTHETIC_ERRNO(EAGAIN), - "The requested prefix %s is already assigned to another link.", - strna(assigned_buf)); - } - *ret = prefix; return 0; } + if (dhcp_pd_get_assigned_subnet_prefix(link, pd_prefix, pd_prefix_len, ret) >= 0) + return 0; + for (uint64_t n = 0; ; n++) { /* If we do not have an allocation preference just iterate * through the address space and return the first free prefix. */ @@ -517,11 +504,16 @@ static int dhcp_pd_get_preferred_subnet_prefix( /* Check that the prefix is not assigned to another link. */ if (link_get_by_dhcp_pd_subnet_prefix(link->manager, &prefix, &assigned_link) < 0 || - assigned_link == link) { - *ret = prefix; - return 0; - } + assigned_link == link) + break; } + + r = link_add_dhcp_pd_subnet_prefix(link, &prefix); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to save acquired free subnet prefix: %m"); + + *ret = prefix; + return 0; } static int dhcp_pd_assign_subnet_prefix( @@ -540,9 +532,9 @@ static int dhcp_pd_assign_subnet_prefix( assert(link->network); assert(pd_prefix); - if (dhcp_pd_get_assigned_subnet_prefix(link, pd_prefix, pd_prefix_len, &prefix) < 0 && - dhcp_pd_get_preferred_subnet_prefix(link, pd_prefix, pd_prefix_len, &prefix) < 0) - return 0; + r = dhcp_pd_get_preferred_subnet_prefix(link, pd_prefix, pd_prefix_len, &prefix); + if (r < 0) + return r == -ERANGE ? 0 : r; (void) in6_addr_prefix_to_string(&prefix, 64, &buf); @@ -570,12 +562,6 @@ static int dhcp_pd_assign_subnet_prefix( "Failed to assign/update address for prefix %s: %m", strna(buf)); - r = link_add_dhcp_pd_subnet_prefix(link, &prefix); - if (r < 0) - return log_link_warning_errno(link, r, - "Failed to save assigned prefix %s: %m", - strna(buf)); - log_link_debug(link, "Assigned prefix %s", strna(buf)); return 1; } @@ -666,13 +652,10 @@ void dhcp4_pd_prefix_lost(Link *uplink) { (void) link_remove(tunnel); } -static int dhcp4_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int dhcp4_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) { int r; assert(link); - assert(link->dhcp4_messages > 0); - - link->dhcp4_messages--; r = route_configure_handler_internal(rtnl, m, link, "Failed to set unreachable route for DHCPv4 delegated prefix"); if (r <= 0) @@ -685,13 +668,10 @@ static int dhcp4_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message return 1; } -static int dhcp6_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int dhcp6_unreachable_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) { int r; assert(link); - assert(link->dhcp6_messages > 0); - - link->dhcp6_messages--; r = route_configure_handler_internal(rtnl, m, link, "Failed to set unreachable route for DHCPv6 delegated prefix"); if (r <= 0) @@ -712,7 +692,7 @@ static int dhcp_request_unreachable_route( NetworkConfigSource source, const union in_addr_union *server_address, unsigned *counter, - link_netlink_message_handler_t callback) { + route_netlink_handler_t callback) { _cleanup_(route_freep) Route *route = NULL; Route *existing; @@ -906,7 +886,7 @@ static int dhcp4_pd_assign_subnet_prefix(Link *link, Link *uplink) { if (r < 0) return log_link_warning_errno(uplink, r, "Failed to get lifetime of DHCPv4 lease: %m"); - lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(clock_boottime_or_monotonic())); + lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(CLOCK_BOOTTIME)); r = sd_dhcp_lease_get_6rd(uplink->dhcp_lease, &ipv4masklen, &sixrd_prefixlen, &sixrd_prefix, &br_addresses, NULL); if (r < 0) @@ -978,7 +958,7 @@ int dhcp4_pd_prefix_acquired(Link *uplink) { if (r < 0) return log_link_warning_errno(uplink, r, "Failed to get lifetime of DHCPv4 lease: %m"); - lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(clock_boottime_or_monotonic())); + lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(CLOCK_BOOTTIME)); r = sd_dhcp_lease_get_server_identifier(uplink->dhcp_lease, &server_address.in); if (r < 0) @@ -1065,7 +1045,7 @@ static int dhcp6_pd_assign_subnet_prefixes(Link *link, Link *uplink) { if (r <= 0) return r; - r = sd_dhcp6_lease_get_timestamp(uplink->dhcp6_lease, clock_boottime_or_monotonic(), ×tamp_usec); + r = sd_dhcp6_lease_get_timestamp(uplink->dhcp6_lease, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) return r; @@ -1114,7 +1094,7 @@ int dhcp6_pd_prefix_acquired(Link *uplink) { if (r < 0) return log_link_warning_errno(uplink, r, "Failed to get server address of DHCPv6 lease: %m"); - r = sd_dhcp6_lease_get_timestamp(uplink->dhcp6_lease, clock_boottime_or_monotonic(), ×tamp_usec); + r = sd_dhcp6_lease_get_timestamp(uplink->dhcp6_lease, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) return log_link_warning_errno(uplink, r, "Failed to get timestamp of DHCPv6 lease: %m"); diff --git a/src/network/networkd-dhcp-server-static-lease.c b/src/network/networkd-dhcp-server-static-lease.c index 6acd838e2..38e8c7e88 100644 --- a/src/network/networkd-dhcp-server-static-lease.c +++ b/src/network/networkd-dhcp-server-static-lease.c @@ -7,7 +7,7 @@ #include "networkd-network.h" #include "networkd-util.h" -DEFINE_NETWORK_SECTION_FUNCTIONS(DHCPStaticLease, dhcp_static_lease_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(DHCPStaticLease, dhcp_static_lease_free); DHCPStaticLease *dhcp_static_lease_free(DHCPStaticLease *static_lease) { if (!static_lease) @@ -16,7 +16,7 @@ DHCPStaticLease *dhcp_static_lease_free(DHCPStaticLease *static_lease) { if (static_lease->network && static_lease->section) hashmap_remove(static_lease->network->dhcp_static_leases_by_section, static_lease->section); - network_config_section_free(static_lease->section); + config_section_free(static_lease->section); free(static_lease->client_id); return mfree(static_lease); } @@ -35,7 +35,7 @@ static int dhcp_static_lease_new(DHCPStaticLease **ret) { } static int lease_new_static(Network *network, const char *filename, unsigned section_line, DHCPStaticLease **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(dhcp_static_lease_freep) DHCPStaticLease *static_lease = NULL; int r; @@ -44,7 +44,7 @@ static int lease_new_static(Network *network, const char *filename, unsigned sec assert(section_line > 0); assert(ret); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -60,7 +60,7 @@ static int lease_new_static(Network *network, const char *filename, unsigned sec static_lease->network = network; static_lease->section = TAKE_PTR(n); - r = hashmap_ensure_put(&network->dhcp_static_leases_by_section, &network_config_hash_ops, static_lease->section, static_lease); + r = hashmap_ensure_put(&network->dhcp_static_leases_by_section, &config_section_hash_ops, static_lease->section, static_lease); if (r < 0) return r; diff --git a/src/network/networkd-dhcp-server-static-lease.h b/src/network/networkd-dhcp-server-static-lease.h index c2a9ad6bb..9b8e78b90 100644 --- a/src/network/networkd-dhcp-server-static-lease.h +++ b/src/network/networkd-dhcp-server-static-lease.h @@ -8,11 +8,11 @@ #include "in-addr-util.h" typedef struct Network Network; -typedef struct NetworkConfigSection NetworkConfigSection; +typedef struct ConfigSection ConfigSection; typedef struct DHCPStaticLease { Network *network; - NetworkConfigSection *section; + ConfigSection *section; struct in_addr address; uint8_t *client_id; diff --git a/src/network/networkd-dhcp-server.c b/src/network/networkd-dhcp-server.c index 9acfd17d4..5861ecb92 100644 --- a/src/network/networkd-dhcp-server.c +++ b/src/network/networkd-dhcp-server.c @@ -58,12 +58,21 @@ void network_adjust_dhcp_server(Network *network) { ORDERED_HASHMAP_FOREACH(address, network->addresses_by_section) { if (section_is_invalid(address->section)) continue; - if (address->family == AF_INET && - !in4_addr_is_localhost(&address->in_addr.in) && - in4_addr_is_null(&address->in_addr_peer.in)) { - have = true; - break; - } + + if (address->family != AF_INET) + continue; + + if (in4_addr_is_localhost(&address->in_addr.in)) + continue; + + if (in4_addr_is_link_local(&address->in_addr.in)) + continue; + + if (in4_addr_is_set(&address->in_addr_peer.in)) + continue; + + have = true; + break; } if (!have) { log_warning("%s: DHCPServer= is enabled, but no static address configured. " @@ -97,7 +106,7 @@ int link_request_dhcp_server_address(Link *link) { address->family = AF_INET; address->in_addr.in = link->network->dhcp_server_address; address->prefixlen = link->network->dhcp_server_address_prefixlen; - address_set_broadcast(address); + address_set_broadcast(address, link); if (address_get(link, address, &existing) >= 0 && address_exists(existing) && @@ -130,6 +139,8 @@ static int link_find_dhcp_server_address(Link *link, Address **ret) { continue; if (in4_addr_is_localhost(&address->in_addr.in)) continue; + if (in4_addr_is_link_local(&address->in_addr.in)) + continue; if (in4_addr_is_set(&address->in_addr_peer.in)) continue; @@ -206,8 +217,6 @@ static int link_push_uplink_to_dhcp_server( break; case SD_DHCP_LEASE_NTP: { - char **i; - /* For NTP things are similar, but for NTP hostnames can be configured too, which we cannot * propagate via DHCP. Hence let's only propagate those which are IP addresses. */ @@ -391,12 +400,6 @@ static int dhcp4_server_configure(Link *link) { if (r < 0) return log_link_error_errno(link, r, "Failed to configure address pool for DHCPv4 server instance: %m"); - /* TODO: - r = sd_dhcp_server_set_router(link->dhcp_server, &main_address->in_addr.in); - if (r < 0) - return r; - */ - if (link->network->dhcp_server_max_lease_time_usec > 0) { r = sd_dhcp_server_set_max_lease_time(link->dhcp_server, DIV_ROUND_UP(link->network->dhcp_server_max_lease_time_usec, USEC_PER_SEC)); @@ -411,6 +414,18 @@ static int dhcp4_server_configure(Link *link) { return log_link_error_errno(link, r, "Failed to set default lease time for DHCPv4 server instance: %m"); } + r = sd_dhcp_server_set_boot_server_address(link->dhcp_server, &link->network->dhcp_server_boot_server_address); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to set boot server address for DHCPv4 server instance: %m"); + + r = sd_dhcp_server_set_boot_server_name(link->dhcp_server, link->network->dhcp_server_boot_server_name); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to set boot server name for DHCPv4 server instance: %m"); + + r = sd_dhcp_server_set_boot_filename(link->dhcp_server, link->network->dhcp_server_boot_filename); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to set boot filename for DHCPv4 server instance: %m"); + for (sd_dhcp_lease_server_type_t type = 0; type < _SD_DHCP_LEASE_SERVER_TYPE_MAX; type ++) { if (!link->network->dhcp_server_emit[type].emit) @@ -515,21 +530,7 @@ static int dhcp4_server_configure(Link *link) { return log_link_error_errno(link, r, "Could not start DHCPv4 server instance: %m"); log_link_debug(link, "Offering DHCPv4 leases"); - - return 1; -} - -int link_request_dhcp_server(Link *link) { - assert(link); - - if (!link_dhcp4_server_enabled(link)) - return 0; - - if (link->dhcp_server) - return 0; - - log_link_debug(link, "Requesting DHCP server."); - return link_queue_request(link, REQUEST_TYPE_DHCP_SERVER, NULL, false, NULL, NULL, NULL); + return 0; } static bool dhcp_server_is_ready_to_configure(Link *link) { @@ -568,15 +569,38 @@ static bool dhcp_server_is_ready_to_configure(Link *link) { return true; } -int request_process_dhcp_server(Request *req) { - assert(req); - assert(req->link); - assert(req->type == REQUEST_TYPE_DHCP_SERVER); +static int dhcp_server_process_request(Request *req, Link *link, void *userdata) { + int r; - if (!dhcp_server_is_ready_to_configure(req->link)) + assert(link); + + if (!dhcp_server_is_ready_to_configure(link)) return 0; - return dhcp4_server_configure(req->link); + r = dhcp4_server_configure(link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure DHCP server: %m"); + + return 1; +} + +int link_request_dhcp_server(Link *link) { + int r; + + assert(link); + + if (!link_dhcp4_server_enabled(link)) + return 0; + + if (link->dhcp_server) + return 0; + + log_link_debug(link, "Requesting DHCP server."); + r = link_queue_request(link, REQUEST_TYPE_DHCP_SERVER, dhcp_server_process_request, NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request configuration of DHCP server: %m"); + + return 0; } int config_parse_dhcp_server_relay_agent_suboption( @@ -629,6 +653,12 @@ int config_parse_dhcp_server_emit( assert(emit); assert(rvalue); + if (isempty(rvalue)) { + emit->addresses = mfree(emit->addresses); + emit->n_addresses = 0; + return 0; + } + for (const char *p = rvalue;;) { _cleanup_free_ char *w = NULL; union in_addr_union a; @@ -645,18 +675,26 @@ int config_parse_dhcp_server_emit( if (r == 0) return 0; - r = in_addr_from_string(AF_INET, w, &a); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse %s= address '%s', ignoring: %m", lvalue, w); - continue; + if (streq(w, "_server_address")) + a = IN_ADDR_NULL; /* null address will be converted to the server address. */ + else { + r = in_addr_from_string(AF_INET, w, &a); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s= address '%s', ignoring: %m", lvalue, w); + continue; + } + + if (in4_addr_is_null(&a.in)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Found a null address in %s=, ignoring.", lvalue); + continue; + } } - struct in_addr *m = reallocarray(emit->addresses, emit->n_addresses + 1, sizeof(struct in_addr)); - if (!m) + if (!GREEDY_REALLOC(emit->addresses, emit->n_addresses + 1)) return log_oom(); - emit->addresses = m; emit->addresses[emit->n_addresses++] = a.in; } } @@ -673,7 +711,7 @@ int config_parse_dhcp_server_address( void *data, void *userdata) { - Network *network = userdata; + Network *network = ASSERT_PTR(userdata); union in_addr_union a; unsigned char prefixlen; int r; diff --git a/src/network/networkd-dhcp-server.h b/src/network/networkd-dhcp-server.h index 1af1c65c6..cb2a8b6a3 100644 --- a/src/network/networkd-dhcp-server.h +++ b/src/network/networkd-dhcp-server.h @@ -5,13 +5,11 @@ typedef struct Link Link; typedef struct Network Network; -typedef struct Request Request; void network_adjust_dhcp_server(Network *network); int link_request_dhcp_server_address(Link *link); int link_request_dhcp_server(Link *link); -int request_process_dhcp_server(Request *req); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_relay_agent_suboption); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_emit); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index cb9c428ae..6bfcf67dd 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -177,16 +177,11 @@ static int dhcp4_retry(Link *link) { return dhcp4_request_address_and_routes(link, false); } -static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int dhcp4_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) { int r; + assert(m); assert(link); - assert(link->dhcp4_messages > 0); - - 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 == -ENETUNREACH && !link->dhcp4_route_retrying) { @@ -404,67 +399,66 @@ static int dhcp4_request_route_auto( } static int dhcp4_request_static_routes(Link *link, struct in_addr *ret_default_gw) { - _cleanup_free_ sd_dhcp_route **static_routes = NULL; - bool classless_route = false, static_route = false; + _cleanup_free_ sd_dhcp_route **static_routes = NULL, **classless_routes = NULL; + size_t n_static_routes, n_classless_routes, n; struct in_addr default_gw = {}; - int n, r; + sd_dhcp_route **routes; + int r; assert(link); assert(link->dhcp_lease); assert(ret_default_gw); - n = sd_dhcp_lease_get_routes(link->dhcp_lease, &static_routes); - if (IN_SET(n, 0, -ENODATA)) { + r = sd_dhcp_lease_get_static_routes(link->dhcp_lease, &static_routes); + if (r == -ENODATA) + n_static_routes = 0; + else if (r < 0) + return r; + else + n_static_routes = r; + + r = sd_dhcp_lease_get_classless_routes(link->dhcp_lease, &classless_routes); + if (r == -ENODATA) + n_classless_routes = 0; + else if (r < 0) + return r; + else + n_classless_routes = r; + + if (n_classless_routes == 0 && n_static_routes == 0) { log_link_debug(link, "DHCP: No static routes received from DHCP server."); return 0; } - if (n < 0) - return n; - - for (int i = 0; i < n; i++) { - switch (sd_dhcp_route_get_option(static_routes[i])) { - case SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE: - classless_route = true; - break; - case SD_DHCP_OPTION_STATIC_ROUTE: - static_route = true; - break; - } - } /* if the DHCP server returns both a Classless Static Routes option and a Static Routes option, * the DHCP client MUST ignore the Static Routes option. */ - if (classless_route && static_route) + if (n_classless_routes > 0 && n_static_routes > 0) log_link_debug(link, "Classless static routes received from DHCP server: ignoring static-route option"); if (!link->network->dhcp_use_routes) { - if (!classless_route) - return 0; /* Even if UseRoutes=no, try to find default gateway to make semi-static routes and * routes to DNS or NTP servers can be configured in later steps. */ - for (int i = 0; i < n; i++) { + + for (size_t i = 0; i < n_classless_routes; i++) { struct in_addr dst; uint8_t prefixlen; - if (sd_dhcp_route_get_option(static_routes[i]) != SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE) - continue; - - r = sd_dhcp_route_get_destination(static_routes[i], &dst); + r = sd_dhcp_route_get_destination(classless_routes[i], &dst); if (r < 0) return r; if (in4_addr_is_set(&dst)) continue; - r = sd_dhcp_route_get_destination_prefix_length(static_routes[i], &prefixlen); + r = sd_dhcp_route_get_destination_prefix_length(classless_routes[i], &prefixlen); if (r < 0) return r; if (prefixlen != 0) continue; - r = sd_dhcp_route_get_gateway(static_routes[i], ret_default_gw); + r = sd_dhcp_route_get_gateway(classless_routes[i], ret_default_gw); if (r < 0) return r; @@ -476,36 +470,41 @@ static int dhcp4_request_static_routes(Link *link, struct in_addr *ret_default_g return 0; } - for (int i = 0; i < n; i++) { + if (n_classless_routes > 0) { + n = n_classless_routes; + routes = classless_routes; + } else if (n_static_routes > 0){ + n = n_static_routes; + routes = static_routes; + } else + assert_not_reached(); + + for (size_t i = 0; i < n; i++) { _cleanup_(route_freep) Route *route = NULL; struct in_addr gw; - if (sd_dhcp_route_get_option(static_routes[i]) != - (classless_route ? SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE : SD_DHCP_OPTION_STATIC_ROUTE)) - continue; - r = route_new(&route); if (r < 0) return r; route->gw_family = AF_INET; - r = sd_dhcp_route_get_gateway(static_routes[i], &gw); + r = sd_dhcp_route_get_gateway(routes[i], &gw); if (r < 0) return r; - r = sd_dhcp_route_get_destination(static_routes[i], &route->dst.in); + r = sd_dhcp_route_get_destination(routes[i], &route->dst.in); if (r < 0) return r; - r = sd_dhcp_route_get_destination_prefix_length(static_routes[i], &route->dst_prefixlen); + r = sd_dhcp_route_get_destination_prefix_length(routes[i], &route->dst_prefixlen); if (r < 0) return r; /* When classless static routes are provided, then router option will be ignored. To * use the default gateway later in other routes, e.g., routes to dns servers, here we * need to find the default gateway in the classless static routes. */ - if (classless_route && + if (n_classless_routes > 0 && in4_addr_is_null(&route->dst.in) && route->dst_prefixlen == 0 && in4_addr_is_null(&default_gw)) default_gw = gw; @@ -516,7 +515,7 @@ static int dhcp4_request_static_routes(Link *link, struct in_addr *ret_default_g } *ret_default_gw = default_gw; - return classless_route; + return n_classless_routes > 0; } static int dhcp4_request_gateway(Link *link, struct in_addr *gw) { @@ -825,13 +824,10 @@ int dhcp4_lease_lost(Link *link) { return link_request_static_routes(link, true); } -static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int dhcp4_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) { int r; assert(link); - assert(link->dhcp4_messages > 0); - - link->dhcp4_messages--; r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv4 address"); if (r <= 0) @@ -875,7 +871,7 @@ static int dhcp4_request_address(Link *link, bool announce) { if (r < 0) return log_link_warning_errno(link, r, "DHCP error: no lifetime: %m"); - lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(clock_boottime_or_monotonic())); + lifetime_usec = usec_add(lifetime_sec * USEC_PER_SEC, now(CLOCK_BOOTTIME)); } else lifetime_usec = USEC_INFINITY; @@ -921,8 +917,7 @@ static int dhcp4_request_address(Link *link, bool announce) { addr->lifetime_preferred_usec = lifetime_usec; addr->lifetime_valid_usec = lifetime_usec; addr->prefixlen = prefixlen; - if (prefixlen <= 30) - addr->broadcast.s_addr = address.s_addr | ~netmask.s_addr; + address_set_broadcast(addr, link); SET_FLAG(addr->flags, IFA_F_NOPREFIXROUTE, !link_prefixroute(link)); addr->route_metric = link->network->dhcp_route_metric; addr->duplicate_address_detection = link->network->dhcp_send_decline ? ADDRESS_FAMILY_IPV4 : ADDRESS_FAMILY_NO; @@ -1423,7 +1418,7 @@ static int dhcp4_configure(Link *link) { if (!link->network->dhcp_anonymize) { if (link->network->dhcp_use_mtu) { - r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_INTERFACE_MTU); + r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_MTU_INTERFACE); if (r < 0) return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for MTU: %m"); } @@ -1439,7 +1434,7 @@ static int dhcp4_configure(Link *link) { } if (link->network->dhcp_use_domains != DHCP_USE_DOMAINS_NO) { - r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_DOMAIN_SEARCH_LIST); + r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_DOMAIN_SEARCH); if (r < 0) return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for domain search list: %m"); } @@ -1457,7 +1452,7 @@ static int dhcp4_configure(Link *link) { } if (link->network->dhcp_use_timezone) { - r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_NEW_TZDB_TIMEZONE); + r = sd_dhcp_client_set_request_option(link->dhcp_client, SD_DHCP_OPTION_TZDB_TIMEZONE); if (r < 0) return log_link_debug_errno(link, r, "DHCPv4 CLIENT: Failed to set request flag for timezone: %m"); } @@ -1594,24 +1589,24 @@ static int dhcp4_configure_duid(Link *link) { return dhcp_configure_duid(link, link_get_dhcp4_duid(link)); } -int request_process_dhcp4_client(Request *req) { - Link *link; +static int dhcp4_process_request(Request *req, Link *link, void *userdata) { int r; - assert(req); - assert(req->link); - assert(req->type == REQUEST_TYPE_DHCP4_CLIENT); - - link = req->link; + assert(link); if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) return 0; + if (!IN_SET(link->hw_addr.length, ETH_ALEN, INFINIBAND_ALEN) || + hw_addr_is_null(&link->hw_addr)) + /* No MAC address is assigned to the hardware, or non-supported MAC address length. */ + return 0; + r = dhcp4_configure_duid(link); if (r <= 0) return r; - r = dhcp4_configure(req->link); + r = dhcp4_configure(link); if (r < 0) return log_link_warning_errno(link, r, "Failed to configure DHCPv4 client: %m"); @@ -1621,7 +1616,6 @@ int request_process_dhcp4_client(Request *req) { log_link_debug(link, "DHCPv4 client is configured%s.", r > 0 ? ", acquiring DHCPv4 lease" : ""); - return 1; } @@ -1636,7 +1630,7 @@ int link_request_dhcp4_client(Link *link) { if (link->dhcp_client) return 0; - r = link_queue_request(link, REQUEST_TYPE_DHCP4_CLIENT, NULL, false, NULL, NULL, NULL); + r = link_queue_request(link, REQUEST_TYPE_DHCP4_CLIENT, dhcp4_process_request, NULL); if (r < 0) return log_link_warning_errno(link, r, "Failed to request configuring of the DHCPv4 client: %m"); diff --git a/src/network/networkd-dhcp4.h b/src/network/networkd-dhcp4.h index 830e39f66..1d30cd15d 100644 --- a/src/network/networkd-dhcp4.h +++ b/src/network/networkd-dhcp4.h @@ -5,7 +5,6 @@ typedef struct Link Link; typedef struct Network Network; -typedef struct Request Request; typedef enum DHCPClientIdentifier { DHCP_CLIENT_ID_MAC, @@ -25,7 +24,6 @@ int dhcp4_start(Link *link); int dhcp4_lease_lost(Link *link); int dhcp4_check_ready(Link *link); -int request_process_dhcp4_client(Request *req); int link_request_dhcp4_client(Link *link); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_client_identifier); diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index e59104311..d4b61ad79 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -136,13 +136,10 @@ int dhcp6_check_ready(Link *link) { return 0; } -static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) { int r; assert(link); - assert(link->dhcp6_messages > 0); - - link->dhcp6_messages--; r = address_configure_handler_internal(rtnl, m, link, "Could not set DHCPv6 address"); if (r <= 0) @@ -155,7 +152,7 @@ static int dhcp6_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link * return 1; } -static void log_dhcp6_address(Link *link, const Address *address) { +static int verify_dhcp6_address(Link *link, const Address *address) { _cleanup_free_ char *buffer = NULL; bool by_ndisc = false; Address *existing; @@ -167,7 +164,8 @@ static void log_dhcp6_address(Link *link, const Address *address) { (void) in6_addr_to_string(&address->in_addr.in6, &buffer); - if (address_get(link, address, &existing) < 0) { + if (address_get(link, address, &existing) < 0 && + link_get_address(link, AF_INET6, &address->in_addr, 0, &existing) < 0) { /* New address. */ log_level = LOG_INFO; goto simple_log; @@ -181,20 +179,23 @@ static void log_dhcp6_address(Link *link, const Address *address) { if (existing->source == NETWORK_CONFIG_SOURCE_NDISC) by_ndisc = true; - log_link_warning(link, "DHCPv6 address %s/%u (valid %s, preferred %s) conflicts the address %s/%u%s.", + log_link_warning(link, "Ignoring DHCPv6 address %s/%u (valid %s, preferred %s) which conflicts with %s/%u%s.", strna(buffer), address->prefixlen, FORMAT_LIFETIME(address->lifetime_valid_usec), FORMAT_LIFETIME(address->lifetime_preferred_usec), strna(buffer), existing->prefixlen, - by_ndisc ? " assigned by NDisc. Please try to use or update IPv6Token= setting " - "to change the address generated by NDISC, or disable UseAutonomousPrefix=" : ""); - return; + by_ndisc ? " assigned by NDisc" : ""); + if (by_ndisc) + log_link_warning(link, "Hint: use IPv6Token= setting to change the address generated by NDisc or set UseAutonomousPrefix=no."); + + return -EEXIST; simple_log: log_link_full(link, log_level, "DHCPv6 address %s/%u (valid %s, preferred %s)", strna(buffer), address->prefixlen, FORMAT_LIFETIME(address->lifetime_valid_usec), FORMAT_LIFETIME(address->lifetime_preferred_usec)); + return 0; } static int dhcp6_request_address( @@ -221,7 +222,8 @@ static int dhcp6_request_address( addr->lifetime_preferred_usec = lifetime_preferred_usec; addr->lifetime_valid_usec = lifetime_valid_usec; - log_dhcp6_address(link, addr); + if (verify_dhcp6_address(link, addr) < 0) + return 0; if (address_get(link, addr, &existing) < 0) link->dhcp6_configured = false; @@ -256,7 +258,7 @@ static int dhcp6_address_acquired(Link *link) { if (r < 0) return log_link_warning_errno(link, r, "Failed to get server address of DHCPv6 lease: %m"); - r = sd_dhcp6_lease_get_timestamp(link->dhcp6_lease, clock_boottime_or_monotonic(), ×tamp_usec); + r = sd_dhcp6_lease_get_timestamp(link->dhcp6_lease, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) return log_link_warning_errno(link, r, "Failed to get timestamp of DHCPv6 lease: %m"); @@ -432,8 +434,20 @@ int dhcp6_start_on_ra(Link *link, bool information_request) { return r; if (inf_req == information_request) + /* The client is already running in the requested mode. */ return 0; + if (!inf_req) { + log_link_debug(link, + "The DHCPv6 client is already running in the managed mode, " + "refusing to start the client in the information requesting mode."); + return 0; + } + + log_link_debug(link, + "The DHCPv6 client is running in the information requesting mode. " + "Restarting the client in the managed mode."); + r = sd_dhcp6_client_stop(link->dhcp6_client); if (r < 0) return r; @@ -698,19 +712,19 @@ int dhcp6_update_mac(Link *link) { return 0; } -int request_process_dhcp6_client(Request *req) { - Link *link; +static int dhcp6_process_request(Request *req, Link *link, void *userdata) { int r; - assert(req); - assert(req->link); - assert(req->type == REQUEST_TYPE_DHCP6_CLIENT); - - link = req->link; + assert(link); if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) return 0; + if (!IN_SET(link->hw_addr.length, ETH_ALEN, INFINIBAND_ALEN) || + hw_addr_is_null(&link->hw_addr)) + /* No MAC address is assigned to the hardware, or non-supported MAC address length. */ + return 0; + r = dhcp_configure_duid(link, link_get_dhcp6_duid(link)); if (r <= 0) return r; @@ -729,7 +743,6 @@ int request_process_dhcp6_client(Request *req) { log_link_debug(link, "DHCPv6 client is configured%s.", r > 0 ? ", acquiring DHCPv6 lease" : ""); - return 1; } @@ -744,7 +757,7 @@ int link_request_dhcp6_client(Link *link) { if (link->dhcp6_client) return 0; - r = link_queue_request(link, REQUEST_TYPE_DHCP6_CLIENT, NULL, false, NULL, NULL, NULL); + r = link_queue_request(link, REQUEST_TYPE_DHCP6_CLIENT, dhcp6_process_request, NULL); if (r < 0) return log_link_warning_errno(link, r, "Failed to request configuring of the DHCPv6 client: %m"); diff --git a/src/network/networkd-dhcp6.h b/src/network/networkd-dhcp6.h index 52347c5e7..81267c255 100644 --- a/src/network/networkd-dhcp6.h +++ b/src/network/networkd-dhcp6.h @@ -13,7 +13,6 @@ typedef enum DHCP6ClientStartMode { } DHCP6ClientStartMode; typedef struct Link Link; -typedef struct Request Request; bool link_dhcp6_with_address_enabled(Link *link); int dhcp6_check_ready(Link *link); @@ -21,7 +20,6 @@ int dhcp6_update_mac(Link *link); int dhcp6_start(Link *link); int dhcp6_start_on_ra(Link *link, bool information_request); -int request_process_dhcp6_client(Request *req); int link_request_dhcp6_client(Link *link); int link_serialize_dhcp6_client(Link *link, FILE *f); diff --git a/src/network/networkd-ipv4acd.c b/src/network/networkd-ipv4acd.c index 703278f8b..009cde27d 100644 --- a/src/network/networkd-ipv4acd.c +++ b/src/network/networkd-ipv4acd.c @@ -132,7 +132,8 @@ int ipv4acd_configure(Address *address) { int r; assert(address); - assert(address->link); + + link = ASSERT_PTR(address->link); if (address->family != AF_INET) return 0; @@ -140,6 +141,9 @@ int ipv4acd_configure(Address *address) { if (!FLAGS_SET(address->duplicate_address_detection, ADDRESS_FAMILY_IPV4)) return 0; + if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr)) + return 0; + /* Currently, only static and DHCP4 addresses are supported. */ assert(IN_SET(address->source, NETWORK_CONFIG_SOURCE_STATIC, NETWORK_CONFIG_SOURCE_DHCP4)); @@ -148,8 +152,6 @@ int ipv4acd_configure(Address *address) { return 0; } - link = address->link; - log_link_debug(link, "Configuring IPv4ACD for address "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index d05182fde..67eb9d2aa 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -34,7 +34,7 @@ static int address_new_from_ipv4ll(Link *link, Address **ret) { address->prefixlen = 16; address->scope = RT_SCOPE_LINK; address->route_metric = IPV4LL_ROUTE_METRIC; - address_set_broadcast(address); + address_set_broadcast(address, link); *ret = TAKE_PTR(address); return 0; @@ -70,7 +70,7 @@ static int ipv4ll_address_lost(Link *link) { return address_remove(existing); } -static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) { int r; assert(link); @@ -117,7 +117,7 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) return; - switch(event) { + switch (event) { case SD_IPV4LL_EVENT_STOP: r = ipv4ll_address_lost(link); if (r < 0) { diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c index a332eaefa..8166851ed 100644 --- a/src/network/networkd-ipv6-proxy-ndp.c +++ b/src/network/networkd-ipv6-proxy-ndp.c @@ -26,13 +26,17 @@ void network_adjust_ipv6_proxy_ndp(Network *network) { } } -static int ipv6_proxy_ndp_address_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int ipv6_proxy_ndp_address_configure_handler( + sd_netlink *rtnl, + sd_netlink_message *m, + Request *req, + Link *link, + struct in6_addr *address) { + int r; + assert(m); assert(link); - assert(link->static_ipv6_proxy_ndp_messages > 0); - - link->static_ipv6_proxy_ndp_messages--; r = sd_netlink_message_get_errno(m); if (r < 0) @@ -48,39 +52,45 @@ static int ipv6_proxy_ndp_address_configure_handler(sd_netlink *rtnl, sd_netlink } /* send a request to the kernel to add an IPv6 Proxy entry to the neighbour table */ -static int ipv6_proxy_ndp_address_configure( - const struct in6_addr *address, - Link *link, - link_netlink_message_handler_t callback) { - - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int ipv6_proxy_ndp_address_configure(const struct in6_addr *address, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert(address); assert(link); assert(link->manager); assert(link->manager->rtnl); - assert(callback); + assert(req); /* create new netlink message */ - r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_INET6); + r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH, link->ifindex, AF_INET6); if (r < 0) - return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m"); + return r; - r = sd_rtnl_message_neigh_set_flags(req, NTF_PROXY); + r = sd_rtnl_message_neigh_set_flags(m, NTF_PROXY); if (r < 0) - return log_link_error_errno(link, r, "Could not set neighbor flags: %m"); + return r; - r = sd_netlink_message_append_in6_addr(req, NDA_DST, address); + r = sd_netlink_message_append_in6_addr(m, NDA_DST, address); if (r < 0) - return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); + return r; - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, - link_netlink_destroy_callback, link); + return request_call_netlink_async(link->manager->rtnl, m, req); +} + +static int ipv6_proxy_ndp_address_process_request(Request *req, Link *link, struct in6_addr *address) { + int r; + + assert(req); + assert(link); + assert(address); + + if (!link_is_ready_to_configure(link, false)) + return 0; + + r = ipv6_proxy_ndp_address_configure(address, link, req); if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); + return log_link_warning_errno(link, r, "Failed to configure IPv6 proxy NDP address: %m"); return 1; } @@ -95,9 +105,14 @@ int link_request_static_ipv6_proxy_ndp_addresses(Link *link) { link->static_ipv6_proxy_ndp_configured = false; SET_FOREACH(address, link->network->ipv6_proxy_ndp_addresses) { - r = link_queue_request(link, REQUEST_TYPE_IPV6_PROXY_NDP, address, false, - &link->static_ipv6_proxy_ndp_messages, - ipv6_proxy_ndp_address_configure_handler, NULL); + r = link_queue_request_safe(link, REQUEST_TYPE_IPV6_PROXY_NDP, + address, NULL, + in6_addr_hash_func, + in6_addr_compare_func, + ipv6_proxy_ndp_address_process_request, + &link->static_ipv6_proxy_ndp_messages, + ipv6_proxy_ndp_address_configure_handler, + NULL); if (r < 0) return log_link_warning_errno(link, r, "Failed to request IPv6 proxy NDP address: %m"); } @@ -113,18 +128,6 @@ int link_request_static_ipv6_proxy_ndp_addresses(Link *link) { return 0; } -int request_process_ipv6_proxy_ndp_address(Request *req) { - assert(req); - assert(req->link); - assert(req->ipv6_proxy_ndp); - assert(req->type == REQUEST_TYPE_IPV6_PROXY_NDP); - - if (!link_is_ready_to_configure(req->link, false)) - return 0; - - return ipv6_proxy_ndp_address_configure(req->ipv6_proxy_ndp, req->link, req->netlink_handler); -} - int config_parse_ipv6_proxy_ndp_address( const char *unit, const char *filename, diff --git a/src/network/networkd-ipv6-proxy-ndp.h b/src/network/networkd-ipv6-proxy-ndp.h index b35cd5618..e57d28f99 100644 --- a/src/network/networkd-ipv6-proxy-ndp.h +++ b/src/network/networkd-ipv6-proxy-ndp.h @@ -5,11 +5,9 @@ typedef struct Link Link; typedef struct Network Network; -typedef struct Request Request; void network_adjust_ipv6_proxy_ndp(Network *network); int link_request_static_ipv6_proxy_ndp_addresses(Link *link); -int request_process_ipv6_proxy_ndp_address(Request *req); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_proxy_ndp_address); diff --git a/src/network/networkd-ipv6ll.c b/src/network/networkd-ipv6ll.c new file mode 100644 index 000000000..79cf679da --- /dev/null +++ b/src/network/networkd-ipv6ll.c @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "in-addr-util.h" +#include "networkd-address.h" +#include "networkd-ipv6ll.h" +#include "networkd-link.h" +#include "networkd-network.h" +#include "networkd-util.h" +#include "socket-util.h" +#include "string-table.h" +#include "strv.h" +#include "sysctl-util.h" + +bool link_ipv6ll_enabled(Link *link) { + assert(link); + + if (!socket_ipv6_is_supported()) + return false; + + if (link->flags & IFF_LOOPBACK) + return false; + + if (!link->network) + return false; + + if (link->iftype == ARPHRD_CAN) + return false; + + if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "sit", "vti", "nlmon")) + return false; + + if (link->network->bond) + return false; + + return link->network->link_local & ADDRESS_FAMILY_IPV6; +} + +bool link_may_have_ipv6ll(Link *link) { + assert(link); + + /* + * This is equivalent to link_ipv6ll_enabled() for non-WireGuard interfaces. + * + * For WireGuard interface, the kernel does not assign any IPv6LL addresses, but we can assign + * it manually. It is necessary to set an IPv6LL address manually to run NDisc or RADV on + * WireGuard interface. Note, also Multicast=yes must be set. See #17380. + * + * TODO: May be better to introduce GenerateIPv6LinkLocalAddress= setting, and use algorithms + * used in networkd-address-generation.c + */ + + if (link_ipv6ll_enabled(link)) + return true; + + /* IPv6LL address can be manually assigned on WireGuard interface. */ + if (streq_ptr(link->kind, "wireguard")) { + Address *a; + + if (!link->network) + return false; + + ORDERED_HASHMAP_FOREACH(a, link->network->addresses_by_section) { + if (a->family != AF_INET6) + continue; + if (in6_addr_is_set(&a->in_addr_peer.in6)) + continue; + if (in6_addr_is_link_local(&a->in_addr.in6)) + return true; + } + } + + return false; +} + +IPv6LinkLocalAddressGenMode link_get_ipv6ll_addrgen_mode(Link *link) { + assert(link); + + if (!link_ipv6ll_enabled(link)) + return IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE; + + if (link->network->ipv6ll_address_gen_mode >= 0) + return link->network->ipv6ll_address_gen_mode; + + if (in6_addr_is_set(&link->network->ipv6ll_stable_secret)) + return IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY; + + return IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64; +} + +int ipv6ll_addrgen_mode_fill_message(sd_netlink_message *message, IPv6LinkLocalAddressGenMode mode) { + int r; + + assert(message); + assert(mode >= 0 && mode < _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX); + + r = sd_netlink_message_open_container(message, IFLA_AF_SPEC); + if (r < 0) + return r; + + r = sd_netlink_message_open_container(message, AF_INET6); + if (r < 0) + return r; + + r = sd_netlink_message_append_u8(message, IFLA_INET6_ADDR_GEN_MODE, mode); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(message); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(message); + if (r < 0) + return r; + + return 0; +} + +int link_update_ipv6ll_addrgen_mode(Link *link, sd_netlink_message *message) { + uint8_t mode; + int family, r; + + assert(link); + assert(message); + + r = sd_rtnl_message_get_family(message, &family); + if (r < 0) + return r; + + if (family != AF_UNSPEC) + return 0; + + r = sd_netlink_message_enter_container(message, IFLA_AF_SPEC); + if (r == -ENODATA) + return 0; + if (r < 0) + return r; + + r = sd_netlink_message_enter_container(message, AF_INET6); + if (r == -ENODATA) + return sd_netlink_message_exit_container(message); + if (r < 0) + return r; + + mode = (uint8_t) link->ipv6ll_address_gen_mode; + r = sd_netlink_message_read_u8(message, IFLA_INET6_ADDR_GEN_MODE, &mode); + if (r < 0 && r != -ENODATA) + return r; + + r = sd_netlink_message_exit_container(message); + if (r < 0) + return r; + + r = sd_netlink_message_exit_container(message); + if (r < 0) + return r; + + if (mode == (uint8_t) link->ipv6ll_address_gen_mode) + return 0; + + if (mode >= _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX) { + log_link_debug(link, "Received invalid IPv6 link-local address generation mode (%u), ignoring.", mode); + return 0; + } + + if (link->ipv6ll_address_gen_mode < 0) + log_link_debug(link, "Saved IPv6 link-local address generation mode: %s", + ipv6_link_local_address_gen_mode_to_string(mode)); + else + log_link_debug(link, "IPv6 link-local address generation mode is changed: %s -> %s", + ipv6_link_local_address_gen_mode_to_string(link->ipv6ll_address_gen_mode), + ipv6_link_local_address_gen_mode_to_string(mode)); + + link->ipv6ll_address_gen_mode = mode; + return 0; +} + +#define STABLE_SECRET_APP_ID_1 SD_ID128_MAKE(aa,05,1d,94,43,68,45,07,b9,73,f1,e8,e4,b7,34,52) +#define STABLE_SECRET_APP_ID_2 SD_ID128_MAKE(52,c4,40,a0,9f,2f,48,58,a9,3a,f6,29,25,ba,7a,7d) + +int link_set_ipv6ll_stable_secret(Link *link) { + _cleanup_free_ char *str = NULL; + struct in6_addr a; + int r; + + assert(link); + assert(link->network); + + if (link->network->ipv6ll_address_gen_mode != IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY) + return 0; + + if (in6_addr_is_set(&link->network->ipv6ll_stable_secret)) + a = link->network->ipv6ll_stable_secret; + else { + sd_id128_t key; + le64_t v; + + /* Generate a stable secret address from machine-ID and the interface name. */ + + r = sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_1, &key); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to generate key: %m"); + + v = htole64(siphash24_string(link->ifname, key.bytes)); + memcpy(a.s6_addr, &v, sizeof(v)); + + r = sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_2, &key); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to generate key: %m"); + + v = htole64(siphash24_string(link->ifname, key.bytes)); + assert_cc(sizeof(v) * 2 == sizeof(a.s6_addr)); + memcpy(a.s6_addr + sizeof(v), &v, sizeof(v)); + } + + r = in6_addr_to_string(&a, &str); + if (r < 0) + return r; + + return sysctl_write_ip_property(AF_INET6, link->ifname, "stable_secret", str); +} + +int link_set_ipv6ll_addrgen_mode(Link *link, IPv6LinkLocalAddressGenMode mode) { + assert(link); + assert(mode >= 0 && mode < _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX); + + if (mode == link->ipv6ll_address_gen_mode) + return 0; + + return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "addr_gen_mode", mode); +} + +static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX] = { + [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64] = "eui64", + [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE] = "none", + [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy", + [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM] = "random", +}; + +DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode); +DEFINE_CONFIG_PARSE_ENUM( + config_parse_ipv6_link_local_address_gen_mode, + ipv6_link_local_address_gen_mode, + IPv6LinkLocalAddressGenMode, + "Failed to parse IPv6 link local address generation mode"); diff --git a/src/network/networkd-ipv6ll.h b/src/network/networkd-ipv6ll.h new file mode 100644 index 000000000..a9763debb --- /dev/null +++ b/src/network/networkd-ipv6ll.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include +#include + +#include "sd-netlink.h" + +#include "conf-parser.h" +#include "macro.h" + +typedef struct Link Link; + +typedef enum IPv6LinkLocalAddressGenMode { + IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64 = IN6_ADDR_GEN_MODE_EUI64, + IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE = IN6_ADDR_GEN_MODE_NONE, + IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY = IN6_ADDR_GEN_MODE_STABLE_PRIVACY, + IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM = IN6_ADDR_GEN_MODE_RANDOM, + _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX, + _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID = -EINVAL, +} IPv6LinkLocalAddressGenMode; + +bool link_ipv6ll_enabled(Link *link); +bool link_may_have_ipv6ll(Link *link); + +IPv6LinkLocalAddressGenMode link_get_ipv6ll_addrgen_mode(Link *link); +int ipv6ll_addrgen_mode_fill_message(sd_netlink_message *message, IPv6LinkLocalAddressGenMode mode); +int link_update_ipv6ll_addrgen_mode(Link *link, sd_netlink_message *message); + +int link_set_ipv6ll_stable_secret(Link *link); +int link_set_ipv6ll_addrgen_mode(Link *link, IPv6LinkLocalAddressGenMode mode); + +const char* ipv6_link_local_address_gen_mode_to_string(IPv6LinkLocalAddressGenMode s) _const_; +IPv6LinkLocalAddressGenMode ipv6_link_local_address_gen_mode_from_string(const char *s) _pure_; + +CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode); diff --git a/src/network/networkd-json.c b/src/network/networkd-json.c index fb1f50063..dc9823c37 100644 --- a/src/network/networkd-json.c +++ b/src/network/networkd-json.c @@ -699,7 +699,6 @@ static int server_build_json_one_string(const char *str, NetworkConfigSource s, static int ntp_build_json(Link *link, JsonVariant **ret) { JsonVariant **elements = NULL; size_t n = 0; - char **p; int r; assert(link); @@ -878,7 +877,7 @@ static int domains_build_json(Link *link, bool is_route, JsonVariant **ret) { JsonVariant **elements = NULL; DHCPUseDomains use_domains; union in_addr_union s; - char **p, **domains; + char **domains; const char *domain; size_t n = 0; int r; diff --git a/src/network/networkd-link-bus.c b/src/network/networkd-link-bus.c index 765733b38..4173eaa15 100644 --- a/src/network/networkd-link-bus.c +++ b/src/network/networkd-link-bus.c @@ -81,7 +81,6 @@ int bus_link_method_set_ntp_servers(sd_bus_message *message, void *userdata, sd_ _cleanup_strv_free_ char **ntp = NULL; Link *l = userdata; int r; - char **i; assert(message); assert(l); @@ -484,7 +483,6 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v _cleanup_strv_free_ char **ntas = NULL; Link *l = userdata; int r; - char **i; assert(message); assert(l); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index b62a15482..d509855f4 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -104,67 +104,6 @@ bool link_ipv4ll_enabled(Link *link) { return link->network->link_local & ADDRESS_FAMILY_IPV4; } -bool link_ipv6ll_enabled(Link *link) { - assert(link); - - if (!socket_ipv6_is_supported()) - return false; - - if (link->flags & IFF_LOOPBACK) - return false; - - if (!link->network) - return false; - - if (link->iftype == ARPHRD_CAN) - return false; - - if (STRPTR_IN_SET(link->kind, "vrf", "wireguard", "ipip", "gre", "sit", "vti", "nlmon")) - return false; - - if (link->network->bond) - return false; - - return link->network->link_local & ADDRESS_FAMILY_IPV6; -} - -bool link_may_have_ipv6ll(Link *link) { - assert(link); - - /* - * This is equivalent to link_ipv6ll_enabled() for non-WireGuard interfaces. - * - * For WireGuard interface, the kernel does not assign any IPv6LL addresses, but we can assign - * it manually. It is necessary to set an IPv6LL address manually to run NDisc or RADV on - * WireGuard interface. Note, also Multicast=yes must be set. See #17380. - * - * TODO: May be better to introduce GenerateIPv6LinkLocalAddress= setting, and use algorithms - * used in networkd-address-generation.c - */ - - if (link_ipv6ll_enabled(link)) - return true; - - /* IPv6LL address can be manually assigned on WireGuard interface. */ - if (streq_ptr(link->kind, "wireguard")) { - Address *a; - - if (!link->network) - return false; - - ORDERED_HASHMAP_FOREACH(a, link->network->addresses_by_section) { - if (a->family != AF_INET6) - continue; - if (in6_addr_is_set(&a->in_addr_peer.in6)) - continue; - if (in6_addr_is_link_local(&a->in_addr.in6)) - return true; - } - } - - return false; -} - bool link_ipv6_enabled(Link *link) { assert(link); @@ -268,12 +207,11 @@ static Link *link_free(Link *link) { link_dns_settings_clear(link); link->routes = set_free(link->routes); - link->nexthops = set_free(link->nexthops); - link->neighbors = set_free(link->neighbors); - link->addresses = set_free(link->addresses); + link->qdiscs = set_free(link->qdiscs); + link->tclasses = set_free(link->tclasses); link->dhcp_pd_prefixes = set_free(link->dhcp_pd_prefixes); @@ -291,6 +229,7 @@ static Link *link_free(Link *link) { unlink_and_free(link->state_file); sd_device_unref(link->sd_device); + netdev_unref(link->netdev); hashmap_free(link->bound_to_links); hashmap_free(link->bound_by_links); @@ -429,6 +368,8 @@ int link_stop_engines(Link *link, bool may_keep_dhcp) { if (k < 0) r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Discovery: %m"); + ndisc_flush(link); + k = sd_radv_stop(link->radv); if (k < 0) r = log_link_warning_errno(link, k, "Could not stop IPv6 Router Advertisement: %m"); @@ -622,7 +563,6 @@ static int link_request_stacked_netdevs(Link *link) { assert(link); link->stacked_netdevs_created = false; - link->stacked_netdevs_after_configured_created = false; HASHMAP_FOREACH(netdev, link->network->stacked_netdevs) { r = link_request_stacked_netdev(link, netdev); @@ -634,8 +574,6 @@ static int link_request_stacked_netdevs(Link *link) { link->stacked_netdevs_created = true; link_check_ready(link); } - if (link->create_stacked_netdev_after_configured_messages == 0) - link->stacked_netdevs_after_configured_created = true; return 0; } @@ -891,24 +829,6 @@ static int link_new_bound_to_list(Link *link) { return 0; } -static int link_new_carrier_maps(Link *link) { - int r; - - r = link_new_bound_by_list(link); - if (r < 0) - return r; - - r = link_handle_bound_by_list(link); - if (r < 0) - return r; - - r = link_new_bound_to_list(link); - if (r < 0) - return r; - - return link_handle_bound_to_list(link); -} - static void link_free_bound_to_list(Link *link) { bool updated = false; Link *bound_to; @@ -945,13 +865,6 @@ static void link_free_bound_by_list(Link *link) { link_dirty(link); } -static void link_free_carrier_maps(Link *link) { - assert(link); - - link_free_bound_to_list(link); - link_free_bound_by_list(link); -} - static int link_append_to_master(Link *link) { Link *master; int r; @@ -993,12 +906,10 @@ static void link_drop_requests(Link *link) { ORDERED_SET_FOREACH(req, link->manager->request_queue) if (req->link == link) - request_drop(req); + request_detach(link->manager, req); } static Link *link_drop(Link *link) { - char **n; - if (!link) return NULL; @@ -1011,7 +922,8 @@ static Link *link_drop(Link *link) { link_drop_requests(link); - link_free_carrier_maps(link); + link_free_bound_to_list(link); + link_free_bound_by_list(link); link_drop_from_master(link); @@ -1025,8 +937,7 @@ static Link *link_drop(Link *link) { hashmap_remove(link->manager->links_by_name, link->ifname); /* bonding master and its slaves have the same hardware address. */ - if (hashmap_get(link->manager->links_by_hw_addr, &link->hw_addr) == link) - hashmap_remove(link->manager->links_by_hw_addr, &link->hw_addr); + hashmap_remove_value(link->manager->links_by_hw_addr, &link->hw_addr, link); /* The following must be called at last. */ assert_se(hashmap_remove(link->manager->links_by_index, INT_TO_PTR(link->ifindex)) == link); @@ -1070,32 +981,30 @@ static int link_drop_foreign_config(Link *link) { return r; } -static int link_drop_config(Link *link) { +static int link_drop_managed_config(Link *link) { int k, r; assert(link); assert(link->manager); - r = link_drop_routes(link); + r = link_drop_managed_routes(link); - k = link_drop_nexthops(link); + k = link_drop_managed_nexthops(link); if (k < 0 && r >= 0) r = k; - k = link_drop_addresses(link); + k = link_drop_managed_addresses(link); if (k < 0 && r >= 0) r = k; - k = link_drop_neighbors(link); + k = link_drop_managed_neighbors(link); if (k < 0 && r >= 0) r = k; - k = link_drop_routing_policy_rules(link); + k = link_drop_managed_routing_policy_rules(link); if (k < 0 && r >= 0) r = k; - ndisc_flush(link); - return r; } @@ -1119,7 +1028,11 @@ static int link_configure(Link *link) { link_set_state(link, LINK_STATE_CONFIGURING); - r = link_configure_traffic_control(link); + r = link_new_bound_to_list(link); + if (r < 0) + return r; + + r = link_request_traffic_control(link); if (r < 0) return r; @@ -1132,7 +1045,7 @@ static int link_configure(Link *link) { return link_request_to_activate(link); } - r = link_configure_sr_iov(link); + r = link_request_sr_iov_vfs(link); if (r < 0) return r; @@ -1200,7 +1113,7 @@ static int link_configure(Link *link) { if (r < 0) return r; - r = ndisc_configure(link); + r = link_request_ndisc(link); if (r < 0) return r; @@ -1252,6 +1165,7 @@ static int link_get_network(Link *link, Network **ret) { &link->permanent_hw_addr, link->driver, link->iftype, + link->kind, link->ifname, link->alternative_names, link->wlan_iftype, @@ -1289,21 +1203,37 @@ static int link_get_network(Link *link, Network **ret) { static int link_reconfigure_impl(Link *link, bool force) { Network *network = NULL; + NetDev *netdev = NULL; int r; assert(link); + if (!IN_SET(link->state, LINK_STATE_INITIALIZED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED, LINK_STATE_UNMANAGED)) + return 0; + + r = netdev_get(link->manager, link->ifname, &netdev); + if (r < 0 && r != -ENOENT) + return r; + r = link_get_network(link, &network); if (r < 0 && r != -ENOENT) return r; + if (link->state != LINK_STATE_UNMANAGED && !network) + /* If link is in initialized state, then link->network is also NULL. */ + force = true; + if (link->network == network && !force) return 0; - if (network) - log_link_info(link, "Reconfiguring with %s.", network->filename); - else - log_link_info(link, "Unmanaging interface."); + if (network) { + if (link->state == LINK_STATE_INITIALIZED) + log_link_info(link, "Configuring with %s.", network->filename); + else + log_link_info(link, "Reconfiguring with %s.", network->filename); + } else + log_link_full(link, link->state == LINK_STATE_INITIALIZED ? LOG_DEBUG : LOG_INFO, + "Unmanaging interface."); /* Dropping old .network file */ r = link_stop_engines(link, false); @@ -1312,21 +1242,34 @@ static int link_reconfigure_impl(Link *link, bool force) { link_drop_requests(link); - if (network && !force) + if (network && !force && network->keep_configuration != KEEP_CONFIGURATION_YES) /* When a new/updated .network file is assigned, first make all configs (addresses, * routes, and so on) foreign, and then drop unnecessary configs later by - * link_drop_foreign_config() in link_configure(). */ + * link_drop_foreign_config() in link_configure(). + * Note, when KeepConfiguration=yes, link_drop_foreign_config() does nothing. Hence, + * here we need to drop the configs such as addresses, routes, and so on configured by + * the previously assigned .network file. */ link_foreignize_config(link); else { - r = link_drop_config(link); + /* Remove all managed configs. Note, foreign configs are removed in later by + * link_configure() -> link_drop_foreign_config() if the link is managed by us. */ + r = link_drop_managed_config(link); if (r < 0) return r; } - link_free_carrier_maps(link); + /* The bound_to map depends on .network file, hence it needs to be freed. But, do not free the + * bound_by map. Otherwise, if a link enters unmanaged state below, then its carrier state will + * not propagated to other interfaces anymore. Moreover, it is not necessary to recreate the + * map here, as it depends on .network files assigned to other links. */ + link_free_bound_to_list(link); + link_free_engines(link); link->network = network_unref(link->network); + netdev_unref(link->netdev); + link->netdev = netdev_ref(netdev); + if (!network) { link_set_state(link, LINK_STATE_UNMANAGED); return 0; @@ -1337,10 +1280,6 @@ static int link_reconfigure_impl(Link *link, bool force) { link_update_operstate(link, true); link_dirty(link); - r = link_new_carrier_maps(link); - if (r < 0) - return r; - link_set_state(link, LINK_STATE_INITIALIZED); link->activated = false; @@ -1440,11 +1379,9 @@ int link_reconfigure_after_sleep(Link *link) { } static int link_initialized_and_synced(Link *link) { - Network *network; int r; assert(link); - assert(link->ifname); assert(link->manager); if (link->manager->test_mode) { @@ -1453,7 +1390,7 @@ static int link_initialized_and_synced(Link *link) { return 0; } - /* We may get called either from the asynchronous netlink callback, + /* This may get called either from the asynchronous netlink callback, * or directly from link_check_initialized() if running in a container. */ if (!IN_SET(link->state, LINK_STATE_PENDING, LINK_STATE_INITIALIZED)) return 0; @@ -1469,36 +1406,7 @@ static int link_initialized_and_synced(Link *link) { if (r < 0) return r; - if (!link->network) { - r = link_get_network(link, &network); - if (r == -ENOENT) { - link_set_state(link, LINK_STATE_UNMANAGED); - return 0; - } - if (r < 0) - return r; - - if (link->flags & IFF_LOOPBACK) { - if (network->link_local != ADDRESS_FAMILY_NO) - log_link_debug(link, "Ignoring link-local autoconfiguration for loopback link"); - - if (network->dhcp != ADDRESS_FAMILY_NO) - log_link_debug(link, "Ignoring DHCP clients for loopback link"); - - if (network->dhcp_server) - log_link_debug(link, "Ignoring DHCP server for loopback link"); - } - - link->network = network_ref(network); - link_update_operstate(link, false); - link_dirty(link); - } - - r = link_new_bound_to_list(link); - if (r < 0) - return r; - - return link_configure(link); + return link_reconfigure_impl(link, /* force = */ false); } static int link_initialized_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { @@ -1705,7 +1613,7 @@ static int link_carrier_lost_impl(Link *link) { if (r < 0) ret = r; - r = link_drop_config(link); + r = link_drop_managed_config(link); if (r < 0 && ret >= 0) ret = r; @@ -1751,7 +1659,7 @@ static int link_carrier_lost(Link *link) { return event_reset_time_relative(link->manager->event, &link->carrier_lost_timer, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, link->network->ignore_carrier_loss_usec, 0, link_carrier_lost_handler, @@ -1773,7 +1681,7 @@ static int link_admin_state_up(Link *link) { return 0; if (link->activated && link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_DOWN) { - log_link_info(link, "ActivationPolicy is \"always-off\", forcing link down."); + log_link_info(link, "Activation policy is \"always-down\", forcing link down."); return link_request_to_bring_up_or_down(link, /* up = */ false); } @@ -1793,28 +1701,13 @@ static int link_admin_state_down(Link *link) { return 0; if (link->activated && link->network->activation_policy == ACTIVATION_POLICY_ALWAYS_UP) { - log_link_info(link, "ActivationPolicy is \"always-on\", forcing link up."); + log_link_info(link, "Activation policy is \"always-up\", forcing link up."); return link_request_to_bring_up_or_down(link, /* up = */ true); } return 0; } -bool link_has_carrier(Link *link) { - /* see Documentation/networking/operstates.txt in the kernel sources */ - - if (link->kernel_operstate == IF_OPER_UP) - return true; - - if (link->kernel_operstate == IF_OPER_UNKNOWN) - /* operstate may not be implemented, so fall back to flags */ - if (FLAGS_SET(link->flags, IFF_LOWER_UP | IFF_RUNNING) && - !FLAGS_SET(link->flags, IFF_DORMANT)) - return true; - - return false; -} - static bool link_is_enslaved(Link *link) { if (link->flags & IFF_SLAVE) return true; @@ -2160,6 +2053,22 @@ static int link_update_driver(Link *link, sd_netlink_message *message) { log_link_debug(link, "Found driver: %s", strna(link->driver)); + if (streq_ptr(link->driver, "dsa")) { + uint32_t dsa_master_ifindex = 0; + + r = sd_netlink_message_read_u32(message, IFLA_LINK, &dsa_master_ifindex); + if (r < 0 && r != -ENODATA) + return log_link_debug_errno(link, r, "rtnl: failed to read ifindex of the DSA master interface: %m"); + + if (dsa_master_ifindex > INT_MAX) { + log_link_debug(link, "rtnl: received too large DSA master ifindex (%"PRIu32" > INT_MAX), ignoring.", + dsa_master_ifindex); + dsa_master_ifindex = 0; + } + + link->dsa_master_ifindex = (int) dsa_master_ifindex; + } + return 0; } @@ -2224,8 +2133,7 @@ static int link_update_hardware_address(Link *link, sd_netlink_message *message) log_link_debug(link, "Hardware address is changed: %s → %s", HW_ADDR_TO_STR(&link->hw_addr), HW_ADDR_TO_STR(&addr)); - if (hashmap_get(link->manager->links_by_hw_addr, &link->hw_addr) == link) - hashmap_remove(link->manager->links_by_hw_addr, &link->hw_addr); + hashmap_remove_value(link->manager->links_by_hw_addr, &link->hw_addr, link); } link->hw_addr = addr; @@ -2341,7 +2249,6 @@ static int link_update_mtu(Link *link, sd_netlink_message *message) { static int link_update_alternative_names(Link *link, sd_netlink_message *message) { _cleanup_strv_free_ char **altnames = NULL; - char **n; int r; assert(link); @@ -2502,6 +2409,10 @@ static int link_update(Link *link, sd_netlink_message *message) { if (r < 0) return r; + r = link_update_ipv6ll_addrgen_mode(link, message); + if (r < 0) + return r; + return link_update_flags(link, message); } @@ -2575,6 +2486,8 @@ static int link_new(Manager *manager, sd_netlink_message *message, Link **ret) { .ifname = TAKE_PTR(ifname), .kind = TAKE_PTR(kind), + .ipv6ll_address_gen_mode = _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID, + .state_file = TAKE_PTR(state_file), .lease_file = TAKE_PTR(lease_file), .lldp_file = TAKE_PTR(lldp_file), @@ -2656,7 +2569,7 @@ int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Man /* netdev exists, so make sure the ifindex matches */ r = netdev_set_ifindex(netdev, message); if (r < 0) { - log_warning_errno(r, "Could not process new link message for netdev, ignoring: %m"); + log_netdev_warning_errno(netdev, r, "Could not process new link message for netdev, ignoring: %m"); return 0; } } @@ -2671,32 +2584,30 @@ int manager_rtnl_process_link(sd_netlink *rtnl, sd_netlink_message *message, Man r = link_update(link, message); if (r < 0) { - log_warning_errno(r, "Could not process link message: %m"); + log_link_warning_errno(link, r, "Could not process link message: %m"); link_enter_failed(link); return 0; } r = link_check_initialized(link); if (r < 0) { - log_warning_errno(r, "Failed to check link is initialized: %m"); + log_link_warning_errno(link, r, "Failed to check link is initialized: %m"); link_enter_failed(link); return 0; } } else { r = link_update(link, message); if (r < 0) { - log_warning_errno(r, "Could not process link message: %m"); + log_link_warning_errno(link, r, "Could not process link message: %m"); link_enter_failed(link); return 0; } } - break; case RTM_DELLINK: link_drop(link); netdev_drop(netdev); - break; default: diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index 4ef86f485..733ac128e 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -19,7 +19,9 @@ #include "ether-addr-util.h" #include "log-link.h" +#include "netif-util.h" #include "network-util.h" +#include "networkd-ipv6ll.h" #include "networkd-util.h" #include "ordered-set.h" #include "resolve-util.h" @@ -39,6 +41,7 @@ typedef enum LinkState { typedef struct Manager Manager; typedef struct Network Network; +typedef struct NetDev NetDev; typedef struct DUID DUID; typedef struct Link { @@ -48,6 +51,7 @@ typedef struct Link { int ifindex; int master_ifindex; + int dsa_master_ifindex; char *ifname; char **alternative_names; char *kind; @@ -65,6 +69,9 @@ typedef struct Link { sd_device *sd_device; char *driver; + /* link local addressing */ + IPv6LinkLocalAddressGenMode ipv6ll_address_gen_mode; + /* wlan */ enum nl80211_iftype wlan_iftype; char *ssid; @@ -77,6 +84,7 @@ typedef struct Link { sd_event_source *carrier_lost_timer; Network *network; + NetDev *netdev; LinkState state; LinkOperationalState operstate; @@ -100,12 +108,13 @@ typedef struct Link { unsigned set_link_messages; unsigned set_flags_messages; unsigned create_stacked_netdev_messages; - unsigned create_stacked_netdev_after_configured_messages; Set *addresses; Set *neighbors; Set *routes; Set *nexthops; + Set *qdiscs; + Set *tclasses; sd_dhcp_client *dhcp_client; sd_dhcp_lease *dhcp_lease; @@ -133,7 +142,6 @@ typedef struct Link { bool activated:1; bool master_set:1; bool stacked_netdevs_created:1; - bool stacked_netdevs_after_configured_created:1; sd_dhcp_server *dhcp_server; @@ -211,11 +219,12 @@ void link_check_ready(Link *link); void link_update_operstate(Link *link, bool also_update_bond_master); -bool link_has_carrier(Link *link); +static inline bool link_has_carrier(Link *link) { + assert(link); + return netif_has_carrier(link->kernel_operstate, link->flags); +} bool link_ipv6_enabled(Link *link); -bool link_ipv6ll_enabled(Link *link); -bool link_may_have_ipv6ll(Link *link); int link_ipv6ll_gained(Link *link); bool link_ipv4ll_enabled(Link *link); diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index 9d790224c..2453c3325 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -42,15 +42,18 @@ #include "networkd-speed-meter.h" #include "networkd-state-file.h" #include "networkd-wifi.h" +#include "networkd-wiphy.h" #include "ordered-set.h" #include "path-lookup.h" #include "path-util.h" +#include "qdisc.h" #include "selinux-util.h" #include "set.h" #include "signal-util.h" #include "stat-util.h" #include "strv.h" #include "sysctl-util.h" +#include "tclass.h" #include "tmpfile-util.h" #include "udev-util.h" @@ -312,6 +315,22 @@ static int manager_connect_rtnl(Manager *m) { if (r < 0) return r; + r = netlink_add_match(m->rtnl, NULL, RTM_NEWQDISC, &manager_rtnl_process_qdisc, NULL, m, "network-rtnl_process_qdisc"); + if (r < 0) + return r; + + r = netlink_add_match(m->rtnl, NULL, RTM_DELQDISC, &manager_rtnl_process_qdisc, NULL, m, "network-rtnl_process_qdisc"); + if (r < 0) + return r; + + r = netlink_add_match(m->rtnl, NULL, RTM_NEWTCLASS, &manager_rtnl_process_tclass, NULL, m, "network-rtnl_process_tclass"); + if (r < 0) + return r; + + r = netlink_add_match(m->rtnl, NULL, RTM_DELTCLASS, &manager_rtnl_process_tclass, NULL, m, "network-rtnl_process_tclass"); + if (r < 0) + return r; + r = netlink_add_match(m->rtnl, NULL, RTM_NEWADDR, &manager_rtnl_process_address, NULL, m, "network-rtnl_process_address"); if (r < 0) return r; @@ -399,6 +418,30 @@ static int signal_restart_callback(sd_event_source *s, const struct signalfd_sig return sd_event_exit(sd_event_source_get_event(s), 0); } +static int manager_set_keep_configuration(Manager *m) { + int r; + + assert(m); + + if (in_initrd()) { + log_debug("Running in initrd, keep DHCPv4 addresses on stopping networkd by default."); + m->keep_configuration = KEEP_CONFIGURATION_DHCP_ON_STOP; + return 0; + } + + r = path_is_network_fs("/"); + if (r < 0) + return log_error_errno(r, "Failed to detect if root is network filesystem: %m"); + if (r == 0) { + m->keep_configuration = _KEEP_CONFIGURATION_INVALID; + return 0; + } + + log_debug("Running on network filesystem, enabling KeepConfiguration= by default."); + m->keep_configuration = KEEP_CONFIGURATION_YES; + return 0; +} + int manager_setup(Manager *m) { int r; @@ -454,6 +497,10 @@ int manager_setup(Manager *m) { if (r < 0) return r; + r = manager_set_keep_configuration(m); + if (r < 0) + return r; + m->state_file = strdup("/run/systemd/netif/state"); if (!m->state_file) return -ENOMEM; @@ -469,6 +516,7 @@ int manager_new(Manager **ret, bool test_mode) { return -ENOMEM; *m = (Manager) { + .keep_configuration = _KEEP_CONFIGURATION_INVALID, .test_mode = test_mode, .speed_meter_interval_usec = SPEED_METER_DEFAULT_TIME_INTERVAL, .online_state = _LINK_ONLINE_STATE_INVALID, @@ -508,6 +556,9 @@ Manager* manager_free(Manager *m) { m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref); + m->wiphy_by_name = hashmap_free(m->wiphy_by_name); + m->wiphy_by_index = hashmap_free_with_destructor(m->wiphy_by_index, wiphy_free); + ordered_set_free_free(m->address_pools); hashmap_free(m->route_table_names_by_number); @@ -643,6 +694,34 @@ static int manager_enumerate_links(Manager *m) { return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_link); } +static int manager_enumerate_qdisc(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(m); + assert(m->rtnl); + + r = sd_rtnl_message_new_traffic_control(m->rtnl, &req, RTM_GETQDISC, 0, 0, 0); + if (r < 0) + return r; + + return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_qdisc); +} + +static int manager_enumerate_tclass(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(m); + assert(m->rtnl); + + r = sd_rtnl_message_new_traffic_control(m->rtnl, &req, RTM_GETTCLASS, 0, 0, 0); + if (r < 0) + return r; + + return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_tclass); +} + static int manager_enumerate_addresses(Manager *m) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -719,6 +798,20 @@ static int manager_enumerate_nexthop(Manager *m) { return manager_enumerate_internal(m, m->rtnl, req, manager_rtnl_process_nexthop); } +static int manager_enumerate_nl80211_wiphy(Manager *m) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(m); + assert(m->genl); + + r = sd_genl_message_new(m->genl, NL80211_GENL_NAME, NL80211_CMD_GET_WIPHY, &req); + if (r < 0) + return r; + + return manager_enumerate_internal(m, m->genl, req, manager_genl_process_nl80211_wiphy); +} + static int manager_enumerate_nl80211_config(Manager *m) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -769,6 +862,14 @@ int manager_enumerate(Manager *m) { if (r < 0) return log_error_errno(r, "Could not enumerate links: %m"); + r = manager_enumerate_qdisc(m); + if (r < 0) + return log_error_errno(r, "Could not enumerate QDisc: %m"); + + r = manager_enumerate_tclass(m); + if (r < 0) + return log_error_errno(r, "Could not enumerate TClass: %m"); + r = manager_enumerate_addresses(m); if (r < 0) return log_error_errno(r, "Could not enumerate addresses: %m"); @@ -796,6 +897,12 @@ int manager_enumerate(Manager *m) { else if (r < 0) return log_error_errno(r, "Could not enumerate routing policy rules: %m"); + r = manager_enumerate_nl80211_wiphy(m); + if (r == -EOPNOTSUPP) + log_debug_errno(r, "Could not enumerate wireless LAN phy, ignoring: %m"); + else if (r < 0) + return log_error_errno(r, "Could not enumerate wireless LAN phy: %m"); + r = manager_enumerate_nl80211_config(m); if (r == -EOPNOTSUPP) log_debug_errno(r, "Could not enumerate wireless LAN interfaces, ignoring: %m"); diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 36313589a..fab2cfaf1 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -28,6 +28,8 @@ struct Manager { Hashmap *polkit_registry; int ethtool_fd; + KeepConfiguration keep_configuration; + bool test_mode; bool enumerating; bool dirty; @@ -82,6 +84,10 @@ struct Manager { Hashmap *route_table_numbers_by_name; Hashmap *route_table_names_by_number; + /* Wiphy */ + Hashmap *wiphy_by_index; + Hashmap *wiphy_by_name; + /* For link speed meter */ bool use_speed_meter; sd_event_source *speed_meter_event_source; diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index f616f2c9b..3078f82a4 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "sd-ndisc.h" @@ -34,6 +35,15 @@ bool link_ipv6_accept_ra_enabled(Link *link) { if (link->flags & IFF_LOOPBACK) return false; + if (link->iftype == ARPHRD_CAN) + return false; + + if (link->hw_addr.length != ETH_ALEN && !streq_ptr(link->kind, "wwan")) + /* Currently, only interfaces whose MAC address length is ETH_ALEN are supported. + * Note, wwan interfaces may be assigned MAC address slightly later. + * Hence, let's wait for a while.*/ + return false; + if (!link->network) return false; @@ -193,13 +203,10 @@ static int ndisc_check_ready(Link *link) { return 0; } -static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) { int r; assert(link); - assert(link->ndisc_messages > 0); - - link->ndisc_messages--; r = route_configure_handler_internal(rtnl, m, link, "Could not set NDisc route"); if (r <= 0) @@ -244,13 +251,10 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) { ndisc_route_handler, NULL); } -static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) { int r; assert(link); - assert(link->ndisc_messages > 0); - - link->ndisc_messages--; r = address_configure_handler_internal(rtnl, m, link, "Could not set NDisc address"); if (r <= 0) @@ -312,7 +316,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { if (lifetime_sec == 0) /* not a default router */ return 0; - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); + r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); @@ -322,7 +326,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m"); - if (link_get_ipv6_address(link, &gateway, NULL) >= 0) { + if (link_get_ipv6_address(link, &gateway, 0, NULL) >= 0) { if (DEBUG_LOGGING) { _cleanup_free_ char *buffer = NULL; @@ -406,7 +410,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r if (!link->network->ipv6_accept_ra_use_autonomous_prefix) return 0; - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); + r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); @@ -506,7 +510,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { if (lifetime_sec == 0) return 0; - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); + r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); @@ -634,7 +638,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m"); - if (link_get_ipv6_address(link, &gateway, NULL) >= 0) { + if (link_get_ipv6_address(link, &gateway, 0, NULL) >= 0) { if (DEBUG_LOGGING) { _cleanup_free_ char *buf = NULL; @@ -648,7 +652,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m"); - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); + r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); @@ -705,7 +709,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get router address from RA: %m"); - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); + r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); @@ -786,7 +790,6 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { struct in6_addr router; uint32_t lifetime_sec; bool updated = false; - char **j; int r; assert(link); @@ -800,7 +803,7 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { if (r < 0) return log_link_error_errno(link, r, "Failed to get router address from RA: %m"); - r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), ×tamp_usec); + r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec); if (r < 0) return log_link_error_errno(link, r, "Failed to get RA timestamp: %m"); @@ -1055,7 +1058,7 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router } } -int ndisc_configure(Link *link) { +static int ndisc_configure(Link *link) { int r; assert(link); @@ -1090,6 +1093,8 @@ int ndisc_configure(Link *link) { } int ndisc_start(Link *link) { + int r; + assert(link); if (!link->ndisc || !link->dhcp6_client) @@ -1103,7 +1108,55 @@ int ndisc_start(Link *link) { log_link_debug(link, "Discovering IPv6 routers"); - return sd_ndisc_start(link->ndisc); + r = sd_ndisc_start(link->ndisc); + if (r < 0) + return r; + + return 1; +} + +static int ndisc_process_request(Request *req, Link *link, void *userdata) { + int r; + + assert(link); + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return 0; + + if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr)) + /* No MAC address is assigned to the hardware, or non-supported MAC address length. */ + return 0; + + r = ndisc_configure(link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Discovery: %m"); + + r = ndisc_start(link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m"); + + log_link_debug(link, "IPv6 Router Discovery is configured%s.", + r > 0 ? " and started" : ""); + return 1; +} + +int link_request_ndisc(Link *link) { + int r; + + assert(link); + + if (!link_ipv6_accept_ra_enabled(link)) + return 0; + + if (link->ndisc) + return 0; + + r = link_queue_request(link, REQUEST_TYPE_NDISC, ndisc_process_request, NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Discovery: %m"); + + log_link_debug(link, "Requested configuring of the IPv6 Router Discovery."); + return 0; } void ndisc_vacuum(Link *link) { @@ -1115,7 +1168,7 @@ void ndisc_vacuum(Link *link) { /* Removes all RDNSS and DNSSL entries whose validity time has passed */ - time_now = now(clock_boottime_or_monotonic()); + time_now = now(CLOCK_BOOTTIME); SET_FOREACH(r, link->ndisc_rdnss) if (r->lifetime_usec < time_now) diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index acb97a892..f88a002c7 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -43,10 +43,11 @@ bool link_ipv6_accept_ra_enabled(Link *link); void network_adjust_ipv6_accept_ra(Network *network); -int ndisc_configure(Link *link); int ndisc_start(Link *link); void ndisc_vacuum(Link *link); void ndisc_flush(Link *link); +int link_request_ndisc(Link *link); + CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_start_dhcp6_client); CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_accept_ra_use_domains); diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index 1766095e5..435fa7002 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -19,7 +19,7 @@ Neighbor *neighbor_free(Neighbor *neighbor) { hashmap_remove(neighbor->network->neighbors_by_section, neighbor->section); } - network_config_section_free(neighbor->section); + config_section_free(neighbor->section); if (neighbor->link) set_remove(neighbor->link->neighbors, neighbor); @@ -27,10 +27,10 @@ Neighbor *neighbor_free(Neighbor *neighbor) { return mfree(neighbor); } -DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(Neighbor, neighbor_free); static int neighbor_new_static(Network *network, const char *filename, unsigned section_line, Neighbor **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(neighbor_freep) Neighbor *neighbor = NULL; int r; @@ -39,7 +39,7 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -60,7 +60,7 @@ static int neighbor_new_static(Network *network, const char *filename, unsigned .source = NETWORK_CONFIG_SOURCE_STATIC, }; - r = hashmap_ensure_put(&network->neighbors_by_section, &network_config_hash_ops, neighbor->section, neighbor); + r = hashmap_ensure_put(&network->neighbors_by_section, &config_section_hash_ops, neighbor->section, neighbor); if (r < 0) return r; @@ -87,7 +87,7 @@ static int neighbor_dup(const Neighbor *neighbor, Neighbor **ret) { return 0; } -void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) { +static void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) { assert(neighbor); siphash24_compress(&neighbor->family, sizeof(neighbor->family), state); @@ -106,7 +106,7 @@ void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state) { hw_addr_hash_func(&neighbor->ll_addr, state); } -int neighbor_compare_func(const Neighbor *a, const Neighbor *b) { +static int neighbor_compare_func(const Neighbor *a, const Neighbor *b) { int r; r = CMP(a->family, b->family); @@ -175,12 +175,26 @@ static void log_neighbor_debug(const Neighbor *neighbor, const char *str, const HW_ADDR_TO_STR(&neighbor->ll_addr), strna(dst)); } -static int neighbor_configure( - Neighbor *neighbor, - Link *link, - link_netlink_message_handler_t callback) { +static int neighbor_configure_message(Neighbor *neighbor, Link *link, sd_netlink_message *req) { + int r; - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT); + if (r < 0) + return r; + + r = netlink_message_append_hw_addr(req, NDA_LLADDR, &neighbor->ll_addr); + if (r < 0) + return r; + + r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr); + if (r < 0) + return r; + + return 0; +} + +static int neighbor_configure(Neighbor *neighbor, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert(neighbor); @@ -188,49 +202,45 @@ static int neighbor_configure( assert(link->ifindex > 0); assert(link->manager); assert(link->manager->rtnl); - assert(callback); + assert(req); log_neighbor_debug(neighbor, "Configuring", link); - r = sd_rtnl_message_new_neigh(link->manager->rtnl, &req, RTM_NEWNEIGH, + r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH, link->ifindex, neighbor->family); if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_NEWNEIGH message: %m"); + return r; - r = sd_rtnl_message_neigh_set_state(req, NUD_PERMANENT); + r = neighbor_configure_message(neighbor, link, m); if (r < 0) - return log_link_error_errno(link, r, "Could not set state: %m"); + return r; - r = netlink_message_append_hw_addr(req, NDA_LLADDR, &neighbor->ll_addr); - if (r < 0) - return log_link_error_errno(link, r, "Could not append NDA_LLADDR attribute: %m"); - - r = netlink_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr); - if (r < 0) - return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); - - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - neighbor_enter_configuring(neighbor); - return r; + return request_call_netlink_async(link->manager->rtnl, m, req); } -static int static_neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int neighbor_process_request(Request *req, Link *link, Neighbor *neighbor) { + int r; + + assert(req); + assert(link); + assert(neighbor); + + if (!link_is_ready_to_configure(link, false)) + return 0; + + r = neighbor_configure(neighbor, link, req); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure neighbor: %m"); + + neighbor_enter_configuring(neighbor); + return 1; +} + +static int static_neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Neighbor *neighbor) { int r; assert(m); assert(link); - assert(link->static_neighbor_messages > 0); - - link->static_neighbor_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) { @@ -248,13 +258,7 @@ static int static_neighbor_configure_handler(sd_netlink *rtnl, sd_netlink_messag return 1; } -static int link_request_neighbor( - Link *link, - const Neighbor *neighbor, - unsigned *message_counter, - link_netlink_message_handler_t netlink_handler, - Request **ret) { - +static int link_request_neighbor(Link *link, const Neighbor *neighbor) { Neighbor *existing; int r; @@ -278,8 +282,14 @@ static int link_request_neighbor( existing->source = neighbor->source; log_neighbor_debug(existing, "Requesting", link); - r = link_queue_request(link, REQUEST_TYPE_NEIGHBOR, existing, false, - message_counter, netlink_handler, ret); + r = link_queue_request_safe(link, REQUEST_TYPE_NEIGHBOR, + existing, NULL, + neighbor_hash_func, + neighbor_compare_func, + neighbor_process_request, + &link->static_neighbor_messages, + static_neighbor_configure_handler, + NULL); if (r <= 0) return r; @@ -298,8 +308,7 @@ int link_request_static_neighbors(Link *link) { link->static_neighbors_configured = false; HASHMAP_FOREACH(neighbor, link->network->neighbors_by_section) { - r = link_request_neighbor(link, neighbor, &link->static_neighbor_messages, - static_neighbor_configure_handler, NULL); + r = link_request_neighbor(link, neighbor); if (r < 0) return log_link_warning_errno(link, r, "Could not request neighbor: %m"); } @@ -406,13 +415,17 @@ int link_drop_foreign_neighbors(Link *link) { return r; } -int link_drop_neighbors(Link *link) { +int link_drop_managed_neighbors(Link *link) { Neighbor *neighbor; int k, r = 0; assert(link); SET_FOREACH(neighbor, link->neighbors) { + /* Do not touch nexthops managed by kernel or other tools. */ + if (neighbor->source == NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + /* Ignore neighbors not assigned yet or already removing. */ if (!neighbor_exists(neighbor)) continue; @@ -434,24 +447,6 @@ void link_foreignize_neighbors(Link *link) { neighbor->source = NETWORK_CONFIG_SOURCE_FOREIGN; } -int request_process_neighbor(Request *req) { - int r; - - assert(req); - assert(req->link); - assert(req->neighbor); - assert(req->type == REQUEST_TYPE_NEIGHBOR); - - if (!link_is_ready_to_configure(req->link, false)) - return 0; - - r = neighbor_configure(req->neighbor, req->link, req->netlink_handler); - if (r < 0) - return r; - - return 1; -} - int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { _cleanup_(neighbor_freep) Neighbor *tmp = NULL; Neighbor *neighbor = NULL; diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h index e9e185411..758475a8f 100644 --- a/src/network/networkd-neighbor.h +++ b/src/network/networkd-neighbor.h @@ -13,12 +13,11 @@ typedef struct Link Link; typedef struct Manager Manager; typedef struct Network Network; -typedef struct Request Request; typedef struct Neighbor { Network *network; Link *link; - NetworkConfigSection *section; + ConfigSection *section; NetworkConfigSource source; NetworkConfigState state; @@ -29,17 +28,13 @@ typedef struct Neighbor { Neighbor *neighbor_free(Neighbor *neighbor); -void neighbor_hash_func(const Neighbor *neighbor, struct siphash *state); -int neighbor_compare_func(const Neighbor *a, const Neighbor *b); - void network_drop_invalid_neighbors(Network *network); -int link_drop_neighbors(Link *link); +int link_drop_managed_neighbors(Link *link); int link_drop_foreign_neighbors(Link *link); void link_foreignize_neighbors(Link *link); int link_request_static_neighbors(Link *link); -int request_process_neighbor(Request *req); int manager_rtnl_process_neighbor(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 4c3bf9731..ceaaa6a0f 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -22,6 +22,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "networkd-dhcp6.h" #include "networkd-ipv4ll.h" #include "networkd-ipv6-proxy-ndp.h" +#include "networkd-ipv6ll.h" #include "networkd-lldp-tx.h" #include "networkd-ndisc.h" #include "networkd-network.h" @@ -51,6 +52,7 @@ Match.PermanentMACAddress, config_parse_hw_addrs, Match.Path, config_parse_match_strv, 0, offsetof(Network, match.path) Match.Driver, config_parse_match_strv, 0, offsetof(Network, match.driver) Match.Type, config_parse_match_strv, 0, offsetof(Network, match.iftype) +Match.Kind, config_parse_match_strv, 0, offsetof(Network, match.kind) Match.WLANInterfaceType, config_parse_match_strv, 0, offsetof(Network, match.wlan_iftype) Match.SSID, config_parse_match_strv, 0, offsetof(Network, match.ssid) Match.BSSID, config_parse_ether_addrs, 0, offsetof(Network, match.bssid) @@ -73,15 +75,15 @@ Link.Unmanaged, config_parse_bool, Link.ActivationPolicy, config_parse_activation_policy, 0, offsetof(Network, activation_policy) Link.RequiredForOnline, config_parse_required_for_online, 0, 0 Link.RequiredFamilyForOnline, config_parse_required_family_for_online, 0, offsetof(Network, required_family_for_online) -SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, 0 -SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, 0 -SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, 0 -SR-IOV.VLANProtocol, config_parse_sr_iov_vlan_proto, 0, 0 -SR-IOV.MACSpoofCheck, config_parse_sr_iov_boolean, 0, 0 -SR-IOV.QueryReceiveSideScaling, config_parse_sr_iov_boolean, 0, 0 -SR-IOV.Trust, config_parse_sr_iov_boolean, 0, 0 -SR-IOV.LinkState, config_parse_sr_iov_link_state, 0, 0 -SR-IOV.MACAddress, config_parse_sr_iov_mac, 0, 0 +SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.VLANProtocol, config_parse_sr_iov_vlan_proto, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.MACSpoofCheck, config_parse_sr_iov_boolean, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.QueryReceiveSideScaling, config_parse_sr_iov_boolean, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.Trust, config_parse_sr_iov_boolean, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.LinkState, config_parse_sr_iov_link_state, 0, offsetof(Network, sr_iov_by_section) +SR-IOV.MACAddress, config_parse_sr_iov_mac, 0, offsetof(Network, sr_iov_by_section) Network.Description, config_parse_string, 0, offsetof(Network, description) Network.KeepMaster, config_parse_bool, 0, offsetof(Network, keep_master) Network.BatmanAdvanced, config_parse_ifname, 0, offsetof(Network, batadv_name) @@ -91,7 +93,7 @@ Network.VRF, config_parse_ifname, Network.IPoIB, config_parse_stacked_netdev, NETDEV_KIND_IPOIB, offsetof(Network, stacked_netdev_names) Network.IPVLAN, config_parse_stacked_netdev, NETDEV_KIND_IPVLAN, offsetof(Network, stacked_netdev_names) Network.IPVTAP, config_parse_stacked_netdev, NETDEV_KIND_IPVTAP, offsetof(Network, stacked_netdev_names) -Network.L2TP, config_parse_stacked_netdev, NETDEV_KIND_L2TP, offsetof(Network, stacked_netdev_names) +Network.L2TP, config_parse_warn_compat, DISABLED_LEGACY, 0 Network.MACsec, config_parse_stacked_netdev, NETDEV_KIND_MACSEC, offsetof(Network, stacked_netdev_names) Network.MACVLAN, config_parse_stacked_netdev, NETDEV_KIND_MACVLAN, offsetof(Network, stacked_netdev_names) Network.MACVTAP, config_parse_stacked_netdev, NETDEV_KIND_MACVTAP, offsetof(Network, stacked_netdev_names) @@ -220,7 +222,7 @@ DHCPv4.SendHostname, config_parse_bool, DHCPv4.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname) DHCPv4.Label, config_parse_dhcp_label, 0, offsetof(Network, dhcp_label) DHCPv4.RequestBroadcast, config_parse_tristate, 0, offsetof(Network, dhcp_broadcast) -DHCPv4.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) +DHCPv4.VendorClassIdentifier, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_vendor_class_identifier) DHCPv4.MUDURL, config_parse_mud_url, 0, offsetof(Network, dhcp_mudurl) DHCPv4.MaxAttempts, config_parse_dhcp_max_attempts, 0, 0 DHCPv4.UserClass, config_parse_dhcp_user_or_vendor_class, AF_INET, offsetof(Network, dhcp_user_class) @@ -303,11 +305,15 @@ DHCPServer.PoolSize, config_parse_uint32, DHCPServer.SendVendorOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_vendor_options) DHCPServer.SendOption, config_parse_dhcp_send_option, 0, offsetof(Network, dhcp_server_send_options) DHCPServer.BindToInterface, config_parse_bool, 0, offsetof(Network, dhcp_server_bind_to_interface) +DHCPServer.BootServerAddress, config_parse_in_addr_non_null, AF_INET, offsetof(Network, dhcp_server_boot_server_address) +DHCPServer.BootServerName, config_parse_dns_name, 0, offsetof(Network, dhcp_server_boot_server_name) +DHCPServer.BootFilename, config_parse_string, CONFIG_PARSE_STRING_SAFE_AND_ASCII, offsetof(Network, dhcp_server_boot_filename) DHCPServerStaticLease.Address, config_parse_dhcp_static_lease_address, 0, 0 DHCPServerStaticLease.MACAddress, config_parse_dhcp_static_lease_hwaddr, 0, 0 Bridge.Cost, config_parse_uint32, 0, offsetof(Network, cost) Bridge.UseBPDU, config_parse_tristate, 0, offsetof(Network, use_bpdu) Bridge.HairPin, config_parse_tristate, 0, offsetof(Network, hairpin) +Bridge.Isolated, config_parse_tristate, 0, offsetof(Network, isolated) Bridge.FastLeave, config_parse_tristate, 0, offsetof(Network, fast_leave) Bridge.AllowPortToBeRoot, config_parse_tristate, 0, offsetof(Network, allow_port_to_be_root) Bridge.UnicastFlood, config_parse_tristate, 0, offsetof(Network, unicast_flood) @@ -538,7 +544,7 @@ DHCP.SendHostname, config_parse_bool, DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname) DHCP.RequestBroadcast, config_parse_tristate, 0, offsetof(Network, dhcp_broadcast) DHCP.CriticalConnection, config_parse_tristate, 0, offsetof(Network, dhcp_critical) -DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) +DHCP.VendorClassIdentifier, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Network, dhcp_vendor_class_identifier) DHCP.UserClass, config_parse_dhcp_user_or_vendor_class, AF_INET, offsetof(Network, dhcp_user_class) DHCP.IAID, config_parse_iaid, AF_INET, 0 DHCP.DUIDType, config_parse_network_duid_type, 0, 0 diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 873ad2e70..9acfc863f 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -33,6 +33,7 @@ #include "networkd-sriov.h" #include "parse-util.h" #include "path-lookup.h" +#include "qdisc.h" #include "radv-internal.h" #include "set.h" #include "socket-util.h" @@ -40,7 +41,7 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" -#include "tc.h" +#include "tclass.h" #include "util.h" /* Let's assume that anything above this number is a user misconfiguration. */ @@ -124,6 +125,7 @@ int network_verify(Network *network) { int r; assert(network); + assert(network->manager); assert(network->filename); if (net_match_is_empty(&network->match) && !network->conditions) @@ -248,10 +250,11 @@ int network_verify(Network *network) { } 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) + if (network->keep_configuration >= 0) { + if (network->manager->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 @@ -320,8 +323,11 @@ int network_verify(Network *network) { network_drop_invalid_prefixes(network); network_drop_invalid_route_prefixes(network); network_drop_invalid_routing_policy_rules(network); - network_drop_invalid_traffic_control(network); - network_drop_invalid_sr_iov(network); + network_drop_invalid_qdisc(network); + network_drop_invalid_tclass(network); + r = sr_iov_drop_invalid_sections(UINT32_MAX, network->sr_iov_by_section); + if (r < 0) + return r; network_drop_invalid_static_leases(network); network_adjust_dhcp_server(network); @@ -384,7 +390,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .allmulticast = -1, .promiscuous = -1, - .keep_configuration = _KEEP_CONFIGURATION_INVALID, + .keep_configuration = manager->keep_configuration, .dhcp_duid.type = _DUID_TYPE_INVALID, .dhcp_critical = -1, @@ -433,6 +439,7 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi .use_bpdu = -1, .hairpin = -1, + .isolated = -1, .fast_leave = -1, .allow_port_to_be_root = -1, .unicast_flood = -1, @@ -573,7 +580,6 @@ int network_load_one(Manager *manager, OrderedHashmap **networks, const char *fi int network_load(Manager *manager, OrderedHashmap **networks) { _cleanup_strv_free_ char **files = NULL; - char **f; int r; assert(manager); @@ -695,6 +701,8 @@ static Network *network_free(Network *network) { free(network->dhcp_server_relay_agent_circuit_id); free(network->dhcp_server_relay_agent_remote_id); + free(network->dhcp_server_boot_server_name); + free(network->dhcp_server_boot_filename); free(network->description); free(network->dhcp_vendor_class_identifier); @@ -750,7 +758,8 @@ static Network *network_free(Network *network) { hashmap_free_with_destructor(network->rules_by_section, routing_policy_rule_free); hashmap_free_with_destructor(network->dhcp_static_leases_by_section, dhcp_static_lease_free); ordered_hashmap_free_with_destructor(network->sr_iov_by_section, sr_iov_free); - ordered_hashmap_free_with_destructor(network->tc_by_section, traffic_control_free); + hashmap_free_with_destructor(network->qdiscs_by_section, qdisc_free); + hashmap_free_with_destructor(network->tclasses_by_section, tclass_free); free(network->name); @@ -862,7 +871,6 @@ int config_parse_stacked_netdev( NETDEV_KIND_IPOIB, NETDEV_KIND_IPVLAN, NETDEV_KIND_IPVTAP, - NETDEV_KIND_L2TP, NETDEV_KIND_MACSEC, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP, @@ -974,52 +982,6 @@ int config_parse_domains( } } -int config_parse_hostname( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - char **hostname = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - if (isempty(rvalue)) { - *hostname = mfree(*hostname); - return 0; - } - - if (!hostname_is_valid(rvalue, 0)) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Hostname is not valid, ignoring assignment: %s", rvalue); - return 0; - } - - r = dns_name_is_valid(rvalue); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue); - return 0; - } - if (r == 0) { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue); - return 0; - } - - return free_and_strdup_warn(hostname, rvalue); -} - int config_parse_timezone( const char *unit, const char *filename, @@ -1381,16 +1343,6 @@ static const char* const keep_configuration_table[_KEEP_CONFIGURATION_MAX] = { DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(keep_configuration, KeepConfiguration, KEEP_CONFIGURATION_YES); -static const char* const ipv6_link_local_address_gen_mode_table[_IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX] = { - [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64] = "eui64", - [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE] = "none", - [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY] = "stable-privacy", - [IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM] = "random", -}; - -DEFINE_STRING_TABLE_LOOKUP(ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode); -DEFINE_CONFIG_PARSE_ENUM(config_parse_ipv6_link_local_address_gen_mode, ipv6_link_local_address_gen_mode, IPv6LinkLocalAddressGenMode, "Failed to parse IPv6 link local address generation mode"); - static const char* const activation_policy_table[_ACTIVATION_POLICY_MAX] = { [ACTIVATION_POLICY_UP] = "up", [ACTIVATION_POLICY_ALWAYS_UP] = "always-up", diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index f7eb37ace..807e0fadc 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -18,6 +18,7 @@ #include "networkd-dhcp-common.h" #include "networkd-dhcp4.h" #include "networkd-dhcp6.h" +#include "networkd-ipv6ll.h" #include "networkd-lldp-rx.h" #include "networkd-ndisc.h" #include "networkd-radv.h" @@ -38,15 +39,6 @@ typedef enum KeepConfiguration { _KEEP_CONFIGURATION_INVALID = -EINVAL, } KeepConfiguration; -typedef enum IPv6LinkLocalAddressGenMode { - IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_EUI64 = IN6_ADDR_GEN_MODE_EUI64, - IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE = IN6_ADDR_GEN_MODE_NONE, - IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY = IN6_ADDR_GEN_MODE_STABLE_PRIVACY, - IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_RANDOM = IN6_ADDR_GEN_MODE_RANDOM, - _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_MAX, - _IPV6_LINK_LOCAL_ADDRESS_GEN_MODE_INVALID = -EINVAL, -} IPv6LinkLocalAddressGenMode; - typedef enum ActivationPolicy { ACTIVATION_POLICY_UP, ACTIVATION_POLICY_ALWAYS_UP, @@ -208,6 +200,9 @@ struct Network { uint32_t dhcp_server_pool_size; OrderedHashmap *dhcp_server_send_options; OrderedHashmap *dhcp_server_send_vendor_options; + struct in_addr dhcp_server_boot_server_address; + char *dhcp_server_boot_server_name; + char *dhcp_server_boot_filename; /* link local addressing support */ AddressFamily link_local; @@ -244,6 +239,7 @@ struct Network { /* Bridge Support */ int use_bpdu; int hairpin; + int isolated; int fast_leave; int allow_port_to_be_root; int unicast_flood; @@ -340,7 +336,8 @@ struct Network { Hashmap *route_prefixes_by_section; Hashmap *rules_by_section; Hashmap *dhcp_static_leases_by_section; - OrderedHashmap *tc_by_section; + Hashmap *qdiscs_by_section; + Hashmap *tclasses_by_section; OrderedHashmap *sr_iov_by_section; /* All kinds of DNS configuration */ @@ -378,14 +375,12 @@ CONFIG_PARSER_PROTOTYPE(config_parse_stacked_netdev); CONFIG_PARSER_PROTOTYPE(config_parse_tunnel); CONFIG_PARSER_PROTOTYPE(config_parse_domains); CONFIG_PARSER_PROTOTYPE(config_parse_dns); -CONFIG_PARSER_PROTOTYPE(config_parse_hostname); CONFIG_PARSER_PROTOTYPE(config_parse_timezone); CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_negative_trust_anchors); CONFIG_PARSER_PROTOTYPE(config_parse_ntp); CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online); CONFIG_PARSER_PROTOTYPE(config_parse_required_family_for_online); CONFIG_PARSER_PROTOTYPE(config_parse_keep_configuration); -CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_link_local_address_gen_mode); CONFIG_PARSER_PROTOTYPE(config_parse_activation_policy); CONFIG_PARSER_PROTOTYPE(config_parse_link_group); CONFIG_PARSER_PROTOTYPE(config_parse_ignore_carrier_loss); @@ -395,8 +390,5 @@ const struct ConfigPerfItem* network_network_gperf_lookup(const char *key, GPERF const char* keep_configuration_to_string(KeepConfiguration i) _const_; KeepConfiguration keep_configuration_from_string(const char *s) _pure_; -const char* ipv6_link_local_address_gen_mode_to_string(IPv6LinkLocalAddressGenMode s) _const_; -IPv6LinkLocalAddressGenMode ipv6_link_local_address_gen_mode_from_string(const char *s) _pure_; - const char* activation_policy_to_string(ActivationPolicy i) _const_; ActivationPolicy activation_policy_from_string(const char *s) _pure_; diff --git a/src/network/networkd-nexthop.c b/src/network/networkd-nexthop.c index b829aaab9..3eb9d62f7 100644 --- a/src/network/networkd-nexthop.c +++ b/src/network/networkd-nexthop.c @@ -27,7 +27,7 @@ NextHop *nexthop_free(NextHop *nexthop) { hashmap_remove(nexthop->network->nexthops_by_section, nexthop->section); } - network_config_section_free(nexthop->section); + config_section_free(nexthop->section); if (nexthop->link) { set_remove(nexthop->link->nexthops, nexthop); @@ -48,7 +48,7 @@ NextHop *nexthop_free(NextHop *nexthop) { return mfree(nexthop); } -DEFINE_NETWORK_SECTION_FUNCTIONS(NextHop, nexthop_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(NextHop, nexthop_free); static int nexthop_new(NextHop **ret) { _cleanup_(nexthop_freep) NextHop *nexthop = NULL; @@ -68,7 +68,7 @@ static int nexthop_new(NextHop **ret) { } static int nexthop_new_static(Network *network, const char *filename, unsigned section_line, NextHop **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(nexthop_freep) NextHop *nexthop = NULL; int r; @@ -77,7 +77,7 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -96,7 +96,7 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s nexthop->section = TAKE_PTR(n); nexthop->source = NETWORK_CONFIG_SOURCE_STATIC; - r = hashmap_ensure_put(&network->nexthops_by_section, &network_config_hash_ops, nexthop->section, nexthop); + r = hashmap_ensure_put(&network->nexthops_by_section, &config_section_hash_ops, nexthop->section, nexthop); if (r < 0) return r; @@ -104,7 +104,7 @@ static int nexthop_new_static(Network *network, const char *filename, unsigned s return 0; } -void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) { +static void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) { assert(nexthop); siphash24_compress(&nexthop->protocol, sizeof(nexthop->protocol), state); @@ -124,7 +124,7 @@ void nexthop_hash_func(const NextHop *nexthop, struct siphash *state) { } } -int nexthop_compare_func(const NextHop *a, const NextHop *b) { +static int nexthop_compare_func(const NextHop *a, const NextHop *b) { int r; r = CMP(a->protocol, b->protocol); @@ -425,33 +425,28 @@ static int nexthop_remove(NextHop *nexthop) { return 0; } -static int nexthop_configure( - NextHop *nexthop, - Link *link, - link_netlink_message_handler_t callback) { - - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int nexthop_configure(NextHop *nexthop, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; + assert(nexthop); + assert(IN_SET(nexthop->family, AF_UNSPEC, AF_INET, AF_INET6)); assert(link); assert(link->manager); assert(link->manager->rtnl); assert(link->ifindex > 0); - assert(IN_SET(nexthop->family, AF_UNSPEC, AF_INET, AF_INET6)); - assert(callback); + assert(req); log_nexthop_debug(nexthop, "Configuring", link); - r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &req, - RTM_NEWNEXTHOP, nexthop->family, - nexthop->protocol); + r = sd_rtnl_message_new_nexthop(link->manager->rtnl, &m, RTM_NEWNEXTHOP, nexthop->family, nexthop->protocol); if (r < 0) - return log_link_error_errno(link, r, "Could not create RTM_NEWNEXTHOP message: %m"); + return r; if (nexthop->id > 0) { - r = sd_netlink_message_append_u32(req, NHA_ID, nexthop->id); + r = sd_netlink_message_append_u32(m, NHA_ID, nexthop->id); if (r < 0) - return log_link_error_errno(link, r, "Could not append NHA_ID attribute: %m"); + return r; } if (!hashmap_isempty(nexthop->group)) { @@ -466,51 +461,38 @@ static int nexthop_configure( HASHMAP_FOREACH(nhg, nexthop->group) *p++ = *nhg; - r = sd_netlink_message_append_data(req, NHA_GROUP, group, sizeof(struct nexthop_grp) * hashmap_size(nexthop->group)); + r = sd_netlink_message_append_data(m, NHA_GROUP, group, sizeof(struct nexthop_grp) * hashmap_size(nexthop->group)); if (r < 0) - return log_link_error_errno(link, r, "Could not append NHA_GROUP attribute: %m"); + return r; } else if (nexthop->blackhole) { - r = sd_netlink_message_append_flag(req, NHA_BLACKHOLE); + r = sd_netlink_message_append_flag(m, NHA_BLACKHOLE); if (r < 0) - return log_link_error_errno(link, r, "Could not append NHA_BLACKHOLE attribute: %m"); + return r; } else { - r = sd_netlink_message_append_u32(req, NHA_OIF, link->ifindex); + r = sd_netlink_message_append_u32(m, NHA_OIF, link->ifindex); if (r < 0) - return log_link_error_errno(link, r, "Could not append NHA_OIF attribute: %m"); + return r; if (in_addr_is_set(nexthop->family, &nexthop->gw)) { - r = netlink_message_append_in_addr_union(req, NHA_GATEWAY, nexthop->family, &nexthop->gw); + r = netlink_message_append_in_addr_union(m, NHA_GATEWAY, nexthop->family, &nexthop->gw); if (r < 0) - return log_link_error_errno(link, r, "Could not append NHA_GATEWAY attribute: %m"); + return r; - r = sd_rtnl_message_nexthop_set_flags(req, nexthop->flags & RTNH_F_ONLINK); + r = sd_rtnl_message_nexthop_set_flags(m, nexthop->flags & RTNH_F_ONLINK); if (r < 0) - return log_link_error_errno(link, r, "Failed to set nexthop flags: %m"); + return r; } } - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - nexthop_enter_configuring(nexthop); - return 0; + return request_call_netlink_async(link->manager->rtnl, m, req); } -static int static_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int static_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, NextHop *nexthop) { int r; + assert(m); assert(link); - assert(link->static_nexthop_messages > 0); - - link->static_nexthop_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) { @@ -528,13 +510,69 @@ static int static_nexthop_handler(sd_netlink *rtnl, sd_netlink_message *m, Link return 1; } -static int link_request_nexthop( - Link *link, - NextHop *nexthop, - unsigned *message_counter, - link_netlink_message_handler_t netlink_handler, - Request **ret) { +static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { + struct nexthop_grp *nhg; + assert(link); + assert(nexthop); + + if (!link_is_ready_to_configure(link, false)) + return false; + + if (nexthop_owned_by_link(nexthop)) { + /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated + * when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of + * kernel. */ + if (link->set_flags_messages > 0) + return false; + if (!FLAGS_SET(link->flags, IFF_UP)) + return false; + } + + /* All group members must be configured first. */ + HASHMAP_FOREACH(nhg, nexthop->group) { + NextHop *g; + + if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0) + return false; + + if (!nexthop_exists(g)) + return false; + } + + if (nexthop->id == 0) { + Request *req; + + ORDERED_SET_FOREACH(req, link->manager->request_queue) { + if (req->type != REQUEST_TYPE_NEXTHOP) + continue; + if (((NextHop*) req->userdata)->id != 0) + return false; /* first configure nexthop with id. */ + } + } + + return gateway_is_ready(link, FLAGS_SET(nexthop->flags, RTNH_F_ONLINK), nexthop->family, &nexthop->gw); +} + +static int nexthop_process_request(Request *req, Link *link, NextHop *nexthop) { + int r; + + assert(req); + assert(link); + assert(nexthop); + + if (!nexthop_is_ready_to_configure(link, nexthop)) + return 0; + + r = nexthop_configure(nexthop, link, req); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure nexthop"); + + nexthop_enter_configuring(nexthop); + return 1; +} + +static int link_request_nexthop(Link *link, NextHop *nexthop) { NextHop *existing; int r; @@ -562,8 +600,14 @@ static int link_request_nexthop( existing->source = nexthop->source; log_nexthop_debug(existing, "Requesting", link); - r = link_queue_request(link, REQUEST_TYPE_NEXTHOP, existing, false, - message_counter, netlink_handler, ret); + r = link_queue_request_safe(link, REQUEST_TYPE_NEXTHOP, + existing, NULL, + nexthop_hash_func, + nexthop_compare_func, + nexthop_process_request, + &link->static_nexthop_messages, + static_nexthop_handler, + NULL); if (r <= 0) return r; @@ -584,8 +628,7 @@ int link_request_static_nexthops(Link *link, bool only_ipv4) { if (only_ipv4 && nh->family != AF_INET) continue; - r = link_request_nexthop(link, nh, &link->static_nexthop_messages, - static_nexthop_handler, NULL); + r = link_request_nexthop(link, nh); if (r < 0) return log_link_warning_errno(link, r, "Could not request nexthop: %m"); } @@ -613,8 +656,8 @@ static void manager_mark_nexthops(Manager *manager, bool foreign, const Link *ex if (nexthop->protocol == RTPROT_KERNEL) continue; - /* When 'foreign' is true, do not remove nexthops we configured. */ - if (foreign && nexthop->source != NETWORK_CONFIG_SOURCE_FOREIGN) + /* When 'foreign' is true, mark only foreign nexthops, and vice versa. */ + if (foreign != (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN)) continue; /* Ignore nexthops not assigned yet or already removed. */ @@ -641,7 +684,7 @@ static void manager_mark_nexthops(Manager *manager, bool foreign, const Link *ex } } -static int manager_drop_nexthops(Manager *manager) { +static int manager_drop_marked_nexthops(Manager *manager) { NextHop *nexthop; int k, r = 0; @@ -704,14 +747,14 @@ int link_drop_foreign_nexthops(Link *link) { manager_mark_nexthops(link->manager, /* foreign = */ true, NULL); - k = manager_drop_nexthops(link->manager); + k = manager_drop_marked_nexthops(link->manager); if (k < 0 && r >= 0) r = k; return r; } -int link_drop_nexthops(Link *link) { +int link_drop_managed_nexthops(Link *link) { NextHop *nexthop; int k, r = 0; @@ -723,6 +766,10 @@ int link_drop_nexthops(Link *link) { if (nexthop->protocol == RTPROT_KERNEL) continue; + /* Do not touch addresses managed by kernel or other tools. */ + if (nexthop->source == NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + /* Ignore nexthops not assigned yet or already removing. */ if (!nexthop_exists(nexthop)) continue; @@ -734,7 +781,7 @@ int link_drop_nexthops(Link *link) { manager_mark_nexthops(link->manager, /* foreign = */ false, link); - k = manager_drop_nexthops(link->manager); + k = manager_drop_marked_nexthops(link->manager); if (k < 0 && r >= 0) r = k; @@ -759,68 +806,6 @@ void link_foreignize_nexthops(Link *link) { } } -static bool nexthop_is_ready_to_configure(Link *link, const NextHop *nexthop) { - struct nexthop_grp *nhg; - - assert(link); - assert(nexthop); - - if (!link_is_ready_to_configure(link, false)) - return false; - - if (nexthop_owned_by_link(nexthop)) { - /* TODO: fdb nexthop does not require IFF_UP. The conditions below needs to be updated - * when fdb nexthop support is added. See rtm_to_nh_config() in net/ipv4/nexthop.c of - * kernel. */ - if (link->set_flags_messages > 0) - return false; - if (!FLAGS_SET(link->flags, IFF_UP)) - return false; - } - - /* All group members must be configured first. */ - HASHMAP_FOREACH(nhg, nexthop->group) { - NextHop *g; - - if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0) - return false; - - if (!nexthop_exists(g)) - return false; - } - - if (nexthop->id == 0) { - Request *req; - - ORDERED_SET_FOREACH(req, link->manager->request_queue) { - if (req->type != REQUEST_TYPE_NEXTHOP) - continue; - if (req->nexthop->id != 0) - return false; /* first configure nexthop with id. */ - } - } - - return gateway_is_ready(link, FLAGS_SET(nexthop->flags, RTNH_F_ONLINK), nexthop->family, &nexthop->gw); -} - -int request_process_nexthop(Request *req) { - int r; - - assert(req); - assert(req->link); - assert(req->nexthop); - assert(req->type == REQUEST_TYPE_NEXTHOP); - - if (!nexthop_is_ready_to_configure(req->link, req->nexthop)) - return 0; - - r = nexthop_configure(req->nexthop, req->link, req->netlink_handler); - if (r < 0) - return r; - - return 1; -} - int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { _cleanup_(nexthop_freep) NextHop *tmp = NULL; _cleanup_free_ void *raw_group = NULL; @@ -1216,7 +1201,7 @@ int config_parse_nexthop_family( return 0; } - switch(a) { + switch (a) { case ADDRESS_FAMILY_IPV4: n->family = AF_INET; break; diff --git a/src/network/networkd-nexthop.h b/src/network/networkd-nexthop.h index 7a8920238..6f2aa6f6d 100644 --- a/src/network/networkd-nexthop.h +++ b/src/network/networkd-nexthop.h @@ -16,13 +16,12 @@ typedef struct Link Link; typedef struct Manager Manager; typedef struct Network Network; -typedef struct Request Request; typedef struct NextHop { Network *network; Manager *manager; Link *link; - NetworkConfigSection *section; + ConfigSection *section; NetworkConfigSource source; NetworkConfigState state; @@ -39,17 +38,13 @@ typedef struct NextHop { NextHop *nexthop_free(NextHop *nexthop); -void nexthop_hash_func(const NextHop *nexthop, struct siphash *state); -int nexthop_compare_func(const NextHop *a, const NextHop *b); - void network_drop_invalid_nexthops(Network *network); -int link_drop_nexthops(Link *link); +int link_drop_managed_nexthops(Link *link); int link_drop_foreign_nexthops(Link *link); void link_foreignize_nexthops(Link *link); int link_request_static_nexthops(Link *link, bool only_ipv4); -int request_process_nexthop(Request *req); int manager_get_nexthop_by_id(Manager *manager, uint32_t id, NextHop **ret); int manager_rtnl_process_nexthop(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); diff --git a/src/network/networkd-queue.c b/src/network/networkd-queue.c index 8cd5300ce..cf2ffa032 100644 --- a/src/network/networkd-queue.c +++ b/src/network/networkd-queue.c @@ -1,148 +1,64 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "networkd-address.h" -#include "networkd-address-label.h" -#include "networkd-bridge-fdb.h" -#include "networkd-bridge-mdb.h" -#include "networkd-dhcp-server.h" -#include "networkd-dhcp4.h" -#include "networkd-dhcp6.h" -#include "networkd-ipv6-proxy-ndp.h" +#include "netdev.h" +#include "netlink-util.h" +#include "networkd-link.h" #include "networkd-manager.h" -#include "networkd-neighbor.h" -#include "networkd-nexthop.h" -#include "networkd-route.h" -#include "networkd-routing-policy-rule.h" #include "networkd-queue.h" -#include "networkd-setlink.h" - -static void request_free_object(RequestType type, void *object) { - switch(type) { - case REQUEST_TYPE_ACTIVATE_LINK: - break; - case REQUEST_TYPE_ADDRESS: - address_free(object); - break; - case REQUEST_TYPE_ADDRESS_LABEL: - address_label_free(object); - break; - case REQUEST_TYPE_BRIDGE_FDB: - bridge_fdb_free(object); - break; - case REQUEST_TYPE_BRIDGE_MDB: - bridge_mdb_free(object); - break; - case REQUEST_TYPE_DHCP_SERVER: - case REQUEST_TYPE_DHCP4_CLIENT: - case REQUEST_TYPE_DHCP6_CLIENT: - break; - case REQUEST_TYPE_IPV6_PROXY_NDP: - free(object); - break; - case REQUEST_TYPE_NEIGHBOR: - neighbor_free(object); - break; - case REQUEST_TYPE_NEXTHOP: - nexthop_free(object); - break; - case REQUEST_TYPE_RADV: - break; - case REQUEST_TYPE_ROUTE: - route_free(object); - break; - case REQUEST_TYPE_ROUTING_POLICY_RULE: - routing_policy_rule_free(object); - break; - case REQUEST_TYPE_SET_LINK: - case REQUEST_TYPE_STACKED_NETDEV: - case REQUEST_TYPE_UP_DOWN: - break; - default: - assert_not_reached(); - } -} +#include "string-table.h" static Request *request_free(Request *req) { if (!req) return NULL; - if (req->link && req->link->manager) - /* To prevent from triggering assertions in hash functions, remove this request before - * freeing object below. */ - ordered_set_remove(req->link->manager->request_queue, req); - if (req->consume_object) - request_free_object(req->type, req->object); - link_unref(req->link); + /* To prevent from triggering assertions in the hash and compare functions, remove this request + * before freeing userdata below. */ + if (req->manager) + ordered_set_remove(req->manager->request_queue, req); + + if (req->free_func) + req->free_func(req->userdata); + + if (req->counter) + (*req->counter)--; + + link_unref(req->link); /* link may be NULL, but link_unref() can handle it gracefully. */ return mfree(req); } -DEFINE_TRIVIAL_CLEANUP_FUNC(Request*, request_free); +DEFINE_TRIVIAL_REF_UNREF_FUNC(Request, request, request_free); +DEFINE_TRIVIAL_DESTRUCTOR(request_destroy_callback, Request, request_unref); + +void request_detach(Manager *manager, Request *req) { + assert(manager); -void request_drop(Request *req) { if (!req) return; - if (req->message_counter) - (*req->message_counter)--; + req = ordered_set_remove(manager->request_queue, req); + if (!req) + return; - request_free(req); + req->manager = NULL; + request_unref(req); } static void request_hash_func(const Request *req, struct siphash *state) { assert(req); - assert(req->link); assert(state); - siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state); + siphash24_compress_boolean(req->link, state); + if (req->link) + siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state); + siphash24_compress(&req->type, sizeof(req->type), state); - switch(req->type) { - case REQUEST_TYPE_ACTIVATE_LINK: - break; - case REQUEST_TYPE_ADDRESS: - address_hash_func(req->address, state); - break; - case REQUEST_TYPE_ADDRESS_LABEL: - case REQUEST_TYPE_BRIDGE_FDB: - case REQUEST_TYPE_BRIDGE_MDB: - case REQUEST_TYPE_STACKED_NETDEV: - /* TODO: Currently, these types do not have any specific hash and compare functions. - * Fortunately, all these objects are 'static', thus we can use the trivial functions. */ - trivial_hash_func(req->object, state); - break; - case REQUEST_TYPE_DHCP_SERVER: - case REQUEST_TYPE_DHCP4_CLIENT: - case REQUEST_TYPE_DHCP6_CLIENT: - /* These types do not have an object. */ - break; - case REQUEST_TYPE_IPV6_PROXY_NDP: - in6_addr_hash_func(req->ipv6_proxy_ndp, state); - break; - case REQUEST_TYPE_NEIGHBOR: - neighbor_hash_func(req->neighbor, state); - break; - case REQUEST_TYPE_NEXTHOP: - nexthop_hash_func(req->nexthop, state); - break; - case REQUEST_TYPE_RADV: - /* This type does not have an object. */ - break; - case REQUEST_TYPE_ROUTE: - route_hash_func(req->route, state); - break; - case REQUEST_TYPE_ROUTING_POLICY_RULE: - routing_policy_rule_hash_func(req->rule, state); - break; - case REQUEST_TYPE_SET_LINK: { - trivial_hash_func(req->set_link_operation_ptr, state); - break; - } - case REQUEST_TYPE_UP_DOWN: - break; - default: - assert_not_reached(); - } + siphash24_compress(&req->hash_func, sizeof(req->hash_func), state); + siphash24_compress(&req->compare_func, sizeof(req->compare_func), state); + + if (req->hash_func) + req->hash_func(req->userdata, state); } static int request_compare_func(const struct Request *a, const struct Request *b) { @@ -150,50 +66,33 @@ static int request_compare_func(const struct Request *a, const struct Request *b assert(a); assert(b); - assert(a->link); - assert(b->link); - r = CMP(a->link->ifindex, b->link->ifindex); + r = CMP(!!a->link, !!b->link); if (r != 0) return r; + if (a->link) { + r = CMP(a->link->ifindex, b->link->ifindex); + if (r != 0) + return r; + } + r = CMP(a->type, b->type); if (r != 0) return r; - switch (a->type) { - case REQUEST_TYPE_ACTIVATE_LINK: - return 0; - case REQUEST_TYPE_ADDRESS: - return address_compare_func(a->address, b->address); - case REQUEST_TYPE_ADDRESS_LABEL: - case REQUEST_TYPE_BRIDGE_FDB: - case REQUEST_TYPE_BRIDGE_MDB: - case REQUEST_TYPE_STACKED_NETDEV: - return trivial_compare_func(a->object, b->object); - case REQUEST_TYPE_DHCP_SERVER: - case REQUEST_TYPE_DHCP4_CLIENT: - case REQUEST_TYPE_DHCP6_CLIENT: - return 0; - case REQUEST_TYPE_IPV6_PROXY_NDP: - return in6_addr_compare_func(a->ipv6_proxy_ndp, b->ipv6_proxy_ndp); - case REQUEST_TYPE_NEIGHBOR: - return neighbor_compare_func(a->neighbor, b->neighbor); - case REQUEST_TYPE_NEXTHOP: - return nexthop_compare_func(a->nexthop, b->nexthop); - case REQUEST_TYPE_ROUTE: - return route_compare_func(a->route, b->route); - case REQUEST_TYPE_RADV: - return 0; - case REQUEST_TYPE_ROUTING_POLICY_RULE: - return routing_policy_rule_compare_func(a->rule, b->rule); - case REQUEST_TYPE_SET_LINK: - return trivial_compare_func(a->set_link_operation_ptr, b->set_link_operation_ptr); - case REQUEST_TYPE_UP_DOWN: - return 0; - default: - assert_not_reached(); - } + r = CMP(PTR_TO_UINT64(a->hash_func), PTR_TO_UINT64(b->hash_func)); + if (r != 0) + return r; + + r = CMP(PTR_TO_UINT64(a->compare_func), PTR_TO_UINT64(b->compare_func)); + if (r != 0) + return r; + + if (a->compare_func) + return a->compare_func(a->userdata, b->userdata); + + return 0; } DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( @@ -201,72 +100,62 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( Request, request_hash_func, request_compare_func, - request_free); + request_unref); -int link_queue_request( +static int request_new( + Manager *manager, Link *link, RequestType type, - void *object, - bool consume_object, - unsigned *message_counter, - link_netlink_message_handler_t netlink_handler, + void *userdata, + mfree_func_t free_func, + hash_func_t hash_func, + compare_func_t compare_func, + request_process_func_t process, + unsigned *counter, + request_netlink_handler_t netlink_handler, Request **ret) { - _cleanup_(request_freep) Request *req = NULL; + _cleanup_(request_unrefp) Request *req = NULL; Request *existing; int r; - assert(link); - assert(link->manager); - assert(type >= 0 && type < _REQUEST_TYPE_MAX); - assert(IN_SET(type, - REQUEST_TYPE_ACTIVATE_LINK, - REQUEST_TYPE_DHCP_SERVER, - REQUEST_TYPE_DHCP4_CLIENT, - REQUEST_TYPE_DHCP6_CLIENT, - REQUEST_TYPE_RADV, - REQUEST_TYPE_SET_LINK, - REQUEST_TYPE_UP_DOWN) || - object); - assert(IN_SET(type, - REQUEST_TYPE_DHCP_SERVER, - REQUEST_TYPE_DHCP4_CLIENT, - REQUEST_TYPE_DHCP6_CLIENT, - REQUEST_TYPE_RADV) || - netlink_handler); + assert(manager); + assert(process); req = new(Request, 1); if (!req) { - if (consume_object) - request_free_object(type, object); + if (free_func) + free_func(userdata); return -ENOMEM; } *req = (Request) { - .link = link_ref(link), + .n_ref = 1, + .link = link_ref(link), /* link may be NULL, but link_ref() handles it gracefully. */ .type = type, - .object = object, - .consume_object = consume_object, - .message_counter = message_counter, + .userdata = userdata, + .free_func = free_func, + .hash_func = hash_func, + .compare_func = compare_func, + .process = process, .netlink_handler = netlink_handler, }; - existing = ordered_set_get(link->manager->request_queue, req); + existing = ordered_set_get(manager->request_queue, req); if (existing) { - /* To prevent from removing the existing request. */ - req->link = link_unref(req->link); - if (ret) *ret = existing; return 0; } - r = ordered_set_ensure_put(&link->manager->request_queue, &request_hash_ops, req); + r = ordered_set_ensure_put(&manager->request_queue, &request_hash_ops, req); if (r < 0) return r; - if (req->message_counter) - (*req->message_counter)++; + req->manager = manager; + req->counter = counter; + if (req->counter) + (*req->counter)++; if (ret) *ret = req; @@ -275,84 +164,141 @@ int link_queue_request( return 1; } -int manager_process_requests(sd_event_source *s, void *userdata) { - Manager *manager = userdata; - int r; +int netdev_queue_request( + NetDev *netdev, + request_process_func_t process, + Request **ret) { - assert(manager); + assert(netdev); + + return request_new(netdev->manager, NULL, REQUEST_TYPE_NETDEV_INDEPENDENT, + netdev_ref(netdev), (mfree_func_t) netdev_unref, + trivial_hash_func, trivial_compare_func, + process, NULL, NULL, ret); +} + +int link_queue_request_full( + Link *link, + RequestType type, + void *userdata, + mfree_func_t free_func, + hash_func_t hash_func, + compare_func_t compare_func, + request_process_func_t process, + unsigned *counter, + request_netlink_handler_t netlink_handler, + Request **ret) { + + assert(link); + + return request_new(link->manager, link, type, + userdata, free_func, hash_func, compare_func, + process, counter, netlink_handler, ret); +} + +int manager_process_requests(sd_event_source *s, void *userdata) { + Manager *manager = ASSERT_PTR(userdata); + int r; for (;;) { bool processed = false; Request *req; ORDERED_SET_FOREACH(req, manager->request_queue) { - switch(req->type) { - case REQUEST_TYPE_ACTIVATE_LINK: - r = request_process_activation(req); + _unused_ _cleanup_(request_unrefp) Request *ref = request_ref(req); + _cleanup_(link_unrefp) Link *link = link_ref(req->link); + + assert(req->process); + + r = req->process(req, link, req->userdata); + if (r == 0) + continue; + + processed = true; + request_detach(manager, req); + + if (r < 0 && link) { + link_enter_failed(link); + /* link_enter_failed() may remove multiple requests, + * hence we need to exit from the loop. */ break; - case REQUEST_TYPE_ADDRESS: - r = request_process_address(req); - break; - case REQUEST_TYPE_ADDRESS_LABEL: - r = request_process_address_label(req); - break; - case REQUEST_TYPE_BRIDGE_FDB: - r = request_process_bridge_fdb(req); - break; - case REQUEST_TYPE_BRIDGE_MDB: - r = request_process_bridge_mdb(req); - break; - case REQUEST_TYPE_DHCP_SERVER: - r = request_process_dhcp_server(req); - break; - case REQUEST_TYPE_DHCP4_CLIENT: - r = request_process_dhcp4_client(req); - break; - case REQUEST_TYPE_DHCP6_CLIENT: - r = request_process_dhcp6_client(req); - break; - case REQUEST_TYPE_IPV6_PROXY_NDP: - r = request_process_ipv6_proxy_ndp_address(req); - break; - case REQUEST_TYPE_NEIGHBOR: - r = request_process_neighbor(req); - break; - case REQUEST_TYPE_NEXTHOP: - r = request_process_nexthop(req); - break; - case REQUEST_TYPE_RADV: - r = request_process_radv(req); - break; - case REQUEST_TYPE_ROUTE: - r = request_process_route(req); - break; - case REQUEST_TYPE_ROUTING_POLICY_RULE: - r = request_process_routing_policy_rule(req); - break; - case REQUEST_TYPE_SET_LINK: - r = request_process_set_link(req); - break; - case REQUEST_TYPE_STACKED_NETDEV: - r = request_process_stacked_netdev(req); - break; - case REQUEST_TYPE_UP_DOWN: - r = request_process_link_up_or_down(req); - break; - default: - return -EINVAL; - } - if (r < 0) - link_enter_failed(req->link); - if (r > 0) { - ordered_set_remove(manager->request_queue, req); - request_free(req); - processed = true; } } + /* When at least one request is processed, then another request may be ready now. */ if (!processed) break; } return 0; } + +static int request_netlink_handler(sd_netlink *nl, sd_netlink_message *m, Request *req) { + assert(req); + + if (req->counter) { + assert(*req->counter > 0); + (*req->counter)--; + req->counter = NULL; /* To prevent double decrement on free. */ + } + + if (req->link && IN_SET(req->link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; + + if (req->netlink_handler) + return req->netlink_handler(nl, m, req, req->link, req->userdata); + + return 0; +} + +int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req) { + int r; + + assert(nl); + assert(m); + assert(req); + + r = netlink_call_async(nl, NULL, m, request_netlink_handler, request_destroy_callback, req); + if (r < 0) + return r; + + request_ref(req); + return 0; +} + +static const char *const request_type_table[_REQUEST_TYPE_MAX] = { + [REQUEST_TYPE_ACTIVATE_LINK] = "activate link", + [REQUEST_TYPE_ADDRESS] = "address", + [REQUEST_TYPE_ADDRESS_LABEL] = "address label", + [REQUEST_TYPE_BRIDGE_FDB] = "bridge FDB", + [REQUEST_TYPE_BRIDGE_MDB] = "bridge MDB", + [REQUEST_TYPE_DHCP_SERVER] = "DHCP server", + [REQUEST_TYPE_DHCP4_CLIENT] = "DHCPv4 client", + [REQUEST_TYPE_DHCP6_CLIENT] = "DHCPv6 client", + [REQUEST_TYPE_IPV6_PROXY_NDP] = "IPv6 proxy NDP", + [REQUEST_TYPE_NDISC] = "NDisc", + [REQUEST_TYPE_NEIGHBOR] = "neighbor", + [REQUEST_TYPE_NETDEV_INDEPENDENT] = "independent netdev", + [REQUEST_TYPE_NETDEV_STACKED] = "stacked netdev", + [REQUEST_TYPE_NEXTHOP] = "nexthop", + [REQUEST_TYPE_RADV] = "RADV", + [REQUEST_TYPE_ROUTE] = "route", + [REQUEST_TYPE_ROUTING_POLICY_RULE] = "routing policy rule", + [REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE] = "IPv6LL address generation mode", + [REQUEST_TYPE_SET_LINK_BOND] = "bond configurations", + [REQUEST_TYPE_SET_LINK_BRIDGE] = "bridge configurations", + [REQUEST_TYPE_SET_LINK_BRIDGE_VLAN] = "bridge VLAN configurations", + [REQUEST_TYPE_SET_LINK_CAN] = "CAN interface configurations", + [REQUEST_TYPE_SET_LINK_FLAGS] = "link flags", + [REQUEST_TYPE_SET_LINK_GROUP] = "interface group", + [REQUEST_TYPE_SET_LINK_IPOIB] = "IPoIB configurations", + [REQUEST_TYPE_SET_LINK_MAC] = "MAC address", + [REQUEST_TYPE_SET_LINK_MASTER] = "master interface", + [REQUEST_TYPE_SET_LINK_MTU] = "MTU", + [REQUEST_TYPE_SRIOV] = "SR-IOV", + [REQUEST_TYPE_TC_QDISC] = "QDisc", + [REQUEST_TYPE_TC_CLASS] = "TClass", + [REQUEST_TYPE_UP_DOWN] = "bring link up or down", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(request_type, RequestType); diff --git a/src/network/networkd-queue.h b/src/network/networkd-queue.h index c0cdacfbd..6db0005e6 100644 --- a/src/network/networkd-queue.h +++ b/src/network/networkd-queue.h @@ -2,18 +2,18 @@ #pragma once #include "sd-event.h" +#include "sd-netlink.h" -#include "networkd-link.h" +#include "alloc-util.h" +#include "hash-funcs.h" -typedef struct Address Address; -typedef struct AddressLabel AddressLabel; -typedef struct BridgeFDB BridgeFDB; -typedef struct BridgeMDB BridgeMDB; -typedef struct Neighbor Neighbor; +typedef struct Link Link; typedef struct NetDev NetDev; -typedef struct NextHop NextHop; -typedef struct Route Route; -typedef struct RoutingPolicyRule RoutingPolicyRule; +typedef struct Manager Manager; +typedef struct Request Request; + +typedef int (*request_process_func_t)(Request *req, Link *link, void *userdata); +typedef int (*request_netlink_handler_t)(sd_netlink *nl, sd_netlink_message *m, Request *req, Link *link, void *userdata); typedef enum RequestType { REQUEST_TYPE_ACTIVATE_LINK, @@ -25,50 +25,115 @@ typedef enum RequestType { REQUEST_TYPE_DHCP4_CLIENT, REQUEST_TYPE_DHCP6_CLIENT, REQUEST_TYPE_IPV6_PROXY_NDP, + REQUEST_TYPE_NDISC, REQUEST_TYPE_NEIGHBOR, + REQUEST_TYPE_NETDEV_INDEPENDENT, + REQUEST_TYPE_NETDEV_STACKED, REQUEST_TYPE_NEXTHOP, REQUEST_TYPE_RADV, REQUEST_TYPE_ROUTE, REQUEST_TYPE_ROUTING_POLICY_RULE, - REQUEST_TYPE_SET_LINK, - REQUEST_TYPE_STACKED_NETDEV, + REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE, /* Setting IPv6LL address generation mode. */ + REQUEST_TYPE_SET_LINK_BOND, /* Setting bond configs. */ + REQUEST_TYPE_SET_LINK_BRIDGE, /* Setting bridge configs. */ + REQUEST_TYPE_SET_LINK_BRIDGE_VLAN, /* Setting bridge VLAN configs. */ + REQUEST_TYPE_SET_LINK_CAN, /* Setting CAN interface configs. */ + REQUEST_TYPE_SET_LINK_FLAGS, /* Setting IFF_NOARP or friends. */ + REQUEST_TYPE_SET_LINK_GROUP, /* Setting interface group. */ + REQUEST_TYPE_SET_LINK_IPOIB, /* Setting IPoIB configs. */ + REQUEST_TYPE_SET_LINK_MAC, /* Setting MAC address. */ + REQUEST_TYPE_SET_LINK_MASTER, /* Setting IFLA_MASTER. */ + REQUEST_TYPE_SET_LINK_MTU, /* Setting MTU. */ + REQUEST_TYPE_SRIOV, + REQUEST_TYPE_TC_CLASS, + REQUEST_TYPE_TC_QDISC, REQUEST_TYPE_UP_DOWN, _REQUEST_TYPE_MAX, _REQUEST_TYPE_INVALID = -EINVAL, } RequestType; -typedef struct Request { - Link *link; +struct Request { + unsigned n_ref; + + Manager *manager; /* must be non-NULL */ + Link *link; /* can be NULL */ + RequestType type; - bool consume_object; - union { - Address *address; - AddressLabel *label; - BridgeFDB *fdb; - BridgeMDB *mdb; - struct in6_addr *ipv6_proxy_ndp; - Neighbor *neighbor; - NextHop *nexthop; - Route *route; - RoutingPolicyRule *rule; - void *set_link_operation_ptr; - NetDev *netdev; - void *object; - }; + + /* Target object, e.g. Address, Route, NetDev, and so on. */ void *userdata; - unsigned *message_counter; - link_netlink_message_handler_t netlink_handler; -} Request; + /* freeing userdata when the request is completed or failed. */ + mfree_func_t free_func; -void request_drop(Request *req); + /* hash and compare functions for userdata, used for dedup requests. */ + hash_func_t hash_func; + compare_func_t compare_func; -int link_queue_request( - Link *link, - RequestType type, - void *object, - bool consume_object, - unsigned *message_counter, - link_netlink_message_handler_t netlink_handler, + /* Checks the request dependencies, and then processes this request, e.g. call address_configure(). + * Return 1 when processed, 0 when its dependencies not resolved, and negative errno on failure. */ + request_process_func_t process; + + /* incremented when requested, decremented when request is completed or failed. */ + unsigned *counter; + /* called in netlink handler, the 'counter' is decremented before this is called. + * If this is specified, then the 'process' function must increment the reference of this + * request, and pass this request to the netlink_call_async(), and set the destroy function + * to the slot. */ + request_netlink_handler_t netlink_handler; +}; + +Request *request_ref(Request *req); +Request *request_unref(Request *req); +DEFINE_TRIVIAL_CLEANUP_FUNC(Request*, request_unref); + +void request_detach(Manager *manager, Request *req); + +int netdev_queue_request( + NetDev *netdev, + request_process_func_t process, Request **ret); +int link_queue_request_full( + Link *link, + RequestType type, + void *userdata, + mfree_func_t free_func, + hash_func_t hash_func, + compare_func_t compare_func, + request_process_func_t process, + unsigned *counter, + request_netlink_handler_t netlink_handler, + Request **ret); + +static inline int link_queue_request( + Link *link, + RequestType type, + request_process_func_t process, + Request **ret) { + + return link_queue_request_full(link, type, NULL, NULL, NULL, NULL, + process, NULL, NULL, ret); +} + +#define link_queue_request_safe(link, type, userdata, free_func, hash_func, compare_func, process, counter, netlink_handler, ret) \ + ({ \ + typeof(userdata) (*_f)(typeof(userdata)) = (free_func); \ + void (*_h)(const typeof(*userdata)*, struct siphash*) = (hash_func); \ + int (*_c)(const typeof(*userdata)*, const typeof(*userdata)*) = (compare_func); \ + int (*_p)(Request*, Link*, typeof(userdata)) = (process); \ + int (*_n)(sd_netlink*, sd_netlink_message*, Request*, Link*, typeof(userdata)) = (netlink_handler); \ + \ + link_queue_request_full(link, type, userdata, \ + (mfree_func_t) _f, \ + (hash_func_t) _h, \ + (compare_func_t) _c, \ + (request_process_func_t) _p, \ + counter, \ + (request_netlink_handler_t) _n, \ + ret); \ + }) + int manager_process_requests(sd_event_source *s, void *userdata); +int request_call_netlink_async(sd_netlink *nl, sd_netlink_message *m, Request *req); + +const char* request_type_to_string(RequestType t) _const_; diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 59b0922a4..b9f5c55f4 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -57,6 +57,9 @@ bool link_radv_enabled(Link *link) { if (!link_may_have_ipv6ll(link)) return false; + if (link->hw_addr.length != ETH_ALEN) + return false; + return link->network->router_prefix_delegation; } @@ -69,16 +72,16 @@ Prefix *prefix_free(Prefix *prefix) { hashmap_remove(prefix->network->prefixes_by_section, prefix->section); } - network_config_section_free(prefix->section); + config_section_free(prefix->section); set_free(prefix->tokens); return mfree(prefix); } -DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(Prefix, prefix_free); static int prefix_new_static(Network *network, const char *filename, unsigned section_line, Prefix **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(prefix_freep) Prefix *prefix = NULL; int r; @@ -87,7 +90,7 @@ static int prefix_new_static(Network *network, const char *filename, unsigned se assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -111,7 +114,7 @@ static int prefix_new_static(Network *network, const char *filename, unsigned se .address_auto_configuration = true, }; - r = hashmap_ensure_put(&network->prefixes_by_section, &network_config_hash_ops, prefix->section, prefix); + r = hashmap_ensure_put(&network->prefixes_by_section, &config_section_hash_ops, prefix->section, prefix); if (r < 0) return r; @@ -128,15 +131,15 @@ RoutePrefix *route_prefix_free(RoutePrefix *prefix) { hashmap_remove(prefix->network->route_prefixes_by_section, prefix->section); } - network_config_section_free(prefix->section); + config_section_free(prefix->section); return mfree(prefix); } -DEFINE_NETWORK_SECTION_FUNCTIONS(RoutePrefix, route_prefix_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(RoutePrefix, route_prefix_free); static int route_prefix_new_static(Network *network, const char *filename, unsigned section_line, RoutePrefix **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(route_prefix_freep) RoutePrefix *prefix = NULL; int r; @@ -145,7 +148,7 @@ static int route_prefix_new_static(Network *network, const char *filename, unsig assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -166,7 +169,7 @@ static int route_prefix_new_static(Network *network, const char *filename, unsig .lifetime = RADV_DEFAULT_VALID_LIFETIME_USEC, }; - r = hashmap_ensure_put(&network->route_prefixes_by_section, &network_config_hash_ops, prefix->section, prefix); + r = hashmap_ensure_put(&network->route_prefixes_by_section, &config_section_hash_ops, prefix->section, prefix); if (r < 0) return r; @@ -547,6 +550,9 @@ static int radv_is_ready_to_configure(Link *link) { if (in6_addr_is_null(&link->ipv6ll_address)) return false; + if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr)) + return false; + if (link->network->router_emit_dns && !link->network->router_dns) { _cleanup_free_ struct in6_addr *dns = NULL; size_t n_dns; @@ -576,15 +582,10 @@ static int radv_is_ready_to_configure(Link *link) { return true; } -int request_process_radv(Request *req) { - Link *link; +static int radv_process_request(Request *req, Link *link, void *userdata) { int r; - assert(req); - assert(req->link); - assert(req->type == REQUEST_TYPE_RADV); - - link = req->link; + assert(link); r = radv_is_ready_to_configure(link); if (r <= 0) @@ -616,7 +617,7 @@ int link_request_radv(Link *link) { if (link->radv) return 0; - r = link_queue_request(link, REQUEST_TYPE_RADV, NULL, false, NULL, NULL, NULL); + r = link_queue_request(link, REQUEST_TYPE_RADV, radv_process_request, NULL); if (r < 0) return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Advertisement engine: %m"); diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index 392c00b37..ebebb3942 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -16,7 +16,6 @@ typedef struct Link Link; typedef struct Network Network; -typedef struct Request Request; typedef enum RADVPrefixDelegation { RADV_PREFIX_DELEGATION_NONE = 0, @@ -29,7 +28,7 @@ typedef enum RADVPrefixDelegation { typedef struct Prefix { Network *network; - NetworkConfigSection *section; + ConfigSection *section; struct in6_addr prefix; uint8_t prefixlen; @@ -46,7 +45,7 @@ typedef struct Prefix { typedef struct RoutePrefix { Network *network; - NetworkConfigSection *section; + ConfigSection *section; struct in6_addr prefix; uint8_t prefixlen; @@ -68,7 +67,6 @@ int radv_update_mac(Link *link); int radv_add_prefix(Link *link, const struct in6_addr *prefix, uint8_t prefix_len, usec_t lifetime_preferred_usec, usec_t lifetime_valid_usec); -int request_process_radv(Request *req); int link_request_radv(Link *link); const char* radv_prefix_delegation_to_string(RADVPrefixDelegation i) _const_; diff --git a/src/network/networkd-route-util.c b/src/network/networkd-route-util.c index c202078f0..4c9920b60 100644 --- a/src/network/networkd-route-util.c +++ b/src/network/networkd-route-util.c @@ -102,14 +102,21 @@ int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret) { return 0; } -static bool link_address_is_reachable(Link *link, int family, const union in_addr_union *address) { +bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw) { Route *route; Address *a; assert(link); assert(link->manager); - assert(IN_SET(family, AF_INET, AF_INET6)); - assert(address); + + if (onlink) + return true; + + if (!gw || !in_addr_is_set(family, gw)) + return true; + + if (family == AF_INET6 && in6_addr_is_link_local(&gw->in6)) + return true; SET_FOREACH(route, link->routes) { if (!route_exists(route)) @@ -118,7 +125,7 @@ static bool link_address_is_reachable(Link *link, int family, const union in_add continue; if (!in_addr_is_set(route->family, &route->dst) && route->dst_prefixlen == 0) continue; - if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, address) > 0) + if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, gw) > 0) return true; } @@ -136,27 +143,148 @@ static bool link_address_is_reachable(Link *link, int family, const union in_add continue; if (in_addr_is_set(a->family, &a->in_addr_peer)) continue; - if (in_addr_prefix_covers(family, &a->in_addr, a->prefixlen, address) > 0) + if (in_addr_prefix_covers(family, &a->in_addr, a->prefixlen, gw) > 0) return true; } return false; } -bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw) { +static int link_address_is_reachable_internal( + Link *link, + int family, + const union in_addr_union *address, + const union in_addr_union *prefsrc, /* optional */ + Route **ret) { + + Route *route, *found = NULL; + assert(link); - assert(gw); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(address); - if (onlink) - return true; + SET_FOREACH(route, link->routes) { + if (!route_exists(route)) + continue; - if (!in_addr_is_set(family, gw)) - return true; + if (route->type != RTN_UNICAST) + continue; - if (family == AF_INET6 && in6_addr_is_link_local(&gw->in6)) - return true; + if (route->family != family) + continue; - return link_address_is_reachable(link, family, gw); + if (in_addr_prefix_covers(family, &route->dst, route->dst_prefixlen, address) <= 0) + continue; + + if (prefsrc && + in_addr_is_set(family, prefsrc) && + in_addr_is_set(family, &route->prefsrc) && + !in_addr_equal(family, prefsrc, &route->prefsrc)) + continue; + + if (found && found->priority <= route->priority) + continue; + + found = route; + } + + if (!found) + return -ENOENT; + + if (ret) + *ret = found; + + return 0; +} + +int link_address_is_reachable( + Link *link, + int family, + const union in_addr_union *address, + const union in_addr_union *prefsrc, /* optional */ + Address **ret) { + + Route *route; + Address *a; + int r; + + assert(link); + assert(IN_SET(family, AF_INET, AF_INET6)); + assert(address); + + /* This checks if the address is reachable, and optionally return the Address object of the + * preferred source to access the address. */ + + r = link_address_is_reachable_internal(link, family, address, prefsrc, &route); + if (r < 0) + return r; + + if (!in_addr_is_set(route->family, &route->prefsrc)) { + if (ret) + *ret = NULL; + return 0; + } + + r = link_get_address(link, route->family, &route->prefsrc, 0, &a); + if (r < 0) + return r; + + if (!address_is_ready(a)) + return -EBUSY; + + if (ret) + *ret = a; + + return 0; +} + +int manager_address_is_reachable( + Manager *manager, + int family, + const union in_addr_union *address, + const union in_addr_union *prefsrc, /* optional */ + Address **ret) { + + Route *route, *found = NULL; + Address *a; + Link *link; + int r; + + assert(manager); + + HASHMAP_FOREACH(link, manager->links_by_index) { + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + continue; + + if (link_address_is_reachable_internal(link, family, address, prefsrc, &route) < 0) + continue; + + if (found && found->priority <= route->priority) + continue; + + found = route; + } + + if (!found) + return -ENOENT; + + if (!in_addr_is_set(found->family, &found->prefsrc)) { + if (ret) + *ret = NULL; + return 0; + } + + r = link_get_address(found->link, found->family, &found->prefsrc, 0, &a); + if (r < 0) + return r; + + if (!address_is_ready(a)) + return -EBUSY; + + if (ret) + *ret = a; + + return 0; } static const char * const route_type_table[__RTN_MAX] = { diff --git a/src/network/networkd-route-util.h b/src/network/networkd-route-util.h index 3dd3d3ace..b862cd677 100644 --- a/src/network/networkd-route-util.h +++ b/src/network/networkd-route-util.h @@ -8,6 +8,7 @@ typedef struct Link Link; typedef struct Manager Manager; +typedef struct Address Address; unsigned routes_max(void); @@ -15,6 +16,20 @@ int manager_find_uplink(Manager *m, int family, Link *exclude, Link **ret); bool gateway_is_ready(Link *link, bool onlink, int family, const union in_addr_union *gw); +int link_address_is_reachable( + Link *link, + int family, + const union in_addr_union *address, + const union in_addr_union *prefsrc, /* optional */ + Address **ret); + +int manager_address_is_reachable( + Manager *manager, + int family, + const union in_addr_union *address, + const union in_addr_union *prefsrc, /* optional */ + Address **ret); + int route_type_from_string(const char *s) _pure_; const char *route_type_to_string(int t) _const_; diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index ee7a53507..934fed3b7 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -47,7 +47,7 @@ int route_new(Route **ret) { } static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(route_freep) Route *route = NULL; int r; @@ -56,7 +56,7 @@ static int route_new_static(Network *network, const char *filename, unsigned sec assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -78,7 +78,7 @@ static int route_new_static(Network *network, const char *filename, unsigned sec route->section = TAKE_PTR(n); route->source = NETWORK_CONFIG_SOURCE_STATIC; - r = hashmap_ensure_put(&network->routes_by_section, &network_config_hash_ops, route->section, route); + r = hashmap_ensure_put(&network->routes_by_section, &config_section_hash_ops, route->section, route); if (r < 0) return r; @@ -95,7 +95,7 @@ Route *route_free(Route *route) { hashmap_remove(route->network->routes_by_section, route->section); } - network_config_section_free(route->section); + config_section_free(route->section); if (route->link) set_remove(route->link->routes, route); @@ -110,7 +110,7 @@ Route *route_free(Route *route) { return mfree(route); } -void route_hash_func(const Route *route, struct siphash *state) { +static void route_hash_func(const Route *route, struct siphash *state) { assert(route); siphash24_compress(&route->family, sizeof(route->family), state); @@ -152,7 +152,7 @@ void route_hash_func(const Route *route, struct siphash *state) { } } -int route_compare_func(const Route *a, const Route *b) { +static int route_compare_func(const Route *a, const Route *b) { int r; r = CMP(a->family, b->family); @@ -617,7 +617,7 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req if (route->gw_family == route->family) { r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->gw_family, &route->gw); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m"); + return r; } else { RouteVia rtvia = { .family = route->gw_family, @@ -626,57 +626,57 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req r = sd_netlink_message_append_data(req, RTA_VIA, &rtvia, sizeof(rtvia)); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_VIA attribute: %m"); + return r; } } if (route->dst_prefixlen > 0) { r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m"); + return r; r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); if (r < 0) - return log_link_error_errno(link, r, "Could not set destination prefix length: %m"); + return r; } if (route->src_prefixlen > 0) { r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m"); + return r; r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); if (r < 0) - return log_link_error_errno(link, r, "Could not set source prefix length: %m"); + return r; } if (in_addr_is_set(route->family, &route->prefsrc)) { r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m"); + return r; } r = sd_rtnl_message_route_set_scope(req, route->scope); if (r < 0) - return log_link_error_errno(link, r, "Could not set scope: %m"); + return r; r = sd_rtnl_message_route_set_flags(req, route->flags & RTNH_F_ONLINK); if (r < 0) - return log_link_error_errno(link, r, "Could not set flags: %m"); + return r; if (route->table < 256) { r = sd_rtnl_message_route_set_table(req, route->table); if (r < 0) - return log_link_error_errno(link, r, "Could not set route table: %m"); + return r; } else { r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC); if (r < 0) - return log_link_error_errno(link, r, "Could not set route table: %m"); + return r; /* Table attribute to allow more than 256. */ r = sd_netlink_message_append_u32(req, RTA_TABLE, route->table); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_TABLE attribute: %m"); + return r; } if (!route_type_is_reject(route) && @@ -686,22 +686,22 @@ static int route_set_netlink_message(const Route *route, sd_netlink_message *req r = sd_netlink_message_append_u32(req, RTA_OIF, link->ifindex); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m"); + return r; } if (route->nexthop_id > 0) { r = sd_netlink_message_append_u32(req, RTA_NH_ID, route->nexthop_id); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_NH_ID attribute: %m"); + return r; } r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m"); + return r; r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m"); + return r; return 0; } @@ -743,7 +743,7 @@ int route_remove(Route *route) { RTM_DELROUTE, route->family, route->protocol); if (r < 0) - return log_link_error_errno(link, r, "Could not create RTM_DELROUTE message: %m"); + return log_link_error_errno(link, r, "Could not create netlink message: %m"); if (route->family == AF_INET && route->nexthop_id > 0 && route->type == RTN_BLACKHOLE) /* When IPv4 route has nexthop id and the nexthop type is blackhole, even though kernel @@ -762,12 +762,12 @@ int route_remove(Route *route) { r = route_set_netlink_message(route, req, link); if (r < 0) - return r; + return log_error_errno(r, "Could not fill netlink message: %m"); r = netlink_call_async(manager->rtnl, NULL, req, route_remove_handler, link ? link_netlink_destroy_callback : NULL, link); if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); + return log_link_error_errno(link, r, "Could not send netlink message: %m"); link_ref(link); @@ -788,8 +788,8 @@ static void manager_mark_routes(Manager *manager, bool foreign, const Link *exce if (route->protocol == RTPROT_KERNEL) continue; - /* When 'foreign' is true, do not remove routes we configured. */ - if (foreign && route->source != NETWORK_CONFIG_SOURCE_FOREIGN) + /* When 'foreign' is true, mark only foreign routes, and vice versa. */ + if (foreign != (route->source == NETWORK_CONFIG_SOURCE_FOREIGN)) continue; /* Do not touch dynamic routes. They will removed by dhcp_pd_prefix_lost() */ @@ -834,7 +834,7 @@ static void manager_mark_routes(Manager *manager, bool foreign, const Link *exce } } -static int manager_drop_routes(Manager *manager) { +static int manager_drop_marked_routes(Manager *manager) { Route *route; int k, r = 0; @@ -871,18 +871,11 @@ static bool route_by_kernel(const Route *route) { static void link_unmark_wireguard_routes(Link *link) { Route *route, *existing; - NetDev *netdev; Wireguard *w; assert(link); - if (!streq_ptr(link->kind, "wireguard")) - return; - - if (netdev_get(link->manager, link->ifname, &netdev) < 0) - return; - - w = WIREGUARD(netdev); + w = WIREGUARD(link->netdev); if (!w) return; @@ -955,14 +948,14 @@ int link_drop_foreign_routes(Link *link) { manager_mark_routes(link->manager, /* foreign = */ true, NULL); - k = manager_drop_routes(link->manager); + k = manager_drop_marked_routes(link->manager); if (k < 0 && r >= 0) r = k; return r; } -int link_drop_routes(Link *link) { +int link_drop_managed_routes(Link *link) { Route *route; int k, r = 0; @@ -973,6 +966,10 @@ int link_drop_routes(Link *link) { if (route_by_kernel(route)) continue; + /* Do not touch routes managed by kernel or other tools. */ + if (route->source == NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + if (!route_exists(route)) continue; @@ -983,7 +980,7 @@ int link_drop_routes(Link *link) { manager_mark_routes(link->manager, /* foreign = */ false, link); - k = manager_drop_routes(link->manager); + k = manager_drop_marked_routes(link->manager); if (k < 0 && r >= 0) r = k; @@ -1044,7 +1041,7 @@ static int route_setup_timer(Route *route, const struct rta_cacheinfo *cacheinfo /* Assume that non-zero rta_expires means kernel will handle the route expiration. */ return 0; - r = event_reset_time(manager->event, &route->expire, clock_boottime_or_monotonic(), + r = event_reset_time(manager->event, &route->expire, CLOCK_BOOTTIME, route->lifetime_usec, 0, route_expire_handler, route, 0, "route-expiration", true); if (r < 0) return r; @@ -1144,9 +1141,6 @@ int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Li assert(link); assert(error_msg); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 0; - r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { log_link_message_warning_errno(link, m, r, "Could not set route"); @@ -1157,12 +1151,8 @@ int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Li return 1; } -static int route_configure( - const Route *route, - Link *link, - link_netlink_message_handler_t callback) { - - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int route_configure(const Route *route, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert(route); @@ -1171,139 +1161,213 @@ static int route_configure( assert(link->manager); assert(link->manager->rtnl); assert(link->ifindex > 0); - assert(callback); + assert(req); log_route_debug(route, "Configuring", link, link->manager); - r = sd_rtnl_message_new_route(link->manager->rtnl, &req, - RTM_NEWROUTE, route->family, - route->protocol); + r = sd_rtnl_message_new_route(link->manager->rtnl, &m, RTM_NEWROUTE, route->family, route->protocol); if (r < 0) - return log_link_error_errno(link, r, "Could not create RTM_NEWROUTE message: %m"); + return r; - r = sd_rtnl_message_route_set_type(req, route->type); + r = sd_rtnl_message_route_set_type(m, route->type); if (r < 0) - return log_link_error_errno(link, r, "Could not set route type: %m"); + return r; - r = route_set_netlink_message(route, req, link); + r = route_set_netlink_message(route, m, link); if (r < 0) return r; if (route->lifetime_usec != USEC_INFINITY) { - r = sd_netlink_message_append_u32(req, RTA_EXPIRES, - MIN(DIV_ROUND_UP(usec_sub_unsigned(route->lifetime_usec, now(clock_boottime_or_monotonic())), USEC_PER_SEC), UINT32_MAX)); + r = sd_netlink_message_append_u32(m, RTA_EXPIRES, + MIN(DIV_ROUND_UP(usec_sub_unsigned(route->lifetime_usec, now(CLOCK_BOOTTIME)), USEC_PER_SEC), UINT32_MAX)); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_EXPIRES attribute: %m"); + return r; } if (route->ttl_propagate >= 0) { - r = sd_netlink_message_append_u8(req, RTA_TTL_PROPAGATE, route->ttl_propagate); + r = sd_netlink_message_append_u8(m, RTA_TTL_PROPAGATE, route->ttl_propagate); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_TTL_PROPAGATE attribute: %m"); + return r; } - r = sd_netlink_message_open_container(req, RTA_METRICS); + r = sd_netlink_message_open_container(m, RTA_METRICS); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m"); + return r; if (route->mtu > 0) { - r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu); + r = sd_netlink_message_append_u32(m, RTAX_MTU, route->mtu); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTAX_MTU attribute: %m"); + return r; } if (route->initcwnd > 0) { - r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd); + r = sd_netlink_message_append_u32(m, RTAX_INITCWND, route->initcwnd); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTAX_INITCWND attribute: %m"); + return r; } if (route->initrwnd > 0) { - r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd); + r = sd_netlink_message_append_u32(m, RTAX_INITRWND, route->initrwnd); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTAX_INITRWND attribute: %m"); + return r; } if (route->quickack >= 0) { - r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack); + r = sd_netlink_message_append_u32(m, RTAX_QUICKACK, route->quickack); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTAX_QUICKACK attribute: %m"); + return r; } if (route->fast_open_no_cookie >= 0) { - r = sd_netlink_message_append_u32(req, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie); + r = sd_netlink_message_append_u32(m, RTAX_FASTOPEN_NO_COOKIE, route->fast_open_no_cookie); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTAX_FASTOPEN_NO_COOKIE attribute: %m"); + return r; } if (route->advmss > 0) { - r = sd_netlink_message_append_u32(req, RTAX_ADVMSS, route->advmss); + r = sd_netlink_message_append_u32(m, RTAX_ADVMSS, route->advmss); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTAX_ADVMSS attribute: %m"); + return r; } - r = sd_netlink_message_close_container(req); + r = sd_netlink_message_close_container(m); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m"); + return r; if (!ordered_set_isempty(route->multipath_routes)) { assert(route->nexthop_id == 0); assert(!in_addr_is_set(route->gw_family, &route->gw)); - r = append_nexthops(link, route, req); + r = append_nexthops(link, route, m); if (r < 0) - return log_link_error_errno(link, r, "Could not append RTA_MULTIPATH attribute: %m"); + return r; } - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - return 0; + return request_call_netlink_async(link->manager->rtnl, m, req); } -void route_cancel_request(Route *route, Link *link) { - Request req; - - assert(route); - - link = route->link ?: link; - - assert(link); - - if (!route_is_requesting(route)) - return; - - req = (Request) { - .link = link, - .type = REQUEST_TYPE_ROUTE, - .route = route, - }; - - request_drop(ordered_set_get(link->manager->request_queue, &req)); - route_cancel_requesting(route); -} - -static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int route_is_ready_to_configure(const Route *route, Link *link) { int r; + assert(route); assert(link); - assert(link->static_route_messages > 0); - link->static_route_messages--; + if (!link_is_ready_to_configure(link, false)) + return false; - r = route_configure_handler_internal(rtnl, m, link, "Could not set route"); - if (r <= 0) - return r; + if (set_size(link->routes) >= routes_max()) + return false; - if (link->static_route_messages == 0) { - log_link_debug(link, "Routes set"); - link->static_routes_configured = true; - link_check_ready(link); + if (route->nexthop_id > 0) { + struct nexthop_grp *nhg; + NextHop *nh; + + if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0) + return false; + + if (!nexthop_exists(nh)) + return false; + + HASHMAP_FOREACH(nhg, nh->group) { + NextHop *g; + + if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0) + return false; + + if (!nexthop_exists(g)) + return false; + } } + if (in_addr_is_set(route->family, &route->prefsrc) > 0) { + r = manager_has_address(link->manager, route->family, &route->prefsrc, route->family == AF_INET6); + if (r <= 0) + return r; + } + + if (!gateway_is_ready(link, FLAGS_SET(route->flags, RTNH_F_ONLINK), route->gw_family, &route->gw)) + return false; + + MultipathRoute *m; + ORDERED_SET_FOREACH(m, route->multipath_routes) { + union in_addr_union a = m->gateway.address; + Link *l = NULL; + + r = multipath_route_get_link(link->manager, m, &l); + if (r < 0) + return false; + if (r > 0) { + if (!link_is_ready_to_configure(l, true)) + return false; + + m->ifindex = l->ifindex; + } + + if (!gateway_is_ready(l ?: link, FLAGS_SET(route->flags, RTNH_F_ONLINK), m->gateway.family, &a)) + return false; + } + + return true; +} + +static int route_process_request(Request *req, Link *link, Route *route) { + _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL; + int r; + + assert(req); + assert(link); + assert(route); + + r = route_is_ready_to_configure(route, link); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to check if route is ready to configure: %m"); + if (r == 0) + return 0; + + if (route_needs_convert(route)) { + r = route_convert(link->manager, route, &converted); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to convert route: %m"); + + assert(r > 0); + assert(converted); + + for (size_t i = 0; i < converted->n; i++) { + Route *existing; + + if (route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) < 0) { + _cleanup_(route_freep) Route *tmp = NULL; + + r = route_dup(converted->routes[i], &tmp); + if (r < 0) + return log_oom(); + + r = route_add(link->manager, converted->links[i] ?: link, tmp); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to add route: %m"); + + TAKE_PTR(tmp); + } else { + existing->source = converted->routes[i]->source; + existing->provider = converted->routes[i]->provider; + } + } + } + + r = route_configure(route, link, req); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure route: %m"); + + if (converted) + for (size_t i = 0; i < converted->n; i++) { + Route *existing; + + assert_se(route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) >= 0); + route_enter_configuring(existing); + } + else + route_enter_configuring(route); + return 1; } @@ -1312,7 +1376,7 @@ int link_request_route( Route *route, bool consume_object, unsigned *message_counter, - link_netlink_message_handler_t netlink_handler, + route_netlink_handler_t netlink_handler, Request **ret) { Route *existing; @@ -1359,8 +1423,12 @@ int link_request_route( } log_route_debug(existing, "Requesting", link, link->manager); - r = link_queue_request(link, REQUEST_TYPE_ROUTE, existing, false, - message_counter, netlink_handler, ret); + r = link_queue_request_safe(link, REQUEST_TYPE_ROUTE, + existing, NULL, + route_hash_func, + route_compare_func, + route_process_request, + message_counter, netlink_handler, ret); if (r <= 0) return r; @@ -1368,6 +1436,24 @@ int link_request_route( return 1; } +static int static_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) { + int r; + + assert(link); + + r = route_configure_handler_internal(rtnl, m, link, "Could not set route"); + if (r <= 0) + return r; + + if (link->static_route_messages == 0) { + log_link_debug(link, "Routes set"); + link->static_routes_configured = true; + link_check_ready(link); + } + + return 1; +} + static int link_request_static_route(Link *link, Route *route) { assert(link); assert(link->manager); @@ -1378,8 +1464,10 @@ static int link_request_static_route(Link *link, Route *route) { static_route_handler, NULL); log_route_debug(route, "Requesting", link, link->manager); - return link_queue_request(link, REQUEST_TYPE_ROUTE, route, false, - &link->static_route_messages, static_route_handler, NULL); + return link_queue_request_safe(link, REQUEST_TYPE_ROUTE, + route, NULL, route_hash_func, route_compare_func, + route_process_request, + &link->static_route_messages, static_route_handler, NULL); } static int link_request_wireguard_routes(Link *link, bool only_ipv4) { @@ -1448,135 +1536,28 @@ int link_request_static_routes(Link *link, bool only_ipv4) { return 0; } -static int route_is_ready_to_configure(const Route *route, Link *link) { - int r; +void route_cancel_request(Route *route, Link *link) { + Request req; assert(route); + + link = route->link ?: link; + assert(link); - if (!link_is_ready_to_configure(link, false)) - return false; + if (!route_is_requesting(route)) + return; - if (set_size(link->routes) >= routes_max()) - return false; + req = (Request) { + .link = link, + .type = REQUEST_TYPE_ROUTE, + .userdata = route, + .hash_func = (hash_func_t) route_hash_func, + .compare_func = (compare_func_t) route_compare_func, + }; - if (route->nexthop_id > 0) { - struct nexthop_grp *nhg; - NextHop *nh; - - if (manager_get_nexthop_by_id(link->manager, route->nexthop_id, &nh) < 0) - return false; - - if (!nexthop_exists(nh)) - return false; - - HASHMAP_FOREACH(nhg, nh->group) { - NextHop *g; - - if (manager_get_nexthop_by_id(link->manager, nhg->id, &g) < 0) - return false; - - if (!nexthop_exists(g)) - return false; - } - } - - if (in_addr_is_set(route->family, &route->prefsrc) > 0) { - r = manager_has_address(link->manager, route->family, &route->prefsrc, route->family == AF_INET6); - if (r <= 0) - return r; - } - - if (!gateway_is_ready(link, FLAGS_SET(route->flags, RTNH_F_ONLINK), route->gw_family, &route->gw)) - return false; - - MultipathRoute *m; - ORDERED_SET_FOREACH(m, route->multipath_routes) { - union in_addr_union a = m->gateway.address; - Link *l = NULL; - - r = multipath_route_get_link(link->manager, m, &l); - if (r < 0) - return false; - if (r > 0) { - if (!link_is_ready_to_configure(l, true)) - return false; - - m->ifindex = l->ifindex; - } - - if (!gateway_is_ready(l ?: link, FLAGS_SET(route->flags, RTNH_F_ONLINK), m->gateway.family, &a)) - return false; - } - - return true; -} - -int request_process_route(Request *req) { - _cleanup_(converted_routes_freep) ConvertedRoutes *converted = NULL; - Route *route; - Link *link; - int r; - - assert(req); - assert(req->link); - assert(req->route); - assert(req->type == REQUEST_TYPE_ROUTE); - - link = req->link; - route = req->route; - - r = route_is_ready_to_configure(route, link); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to check if route is ready to configure: %m"); - if (r == 0) - return 0; - - if (route_needs_convert(route)) { - r = route_convert(link->manager, route, &converted); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to convert route: %m"); - - assert(r > 0); - assert(converted); - - for (size_t i = 0; i < converted->n; i++) { - Route *existing; - - if (route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) < 0) { - _cleanup_(route_freep) Route *tmp = NULL; - - r = route_dup(converted->routes[i], &tmp); - if (r < 0) - return log_oom(); - - r = route_add(link->manager, converted->links[i] ?: link, tmp); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to add route: %m"); - - TAKE_PTR(tmp); - } else { - existing->source = converted->routes[i]->source; - existing->provider = converted->routes[i]->provider; - } - } - } - - r = route_configure(route, link, req->netlink_handler); - if (r < 0) - return log_link_warning_errno(link, r, "Failed to configure route: %m"); - - if (converted) - for (size_t i = 0; i < converted->n; i++) { - Route *existing; - - assert_se(route_get(link->manager, converted->links[i] ?: link, converted->routes[i], &existing) >= 0); - route_enter_configuring(existing); - } - else - route_enter_configuring(route); - - return 1; + request_detach(link->manager, &req); + route_cancel_requesting(route); } static int process_route_one( @@ -2846,6 +2827,12 @@ static int route_section_verify(Route *route, Network *network) { route->scope = RT_SCOPE_HOST; else if (IN_SET(route->type, RTN_BROADCAST, RTN_ANYCAST, RTN_MULTICAST)) route->scope = RT_SCOPE_LINK; + else if (IN_SET(route->type, RTN_UNICAST, RTN_UNSPEC) && + !route->gateway_from_dhcp_or_ra && + !in_addr_is_set(route->gw_family, &route->gw) && + ordered_set_isempty(route->multipath_routes) && + route->nexthop_id == 0) + route->scope = RT_SCOPE_LINK; } if (route->scope != RT_SCOPE_UNIVERSE && route->family == AF_INET6) { diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index e3e22a598..b431e1a30 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -14,12 +14,19 @@ typedef struct Manager Manager; typedef struct Network Network; typedef struct Request Request; +typedef struct Route Route; +typedef int (*route_netlink_handler_t)( + sd_netlink *rtnl, + sd_netlink_message *m, + Request *req, + Link *link, + Route *route); -typedef struct Route { +struct Route { Link *link; Manager *manager; Network *network; - NetworkConfigSection *section; + ConfigSection *section; NetworkConfigSource source; NetworkConfigState state; union in_addr_union provider; /* DHCP server or router address */ @@ -66,15 +73,13 @@ typedef struct Route { usec_t lifetime_usec; /* Used when kernel does not support RTA_EXPIRES attribute. */ sd_event_source *expire; -} Route; +}; -void route_hash_func(const Route *route, struct siphash *state); -int route_compare_func(const Route *a, const Route *b); extern const struct hash_ops route_hash_ops; int route_new(Route **ret); Route *route_free(Route *route); -DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(Route, route_free); int route_dup(const Route *src, Route **ret); int route_configure_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, const char *error_msg); @@ -82,7 +87,7 @@ int route_remove(Route *route); int route_get(Manager *manager, Link *link, const Route *in, Route **ret); -int link_drop_routes(Link *link); +int link_drop_managed_routes(Link *link); int link_drop_foreign_routes(Link *link); void link_foreignize_routes(Link *link); @@ -92,10 +97,9 @@ int link_request_route( Route *route, bool consume_object, unsigned *message_counter, - link_netlink_message_handler_t netlink_handler, + route_netlink_handler_t netlink_handler, Request **ret); int link_request_static_routes(Link *link, bool only_ipv4); -int request_process_route(Request *req); int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index 90086f35a..8f4297a86 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -54,14 +54,14 @@ RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule) { if (rule->manager) set_remove(rule->manager->rules, rule); - network_config_section_free(rule->section); + config_section_free(rule->section); free(rule->iif); free(rule->oif); return mfree(rule); } -DEFINE_NETWORK_SECTION_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free); +DEFINE_SECTION_CLEANUP_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free); static int routing_policy_rule_new(RoutingPolicyRule **ret) { RoutingPolicyRule *rule; @@ -86,7 +86,7 @@ static int routing_policy_rule_new(RoutingPolicyRule **ret) { static int routing_policy_rule_new_static(Network *network, const char *filename, unsigned section_line, RoutingPolicyRule **ret) { _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *rule = NULL; - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; int r; assert(network); @@ -94,7 +94,7 @@ static int routing_policy_rule_new_static(Network *network, const char *filename assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; @@ -113,7 +113,7 @@ static int routing_policy_rule_new_static(Network *network, const char *filename rule->source = NETWORK_CONFIG_SOURCE_STATIC; rule->protocol = RTPROT_STATIC; - r = hashmap_ensure_put(&network->rules_by_section, &network_config_hash_ops, rule->section, rule); + r = hashmap_ensure_put(&network->rules_by_section, &config_section_hash_ops, rule->section, rule); if (r < 0) return r; @@ -153,7 +153,7 @@ static int routing_policy_rule_dup(const RoutingPolicyRule *src, RoutingPolicyRu return 0; } -void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct siphash *state) { +static void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct siphash *state) { assert(rule); siphash24_compress(&rule->family, sizeof(rule->family), state); @@ -194,7 +194,7 @@ void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct siphash } } -int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const RoutingPolicyRule *b) { +static int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const RoutingPolicyRule *b) { int r; r = CMP(a->family, b->family); @@ -449,116 +449,116 @@ static int routing_policy_rule_set_netlink_message(const RoutingPolicyRule *rule if (rule->from_prefixlen > 0) { r = netlink_message_append_in_addr_union(m, FRA_SRC, rule->family, &rule->from); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_SRC attribute: %m"); + return r; r = sd_rtnl_message_routing_policy_rule_set_fib_src_prefixlen(m, rule->from_prefixlen); if (r < 0) - return log_link_error_errno(link, r, "Could not set source prefix length: %m"); + return r; } if (rule->to_prefixlen > 0) { r = netlink_message_append_in_addr_union(m, FRA_DST, rule->family, &rule->to); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_DST attribute: %m"); + return r; r = sd_rtnl_message_routing_policy_rule_set_fib_dst_prefixlen(m, rule->to_prefixlen); if (r < 0) - return log_link_error_errno(link, r, "Could not set destination prefix length: %m"); + return r; } r = sd_netlink_message_append_u32(m, FRA_PRIORITY, rule->priority); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_PRIORITY attribute: %m"); + return r; if (rule->tos > 0) { r = sd_rtnl_message_routing_policy_rule_set_tos(m, rule->tos); if (r < 0) - return log_link_error_errno(link, r, "Could not set IP rule TOS: %m"); + return r; } if (rule->table < 256) { r = sd_rtnl_message_routing_policy_rule_set_table(m, rule->table); if (r < 0) - return log_link_error_errno(link, r, "Could not set IP rule table: %m"); + return r; } else { r = sd_rtnl_message_routing_policy_rule_set_table(m, RT_TABLE_UNSPEC); if (r < 0) - return log_link_error_errno(link, r, "Could not set IP rule table: %m"); + return r; r = sd_netlink_message_append_u32(m, FRA_TABLE, rule->table); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_TABLE attribute: %m"); + return r; } if (rule->fwmark > 0) { r = sd_netlink_message_append_u32(m, FRA_FWMARK, rule->fwmark); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_FWMARK attribute: %m"); + return r; r = sd_netlink_message_append_u32(m, FRA_FWMASK, rule->fwmask); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_FWMASK attribute: %m"); + return r; } if (rule->iif) { r = sd_netlink_message_append_string(m, FRA_IIFNAME, rule->iif); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_IIFNAME attribute: %m"); + return r; } if (rule->oif) { r = sd_netlink_message_append_string(m, FRA_OIFNAME, rule->oif); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_OIFNAME attribute: %m"); + return r; } r = sd_netlink_message_append_u8(m, FRA_IP_PROTO, rule->ipproto); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_IP_PROTO attribute: %m"); + return r; r = sd_netlink_message_append_u8(m, FRA_PROTOCOL, rule->protocol); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_PROTOCOL attribute: %m"); + return r; if (rule->sport.start != 0 || rule->sport.end != 0) { r = sd_netlink_message_append_data(m, FRA_SPORT_RANGE, &rule->sport, sizeof(rule->sport)); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_SPORT_RANGE attribute: %m"); + return r; } if (rule->dport.start != 0 || rule->dport.end != 0) { r = sd_netlink_message_append_data(m, FRA_DPORT_RANGE, &rule->dport, sizeof(rule->dport)); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_DPORT_RANGE attribute: %m"); + return r; } if (rule->uid_range.start != UID_INVALID && rule->uid_range.end != UID_INVALID) { r = sd_netlink_message_append_data(m, FRA_UID_RANGE, &rule->uid_range, sizeof(rule->uid_range)); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_UID_RANGE attribute: %m"); + return r; } if (rule->invert_rule) { r = sd_rtnl_message_routing_policy_rule_set_flags(m, FIB_RULE_INVERT); if (r < 0) - return log_link_error_errno(link, r, "Could not append FIB_RULE_INVERT attribute: %m"); + return r; } if (rule->suppress_prefixlen >= 0) { r = sd_netlink_message_append_u32(m, FRA_SUPPRESS_PREFIXLEN, (uint32_t) rule->suppress_prefixlen); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_SUPPRESS_PREFIXLEN attribute: %m"); + return r; } if (rule->suppress_ifgroup >= 0) { r = sd_netlink_message_append_u32(m, FRA_SUPPRESS_IFGROUP, (uint32_t) rule->suppress_ifgroup); if (r < 0) - return log_link_error_errno(link, r, "Could not append FRA_SUPPRESS_IFGROUP attribute: %m"); + return r; } r = sd_rtnl_message_routing_policy_rule_set_fib_type(m, rule->type); if (r < 0) - return log_link_error_errno(link, r, "Could not append FIB rule type attribute: %m"); + return r; return 0; } @@ -588,27 +588,23 @@ static int routing_policy_rule_remove(RoutingPolicyRule *rule) { r = sd_rtnl_message_new_routing_policy_rule(rule->manager->rtnl, &m, RTM_DELRULE, rule->family); if (r < 0) - return log_error_errno(r, "Could not allocate RTM_DELRULE message: %m"); + return log_warning_errno(r, "Could not allocate netlink message: %m"); r = routing_policy_rule_set_netlink_message(rule, m, NULL); if (r < 0) - return r; + return log_warning_errno(r, "Could not create netlink message: %m"); r = netlink_call_async(rule->manager->rtnl, NULL, m, routing_policy_rule_remove_handler, NULL, NULL); if (r < 0) - return log_error_errno(r, "Could not send rtnetlink message: %m"); + return log_warning_errno(r, "Could not send netlink message: %m"); routing_policy_rule_enter_removing(rule); return 0; } -static int routing_policy_rule_configure( - RoutingPolicyRule *rule, - Link *link, - link_netlink_message_handler_t callback) { - +static int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, Request *req) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; @@ -618,27 +614,19 @@ static int routing_policy_rule_configure( assert(link->ifindex > 0); assert(link->manager); assert(link->manager->rtnl); - assert(callback); + assert(req); log_routing_policy_rule_debug(rule, "Configuring", link, link->manager); r = sd_rtnl_message_new_routing_policy_rule(link->manager->rtnl, &m, RTM_NEWRULE, rule->family); if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_NEWRULE message: %m"); + return r; r = routing_policy_rule_set_netlink_message(rule, m, link); if (r < 0) return r; - r = netlink_call_async(link->manager->rtnl, NULL, m, callback, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - routing_policy_rule_enter_configuring(rule); - return r; + return request_call_netlink_async(link->manager->rtnl, m, req); } static void manager_mark_routing_policy_rules(Manager *m, bool foreign, const Link *except) { @@ -653,8 +641,8 @@ static void manager_mark_routing_policy_rules(Manager *m, bool foreign, const Li if (rule->protocol == RTPROT_KERNEL) continue; - /* When 'foreign' is true, do not remove rules we configured. */ - if (foreign && rule->source != NETWORK_CONFIG_SOURCE_FOREIGN) + /* When 'foreign' is true, mark only foreign rules, and vice versa. */ + if (foreign != (rule->source == NETWORK_CONFIG_SOURCE_FOREIGN)) continue; /* Ignore rules not assigned yet or already removing. */ @@ -730,19 +718,35 @@ void link_foreignize_routing_policy_rules(Link *link) { } } -static int static_routing_policy_rule_configure_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int routing_policy_rule_process_request(Request *req, Link *link, RoutingPolicyRule *rule) { + int r; + + assert(req); + assert(link); + assert(rule); + + if (!link_is_ready_to_configure(link, false)) + return 0; + + r = routing_policy_rule_configure(rule, link, req); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure routing policy rule: %m"); + + routing_policy_rule_enter_configuring(rule); + return 1; +} + +static int static_routing_policy_rule_configure_handler( + sd_netlink *rtnl, + sd_netlink_message *m, + Request *req, + Link *link, + RoutingPolicyRule *rule) { + int r; - assert(rtnl); assert(m); assert(link); - assert(link->ifname); - assert(link->static_routing_policy_rule_messages > 0); - - link->static_routing_policy_rule_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) { @@ -760,13 +764,7 @@ static int static_routing_policy_rule_configure_handler(sd_netlink *rtnl, sd_net return 1; } -static int link_request_routing_policy_rule( - Link *link, - RoutingPolicyRule *rule, - unsigned *message_counter, - link_netlink_message_handler_t netlink_handler, - Request **ret) { - +static int link_request_routing_policy_rule(Link *link, RoutingPolicyRule *rule) { RoutingPolicyRule *existing; int r; @@ -795,8 +793,14 @@ static int link_request_routing_policy_rule( existing->source = rule->source; log_routing_policy_rule_debug(existing, "Requesting", link, link->manager); - r = link_queue_request(link, REQUEST_TYPE_ROUTING_POLICY_RULE, existing, false, - message_counter, netlink_handler, ret); + r = link_queue_request_safe(link, REQUEST_TYPE_ROUTING_POLICY_RULE, + existing, NULL, + routing_policy_rule_hash_func, + routing_policy_rule_compare_func, + routing_policy_rule_process_request, + &link->static_routing_policy_rule_messages, + static_routing_policy_rule_configure_handler, + NULL); if (r <= 0) return r; @@ -808,26 +812,17 @@ static int link_request_static_routing_policy_rule(Link *link, RoutingPolicyRule int r; if (IN_SET(rule->family, AF_INET, AF_INET6)) - return link_request_routing_policy_rule(link, rule, - &link->static_routing_policy_rule_messages, - static_routing_policy_rule_configure_handler, - NULL); + return link_request_routing_policy_rule(link, rule); rule->family = AF_INET; - r = link_request_routing_policy_rule(link, rule, - &link->static_routing_policy_rule_messages, - static_routing_policy_rule_configure_handler, - NULL); + r = link_request_routing_policy_rule(link, rule); if (r < 0) { rule->family = AF_UNSPEC; return r; } rule->family = AF_INET6; - r = link_request_routing_policy_rule(link, rule, - &link->static_routing_policy_rule_messages, - static_routing_policy_rule_configure_handler, - NULL); + r = link_request_routing_policy_rule(link, rule); rule->family = AF_UNSPEC; return r; } @@ -858,24 +853,6 @@ int link_request_static_routing_policy_rules(Link *link) { return 0; } -int request_process_routing_policy_rule(Request *req) { - int r; - - assert(req); - assert(req->link); - assert(req->rule); - assert(req->type == REQUEST_TYPE_ROUTING_POLICY_RULE); - - if (!link_is_ready_to_configure(req->link, false)) - return 0; - - r = routing_policy_rule_configure(req->rule, req->link, req->netlink_handler); - if (r < 0) - return r; - - return 1; -} - static const RoutingPolicyRule kernel_rules[] = { { .family = AF_INET, .priority_set = true, .priority = 0, .table = RT_TABLE_LOCAL, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, }, { .family = AF_INET, .priority_set = true, .priority = 32766, .table = RT_TABLE_MAIN, .type = FR_ACT_TO_TBL, .uid_range.start = UID_INVALID, .uid_range.end = UID_INVALID, .suppress_prefixlen = -1, .suppress_ifgroup = -1, }, diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index f52943bd2..b6ce2fa19 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -12,12 +12,11 @@ typedef struct Link Link; typedef struct Manager Manager; typedef struct Network Network; -typedef struct Request Request; typedef struct RoutingPolicyRule { Manager *manager; Network *network; - NetworkConfigSection *section; + ConfigSection *section; NetworkConfigSource source; NetworkConfigState state; @@ -58,20 +57,16 @@ const char *fr_act_type_full_to_string(int t) _const_; RoutingPolicyRule *routing_policy_rule_free(RoutingPolicyRule *rule); -void routing_policy_rule_hash_func(const RoutingPolicyRule *rule, struct siphash *state); -int routing_policy_rule_compare_func(const RoutingPolicyRule *a, const RoutingPolicyRule *b); - void network_drop_invalid_routing_policy_rules(Network *network); int link_request_static_routing_policy_rules(Link *link); -int request_process_routing_policy_rule(Request *req); int manager_rtnl_process_rule(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); int manager_drop_routing_policy_rules_internal(Manager *m, bool foreign, const Link *except); static inline int manager_drop_foreign_routing_policy_rules(Manager *m) { return manager_drop_routing_policy_rules_internal(m, true, NULL); } -static inline int link_drop_routing_policy_rules(Link *link) { +static inline int link_drop_managed_routing_policy_rules(Link *link) { assert(link); return manager_drop_routing_policy_rules_internal(link->manager, false, link); } diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index e00cc1e58..148277685 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -14,23 +14,6 @@ #include "networkd-manager.h" #include "networkd-queue.h" #include "networkd-setlink.h" -#include "string-table.h" - -static const char *const set_link_operation_table[_SET_LINK_OPERATION_MAX] = { - [SET_LINK_ADDRESS_GENERATION_MODE] = "IPv6LL address generation mode", - [SET_LINK_BOND] = "bond configurations", - [SET_LINK_BRIDGE] = "bridge configurations", - [SET_LINK_BRIDGE_VLAN] = "bridge VLAN configurations", - [SET_LINK_CAN] = "CAN interface configurations", - [SET_LINK_FLAGS] = "link flags", - [SET_LINK_GROUP] = "interface group", - [SET_LINK_IPOIB] = "IPoIB configurations", - [SET_LINK_MAC] = "MAC address", - [SET_LINK_MASTER] = "master interface", - [SET_LINK_MTU] = "MTU", -}; - -DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(set_link_operation, SetLinkOperation); static int get_link_default_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { return link_getlink_handler_internal(rtnl, m, link, "Failed to sync link information"); @@ -54,42 +37,36 @@ static int get_link_update_flag_handler(sd_netlink *rtnl, sd_netlink_message *m, static int set_link_handler_internal( sd_netlink *rtnl, sd_netlink_message *m, + Request *req, Link *link, - SetLinkOperation op, bool ignore, link_netlink_message_handler_t get_link_handler) { int r; assert(m); + assert(req); assert(link); - assert(link->set_link_messages > 0); - assert(op >= 0 && op < _SET_LINK_OPERATION_MAX); - - link->set_link_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - goto on_error; r = sd_netlink_message_get_errno(m); if (r < 0) { const char *error_msg; - error_msg = strjoina("Failed to set ", set_link_operation_to_string(op), ignore ? ", ignoring" : ""); + error_msg = strjoina("Failed to set ", request_type_to_string(req->type), ignore ? ", ignoring" : ""); log_link_message_warning_errno(link, m, r, error_msg); if (!ignore) link_enter_failed(link); - goto on_error; + return 0; } - log_link_debug(link, "%s set.", set_link_operation_to_string(op)); + log_link_debug(link, "%s set.", request_type_to_string(req->type)); if (get_link_handler) { r = link_call_getlink(link, get_link_handler); if (r < 0) { link_enter_failed(link); - goto on_error; + return 0; } } @@ -97,27 +74,12 @@ static int set_link_handler_internal( link_check_ready(link); return 1; - -on_error: - switch (op) { - case SET_LINK_FLAGS: - assert(link->set_flags_messages > 0); - link->set_flags_messages--; - break; - case SET_LINK_MASTER: - link->master_set = true; - break; - default: - break; - } - - return 0; } -static int link_set_addrgen_mode_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int link_set_addrgen_mode_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { int r; - r = set_link_handler_internal(rtnl, m, link, SET_LINK_ADDRESS_GENERATION_MODE, /* ignore = */ true, NULL); + r = set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, NULL); if (r <= 0) return r; @@ -130,49 +92,43 @@ static int link_set_addrgen_mode_handler(sd_netlink *rtnl, sd_netlink_message *m return 0; } -static int link_set_bond_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return set_link_handler_internal(rtnl, m, link, SET_LINK_BOND, /* ignore = */ false, NULL); +static int link_set_bond_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); } -static int link_set_bridge_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return set_link_handler_internal(rtnl, m, link, SET_LINK_BRIDGE, /* ignore = */ true, NULL); +static int link_set_bridge_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, NULL); } -static int link_set_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return set_link_handler_internal(rtnl, m, link, SET_LINK_BRIDGE_VLAN, /* ignore = */ false, NULL); +static int link_set_bridge_vlan_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); } -static int link_set_can_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return set_link_handler_internal(rtnl, m, link, SET_LINK_CAN, /* ignore = */ false, NULL); +static int link_set_can_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); } -static int link_set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return set_link_handler_internal(rtnl, m, link, SET_LINK_FLAGS, /* ignore = */ false, get_link_update_flag_handler); +static int link_set_flags_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, get_link_default_handler); } -static int link_set_group_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return set_link_handler_internal(rtnl, m, link, SET_LINK_GROUP, /* ignore = */ false, NULL); +static int link_set_group_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, NULL); } -static int link_set_ipoib_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return set_link_handler_internal(rtnl, m, link, SET_LINK_IPOIB, /* ignore = */ true, NULL); +static int link_set_ipoib_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, NULL); } -static int link_set_mac_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return set_link_handler_internal(rtnl, m, link, SET_LINK_MAC, /* ignore = */ true, get_link_default_handler); +static int link_set_mac_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_default_handler); } -static int link_set_mac_allow_retry_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int link_set_mac_allow_retry_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { int r; assert(m); assert(link); - assert(link->set_link_messages > 0); - - link->set_link_messages--; - - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 0; r = sd_netlink_message_get_errno(m); if (r == -EBUSY) { @@ -189,24 +145,22 @@ static int link_set_mac_allow_retry_handler(sd_netlink *rtnl, sd_netlink_message return 0; } - /* set_link_mac_handler() also decrements set_link_messages, so increment the value once. */ - link->set_link_messages++; - return link_set_mac_handler(rtnl, m, link); + return link_set_mac_handler(rtnl, m, req, link, userdata); } -static int link_set_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return set_link_handler_internal(rtnl, m, link, SET_LINK_MASTER, /* ignore = */ false, get_link_master_handler); +static int link_set_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ false, get_link_master_handler); } -static int link_unset_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int link_unset_master_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { /* Some devices do not support setting master ifindex. Let's ignore error on unsetting master ifindex. */ - return set_link_handler_internal(rtnl, m, link, SET_LINK_MASTER, /* ignore = */ true, get_link_master_handler); + return set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_master_handler); } -static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { int r; - r = set_link_handler_internal(rtnl, m, link, SET_LINK_MTU, /* ignore = */ true, get_link_default_handler); + r = set_link_handler_internal(rtnl, m, req, link, /* ignore = */ true, get_link_default_handler); if (r <= 0) return r; @@ -219,222 +173,187 @@ static int link_set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l return 0; } -static int link_configure( +static int link_configure_fill_message( Link *link, - SetLinkOperation op, - void *userdata, - link_netlink_message_handler_t callback) { - - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + sd_netlink_message *req, + RequestType type, + void *userdata) { int r; - assert(link); - assert(link->manager); - assert(link->manager->rtnl); - assert(link->network); - assert(op >= 0 && op < _SET_LINK_OPERATION_MAX); - assert(callback); - - log_link_debug(link, "Setting %s", set_link_operation_to_string(op)); - - if (op == SET_LINK_BOND) { - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_NEWLINK, link->master_ifindex); + switch (type) { + case REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE: + r = ipv6ll_addrgen_mode_fill_message(req, PTR_TO_UINT8(userdata)); if (r < 0) - return log_link_debug_errno(link, r, "Could not allocate RTM_NEWLINK message: %m"); - } else if (IN_SET(op, SET_LINK_CAN, SET_LINK_IPOIB)) { - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_NEWLINK, link->ifindex); - if (r < 0) - return log_link_debug_errno(link, r, "Could not allocate RTM_NEWLINK message: %m"); - } else { - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); - if (r < 0) - return log_link_debug_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); - } - - switch (op) { - case SET_LINK_ADDRESS_GENERATION_MODE: - r = sd_netlink_message_open_container(req, IFLA_AF_SPEC); - if (r < 0) - return log_link_debug_errno(link, r, "Could not open IFLA_AF_SPEC container: %m"); - - r = sd_netlink_message_open_container(req, AF_INET6); - if (r < 0) - return log_link_debug_errno(link, r, "Could not open AF_INET6 container: %m"); - - r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, PTR_TO_UINT8(userdata)); - if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_INET6_ADDR_GEN_MODE attribute: %m"); - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_debug_errno(link, r, "Could not close AF_INET6 container: %m"); - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_debug_errno(link, r, "Could not close IFLA_AF_SPEC container: %m"); + return r; break; - case SET_LINK_BOND: + case REQUEST_TYPE_SET_LINK_BOND: r = sd_netlink_message_set_flags(req, NLM_F_REQUEST | NLM_F_ACK); if (r < 0) - return log_link_debug_errno(link, r, "Could not set netlink message flags: %m"); + return r; r = sd_netlink_message_open_container(req, IFLA_LINKINFO); if (r < 0) - return log_link_debug_errno(link, r, "Could not open IFLA_LINKINFO container: %m"); + return r; r = sd_netlink_message_open_container_union(req, IFLA_INFO_DATA, "bond"); if (r < 0) - return log_link_debug_errno(link, r, "Could not open IFLA_INFO_DATA container: %m"); + return r; if (link->network->active_slave) { r = sd_netlink_message_append_u32(req, IFLA_BOND_ACTIVE_SLAVE, link->ifindex); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BOND_ACTIVE_SLAVE attribute: %m"); + return r; } if (link->network->primary_slave) { r = sd_netlink_message_append_u32(req, IFLA_BOND_PRIMARY, link->ifindex); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BOND_PRIMARY attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_debug_errno(link, r, "Could not close IFLA_INFO_DATA container: %m"); + return r; r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_debug_errno(link, r, "Could not close IFLA_LINKINFO container: %m"); + return r; break; - case SET_LINK_BRIDGE: + case REQUEST_TYPE_SET_LINK_BRIDGE: r = sd_rtnl_message_link_set_family(req, AF_BRIDGE); if (r < 0) - return log_link_debug_errno(link, r, "Could not set message family: %m"); + return r; r = sd_netlink_message_open_container(req, IFLA_PROTINFO); if (r < 0) - return log_link_debug_errno(link, r, "Could not open IFLA_PROTINFO container: %m"); + return r; if (link->network->use_bpdu >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_GUARD, link->network->use_bpdu); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_GUARD attribute: %m"); + return r; } if (link->network->hairpin >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MODE, link->network->hairpin); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_MODE attribute: %m"); + return r; + } + + if (link->network->isolated >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_ISOLATED, link->network->isolated); + if (r < 0) + return r; } if (link->network->fast_leave >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_FAST_LEAVE, link->network->fast_leave); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_FAST_LEAVE attribute: %m"); + return r; } if (link->network->allow_port_to_be_root >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROTECT, link->network->allow_port_to_be_root); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_PROTECT attribute: %m"); + return r; } if (link->network->unicast_flood >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_UNICAST_FLOOD, link->network->unicast_flood); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_UNICAST_FLOOD attribute: %m"); + return r; } if (link->network->multicast_flood >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_FLOOD, link->network->multicast_flood); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_MCAST_FLOOD attribute: %m"); + return r; } if (link->network->multicast_to_unicast >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_TO_UCAST, link->network->multicast_to_unicast); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_MCAST_TO_UCAST attribute: %m"); + return r; } if (link->network->neighbor_suppression >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_NEIGH_SUPPRESS, link->network->neighbor_suppression); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_NEIGH_SUPPRESS attribute: %m"); + return r; } if (link->network->learning >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_LEARNING, link->network->learning); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_LEARNING attribute: %m"); + return r; } if (link->network->bridge_proxy_arp >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROXYARP, link->network->bridge_proxy_arp); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_PROXYARP attribute: %m"); + return r; } if (link->network->bridge_proxy_arp_wifi >= 0) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_PROXYARP_WIFI, link->network->bridge_proxy_arp_wifi); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_PROXYARP_WIFI attribute: %m"); + return r; } if (link->network->cost != 0) { r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_COST attribute: %m"); + return r; } if (link->network->priority != LINK_BRIDGE_PORT_PRIORITY_INVALID) { r = sd_netlink_message_append_u16(req, IFLA_BRPORT_PRIORITY, link->network->priority); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_PRIORITY attribute: %m"); + return r; } if (link->network->multicast_router != _MULTICAST_ROUTER_INVALID) { r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MULTICAST_ROUTER, link->network->multicast_router); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRPORT_MULTICAST_ROUTER attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_debug_errno(link, r, "Could not close IFLA_PROTINFO container: %m"); + return r; break; - case SET_LINK_BRIDGE_VLAN: + case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN: r = sd_rtnl_message_link_set_family(req, AF_BRIDGE); if (r < 0) - return log_link_debug_errno(link, r, "Could not set message family: %m"); + return r; r = sd_netlink_message_open_container(req, IFLA_AF_SPEC); if (r < 0) - return log_link_debug_errno(link, r, "Could not open IFLA_AF_SPEC container: %m"); + return r; if (link->master_ifindex <= 0) { /* master needs BRIDGE_FLAGS_SELF flag */ r = sd_netlink_message_append_u16(req, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_BRIDGE_FLAGS attribute: %m"); + return r; } r = bridge_vlan_append_info(link, req, link->network->pvid, link->network->br_vid_bitmap, link->network->br_untagged_bitmap); if (r < 0) - return log_link_debug_errno(link, r, "Could not append VLANs: %m"); + return r; r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_debug_errno(link, r, "Could not close IFLA_AF_SPEC container: %m"); + return r; break; - case SET_LINK_CAN: + case REQUEST_TYPE_SET_LINK_CAN: r = can_set_netlink_message(link, req); if (r < 0) return r; break; - case SET_LINK_FLAGS: { + case REQUEST_TYPE_SET_LINK_FLAGS: { unsigned ifi_change = 0, ifi_flags = 0; if (link->network->arp >= 0) { @@ -459,48 +378,68 @@ static int link_configure( r = sd_rtnl_message_link_set_flags(req, ifi_flags, ifi_change); if (r < 0) - return log_link_debug_errno(link, r, "Could not set link flags: %m"); + return r; break; } - case SET_LINK_GROUP: + case REQUEST_TYPE_SET_LINK_GROUP: r = sd_netlink_message_append_u32(req, IFLA_GROUP, (uint32_t) link->network->group); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_GROUP attribute: %m"); + return r; break; - case SET_LINK_MAC: + case REQUEST_TYPE_SET_LINK_MAC: r = netlink_message_append_hw_addr(req, IFLA_ADDRESS, &link->requested_hw_addr); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_ADDRESS attribute: %m"); + return r; break; - case SET_LINK_IPOIB: + case REQUEST_TYPE_SET_LINK_IPOIB: r = ipoib_set_netlink_message(link, req); if (r < 0) return r; break; - case SET_LINK_MASTER: + case REQUEST_TYPE_SET_LINK_MASTER: r = sd_netlink_message_append_u32(req, IFLA_MASTER, PTR_TO_UINT32(userdata)); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_MASTER attribute: %m"); + return r; break; - case SET_LINK_MTU: + case REQUEST_TYPE_SET_LINK_MTU: r = sd_netlink_message_append_u32(req, IFLA_MTU, PTR_TO_UINT32(userdata)); if (r < 0) - return log_link_debug_errno(link, r, "Could not append IFLA_MTU attribute: %m"); + return r; break; default: assert_not_reached(); } - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_debug_errno(link, r, "Could not send RTM_SETLINK message: %m"); - - link_ref(link); return 0; } +static int link_configure(Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + int r; + + assert(link); + assert(link->manager); + assert(req); + + log_link_debug(link, "Setting %s", request_type_to_string(req->type)); + + if (req->type == REQUEST_TYPE_SET_LINK_BOND) + r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->master_ifindex); + else if (IN_SET(req->type, REQUEST_TYPE_SET_LINK_CAN, REQUEST_TYPE_SET_LINK_IPOIB)) + r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_NEWLINK, link->ifindex); + else + r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_SETLINK, link->ifindex); + if (r < 0) + return r; + + r = link_configure_fill_message(link, m, req->type, req->userdata); + if (r < 0) + return r; + + return request_call_netlink_async(link->manager->rtnl, m, req); +} + static bool netdev_is_ready(NetDev *netdev) { assert(netdev); @@ -512,66 +451,63 @@ static bool netdev_is_ready(NetDev *netdev) { return true; } -static bool link_is_ready_to_call_set_link(Request *req) { - SetLinkOperation op; - Link *link; +static int link_is_ready_to_set_link(Link *link, Request *req) { int r; + assert(link); + assert(link->manager); + assert(link->network); assert(req); - assert(req->link); - assert(req->link->manager); - assert(req->link->network); - link = req->link; - op = PTR_TO_INT(req->set_link_operation_ptr); - - if (!IN_SET(link->state, LINK_STATE_INITIALIZED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) return false; - switch (op) { - case SET_LINK_BOND: - case SET_LINK_BRIDGE: + switch (req->type) { + case REQUEST_TYPE_SET_LINK_BOND: + case REQUEST_TYPE_SET_LINK_BRIDGE: if (!link->master_set) return false; + if (link->network->keep_master && link->master_ifindex <= 0) return false; break; - case SET_LINK_BRIDGE_VLAN: + + case REQUEST_TYPE_SET_LINK_BRIDGE_VLAN: if (!link->master_set) return false; + if (link->network->keep_master && link->master_ifindex <= 0 && !streq_ptr(link->kind, "bridge")) return false; + break; - case SET_LINK_CAN: + + case REQUEST_TYPE_SET_LINK_CAN: /* Do not check link->set_flgas_messages here, as it is ok even if link->flags * is outdated, and checking the counter causes a deadlock. */ if (FLAGS_SET(link->flags, IFF_UP)) { /* The CAN interface must be down to configure bitrate, etc... */ - r = link_down(link); - if (r < 0) { - link_enter_failed(link); - return false; - } + r = link_down_now(link); + if (r < 0) + return r; } break; - case SET_LINK_MAC: + + case REQUEST_TYPE_SET_LINK_MAC: if (req->netlink_handler == link_set_mac_handler) { /* This is the second attempt to set hardware address. On the first attempt * req->netlink_handler points to link_set_mac_allow_retry_handler(). * The first attempt failed as the interface was up. */ - r = link_down(link); - if (r < 0) { - link_enter_failed(link); - return false; - } + r = link_down_now(link); + if (r < 0) + return r; } break; - case SET_LINK_MASTER: { + + case REQUEST_TYPE_SET_LINK_MASTER: { uint32_t m = 0; Request req_mac = { .link = link, - .type = REQUEST_TYPE_SET_LINK, - .set_link_operation_ptr = INT_TO_PTR(SET_LINK_MAC), + .type = REQUEST_TYPE_SET_LINK_MAC, }; if (link->network->batadv) { @@ -589,11 +525,9 @@ static bool link_is_ready_to_call_set_link(Request *req) { * is outdated, and checking the counter causes a deadlock. */ if (FLAGS_SET(link->flags, IFF_UP)) { /* link must be down when joining to bond master. */ - r = link_down(link); - if (r < 0) { - link_enter_failed(link); - return false; - } + r = link_down_now(link); + if (r < 0) + return r; } } else if (link->network->bridge) { if (ordered_set_contains(link->manager->request_queue, &req_mac)) @@ -610,11 +544,10 @@ static bool link_is_ready_to_call_set_link(Request *req) { req->userdata = UINT32_TO_PTR(m); break; } - case SET_LINK_MTU: { + case REQUEST_TYPE_SET_LINK_MTU: { Request req_ipoib = { .link = link, - .type = REQUEST_TYPE_SET_LINK, - .set_link_operation_ptr = INT_TO_PTR(SET_LINK_IPOIB), + .type = REQUEST_TYPE_SET_LINK_IPOIB, }; return !ordered_set_contains(link->manager->request_queue, &req_ipoib); @@ -626,53 +559,44 @@ static bool link_is_ready_to_call_set_link(Request *req) { return true; } -int request_process_set_link(Request *req) { - SetLinkOperation op; +static int link_process_set_link(Request *req, Link *link, void *userdata) { int r; assert(req); - assert(req->link); - assert(req->type == REQUEST_TYPE_SET_LINK); - assert(req->netlink_handler); + assert(link); - op = PTR_TO_INT(req->set_link_operation_ptr); + r = link_is_ready_to_set_link(link, req); + if (r <= 0) + return r; - assert(op >= 0 && op < _SET_LINK_OPERATION_MAX); - - if (!link_is_ready_to_call_set_link(req)) - return 0; - - r = link_configure(req->link, op, req->userdata, req->netlink_handler); + r = link_configure(link, req); if (r < 0) - return log_link_error_errno(req->link, r, "Failed to set %s: %m", - set_link_operation_to_string(op)); - - if (op == SET_LINK_FLAGS) - req->link->set_flags_messages++; + return log_link_warning_errno(link, r, "Failed to set %s", request_type_to_string(req->type)); return 1; } static int link_request_set_link( Link *link, - SetLinkOperation op, - link_netlink_message_handler_t netlink_handler, + RequestType type, + request_netlink_handler_t netlink_handler, Request **ret) { Request *req; int r; assert(link); - assert(op >= 0 && op < _SET_LINK_OPERATION_MAX); - assert(netlink_handler); - r = link_queue_request(link, REQUEST_TYPE_SET_LINK, INT_TO_PTR(op), false, - &link->set_link_messages, netlink_handler, &req); + r = link_queue_request_full(link, type, NULL, NULL, NULL, NULL, + link_process_set_link, + &link->set_link_messages, + netlink_handler, + &req); if (r < 0) - return log_link_error_errno(link, r, "Failed to request to set %s: %m", - set_link_operation_to_string(op)); + return log_link_warning_errno(link, r, "Failed to request to set %s: %m", + request_type_to_string(type)); - log_link_debug(link, "Requested to set %s", set_link_operation_to_string(op)); + log_link_debug(link, "Requested to set %s", request_type_to_string(type)); if (ret) *ret = req; @@ -680,8 +604,8 @@ static int link_request_set_link( } int link_request_to_set_addrgen_mode(Link *link) { + IPv6LinkLocalAddressGenMode mode; Request *req; - uint8_t mode; int r; assert(link); @@ -690,16 +614,27 @@ int link_request_to_set_addrgen_mode(Link *link) { if (!socket_ipv6_is_supported()) return 0; - if (!link_ipv6ll_enabled(link)) - mode = IN6_ADDR_GEN_MODE_NONE; - else if (link->network->ipv6ll_address_gen_mode >= 0) - mode = link->network->ipv6ll_address_gen_mode; - else if (in6_addr_is_set(&link->network->ipv6ll_stable_secret)) - mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; - else - mode = IN6_ADDR_GEN_MODE_EUI64; + mode = link_get_ipv6ll_addrgen_mode(link); - r = link_request_set_link(link, SET_LINK_ADDRESS_GENERATION_MODE, link_set_addrgen_mode_handler, &req); + if (mode == link->ipv6ll_address_gen_mode) + return 0; + + /* If the link is already up, then changing the mode by netlink does not take effect until the + * link goes down. Hence, we need to reset the interface. However, setting the mode by sysctl + * does not need that. Let's use the sysctl interface when the link is already up. + * See also issue #22424. */ + if (mode != IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_NONE && + FLAGS_SET(link->flags, IFF_UP)) { + r = link_set_ipv6ll_addrgen_mode(link, mode); + if (r < 0) + log_link_warning_errno(link, r, "Cannot set IPv6 address generation mode, ignoring: %m"); + + return 0; + } + + r = link_request_set_link(link, REQUEST_TYPE_SET_LINK_ADDRESS_GENERATION_MODE, + link_set_addrgen_mode_handler, + &req); if (r < 0) return r; @@ -724,7 +659,8 @@ int link_request_to_set_bond(Link *link) { return 0; } - return link_request_set_link(link, SET_LINK_BOND, link_set_bond_handler, NULL); + return link_request_set_link(link, REQUEST_TYPE_SET_LINK_BOND, + link_set_bond_handler, NULL); } int link_request_to_set_bridge(Link *link) { @@ -744,7 +680,9 @@ int link_request_to_set_bridge(Link *link) { return 0; } - return link_request_set_link(link, SET_LINK_BRIDGE, link_set_bridge_handler, NULL); + return link_request_set_link(link, REQUEST_TYPE_SET_LINK_BRIDGE, + link_set_bridge_handler, + NULL); } int link_request_to_set_bridge_vlan(Link *link) { @@ -767,7 +705,9 @@ int link_request_to_set_bridge_vlan(Link *link) { return 0; } - return link_request_set_link(link, SET_LINK_BRIDGE_VLAN, link_set_bridge_vlan_handler, NULL); + return link_request_set_link(link, REQUEST_TYPE_SET_LINK_BRIDGE_VLAN, + link_set_bridge_vlan_handler, + NULL); } int link_request_to_set_can(Link *link) { @@ -780,7 +720,9 @@ int link_request_to_set_can(Link *link) { if (!streq_ptr(link->kind, "can")) return 0; - return link_request_set_link(link, SET_LINK_CAN, link_set_can_handler, NULL); + return link_request_set_link(link, REQUEST_TYPE_SET_LINK_CAN, + link_set_can_handler, + NULL); } int link_request_to_set_flags(Link *link) { @@ -793,7 +735,9 @@ int link_request_to_set_flags(Link *link) { link->network->promiscuous < 0) return 0; - return link_request_set_link(link, SET_LINK_FLAGS, link_set_flags_handler, NULL); + return link_request_set_link(link, REQUEST_TYPE_SET_LINK_FLAGS, + link_set_flags_handler, + NULL); } int link_request_to_set_group(Link *link) { @@ -803,7 +747,9 @@ int link_request_to_set_group(Link *link) { if (link->network->group < 0) return 0; - return link_request_set_link(link, SET_LINK_GROUP, link_set_group_handler, NULL); + return link_request_set_link(link, REQUEST_TYPE_SET_LINK_GROUP, + link_set_group_handler, + NULL); } int link_request_to_set_mac(Link *link, bool allow_retry) { @@ -824,7 +770,7 @@ int link_request_to_set_mac(Link *link, bool allow_retry) { if (hw_addr_equal(&link->hw_addr, &link->requested_hw_addr)) return 0; - return link_request_set_link(link, SET_LINK_MAC, + return link_request_set_link(link, REQUEST_TYPE_SET_LINK_MAC, allow_retry ? link_set_mac_allow_retry_handler : link_set_mac_handler, NULL); } @@ -840,7 +786,9 @@ int link_request_to_set_ipoib(Link *link) { link->network->ipoib_umcast < 0) return 0; - return link_request_set_link(link, SET_LINK_IPOIB, link_set_ipoib_handler, NULL); + return link_request_set_link(link, REQUEST_TYPE_SET_LINK_IPOIB, + link_set_ipoib_handler, + NULL); } int link_request_to_set_master(Link *link) { @@ -855,15 +803,19 @@ int link_request_to_set_master(Link *link) { link->master_set = false; if (link->network->batadv || link->network->bond || link->network->bridge || link->network->vrf) - return link_request_set_link(link, SET_LINK_MASTER, link_set_master_handler, NULL); + return link_request_set_link(link, REQUEST_TYPE_SET_LINK_MASTER, + link_set_master_handler, + NULL); else - return link_request_set_link(link, SET_LINK_MASTER, link_unset_master_handler, NULL); + return link_request_set_link(link, REQUEST_TYPE_SET_LINK_MASTER, + link_unset_master_handler, + NULL); } int link_request_to_set_mtu(Link *link, uint32_t mtu) { - Request *req; const char *origin; uint32_t min_mtu; + Request *req; int r; assert(link); @@ -899,7 +851,9 @@ int link_request_to_set_mtu(Link *link, uint32_t mtu) { if (link->mtu == mtu) return 0; - r = link_request_set_link(link, SET_LINK_MTU, link_set_mtu_handler, &req); + r = link_request_set_link(link, REQUEST_TYPE_SET_LINK_MTU, + link_set_mtu_handler, + &req); if (r < 0) return r; @@ -948,107 +902,113 @@ int link_configure_mtu(Link *link) { return link_request_to_set_mtu(link, mtu); } -static int link_up_or_down_handler_internal(sd_netlink *rtnl, sd_netlink_message *m, Link *link, bool up, bool check_ready) { +static int link_up_dsa_slave(Link *link) { + Link *master; + int r; + + assert(link); + + /* For older kernels (specifically, older than 9d5ef190e5615a7b63af89f88c4106a5bc127974, kernel-5.12), + * it is necessary to bring up a DSA slave that its master interface is already up. And bringing up + * the slave fails with -ENETDOWN. So, let's bring up the master even if it is not managed by us, + * and try to bring up the slave after the master becomes up. */ + + if (link->dsa_master_ifindex <= 0) + return 0; + + if (!streq_ptr(link->driver, "dsa")) + return 0; + + if (link_get_by_index(link->manager, link->dsa_master_ifindex, &master) < 0) + return 0; + + if (master->state == LINK_STATE_UNMANAGED) { + /* If the DSA master interface is unmanaged, then it will never become up. + * Let's request to bring up the master. */ + r = link_request_to_bring_up_or_down(master, /* up = */ true); + if (r < 0) + return r; + } + + r = link_request_to_bring_up_or_down(link, /* up = */ true); + if (r < 0) + return r; + + return 1; +} + +static int link_up_or_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, void *userdata) { + bool on_activate, up; int r; assert(m); + assert(req); assert(link); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - goto on_error; + on_activate = req->type == REQUEST_TYPE_ACTIVATE_LINK; + up = PTR_TO_INT(req->userdata); r = sd_netlink_message_get_errno(m); - if (r < 0) - log_link_message_warning_errno(link, m, r, up ? - "Could not bring up interface, ignoring" : - "Could not bring down interface, ignoring"); + if (r == -ENETDOWN && up && link_up_dsa_slave(link) > 0) + log_link_message_debug_errno(link, m, r, "Could not bring up dsa slave, retrying again after dsa master becomes up"); + else if (r < 0) { + const char *error_msg; + + error_msg = up ? + (on_activate ? "Could not bring up interface" : "Could not bring up interface, ignoring") : + (on_activate ? "Could not bring down interface" : "Could not bring down interface, ignoring"); + + log_link_message_warning_errno(link, m, r, error_msg); + if (on_activate) + return 0; + } r = link_call_getlink(link, get_link_update_flag_handler); if (r < 0) { link_enter_failed(link); - goto on_error; + return 0; } - if (check_ready) { + link->set_flags_messages++; + + if (on_activate) { link->activated = true; link_check_ready(link); } - return 1; - -on_error: - assert(link->set_flags_messages > 0); - link->set_flags_messages--; - return 0; } -static int link_activate_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return link_up_or_down_handler_internal(rtnl, m, link, true, true); -} - -static int link_activate_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return link_up_or_down_handler_internal(rtnl, m, link, false, true); -} - -static int link_up_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return link_up_or_down_handler_internal(rtnl, m, link, true, false); -} - -static int link_down_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { - return link_up_or_down_handler_internal(rtnl, m, link, false, false); -} - static const char *up_or_down(bool up) { return up ? "up" : "down"; } -static int link_up_or_down(Link *link, bool up, link_netlink_message_handler_t callback) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int link_up_or_down(Link *link, bool up, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; assert(link); assert(link->manager); assert(link->manager->rtnl); - assert(callback); + assert(req); log_link_debug(link, "Bringing link %s", up_or_down(up)); - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_SETLINK, link->ifindex); if (r < 0) - return log_link_debug_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + return r; - r = sd_rtnl_message_link_set_flags(req, up ? IFF_UP : 0, IFF_UP); + r = sd_rtnl_message_link_set_flags(m, up ? IFF_UP : 0, IFF_UP); if (r < 0) - return log_link_debug_errno(link, r, "Could not set link flags: %m"); + return r; - r = netlink_call_async(link->manager->rtnl, NULL, req, callback, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_debug_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - - return 0; -} - -int link_down(Link *link) { - int r; - - assert(link); - - r = link_up_or_down(link, false, link_down_handler); - if (r < 0) - return log_link_error_errno(link, r, "Failed to bring down interface: %m"); - - link->set_flags_messages++; - return 0; + return request_call_netlink_async(link->manager->rtnl, m, req); } static bool link_is_ready_to_activate(Link *link) { assert(link); - if (!IN_SET(link->state, LINK_STATE_INITIALIZED, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) return false; if (link->set_link_messages > 0) @@ -1057,31 +1017,24 @@ static bool link_is_ready_to_activate(Link *link) { return true; } -int request_process_activation(Request *req) { - Link *link; - bool up; +static int link_process_activation(Request *req, Link *link, void *userdata) { + bool up = PTR_TO_INT(userdata); int r; assert(req); - assert(req->link); - assert(req->type == REQUEST_TYPE_ACTIVATE_LINK); - assert(req->netlink_handler); - - link = req->link; - up = PTR_TO_INT(req->userdata); + assert(link); if (!link_is_ready_to_activate(link)) return 0; - r = link_up_or_down(link, up, req->netlink_handler); + r = link_up_or_down(link, up, req); if (r < 0) - return log_link_error_errno(link, r, "Failed to bring %s: %m", up_or_down(up)); + return log_link_warning_errno(link, r, "Failed to activate link: %m"); return 1; } int link_request_to_activate(Link *link) { - Request *req; bool up; int r; @@ -1112,20 +1065,33 @@ int link_request_to_activate(Link *link) { link->activated = false; - r = link_queue_request(link, REQUEST_TYPE_ACTIVATE_LINK, NULL, false, &link->set_flags_messages, - up ? link_activate_up_handler : link_activate_down_handler, &req); + r = link_queue_request_full(link, REQUEST_TYPE_ACTIVATE_LINK, + INT_TO_PTR(up), NULL, NULL, NULL, + link_process_activation, + &link->set_flags_messages, + link_up_or_down_handler, NULL); if (r < 0) return log_link_error_errno(link, r, "Failed to request to activate link: %m"); - req->userdata = INT_TO_PTR(up); - log_link_debug(link, "Requested to activate link"); return 0; } -static bool link_is_ready_to_bring_up_or_down(Link *link) { +static bool link_is_ready_to_bring_up_or_down(Link *link, bool up) { assert(link); + if (up && link->dsa_master_ifindex > 0) { + Link *master; + + /* The master interface must be up. See comments in link_up_dsa_slave(). */ + + if (link_get_by_index(link->manager, link->dsa_master_ifindex, &master) < 0) + return false; + + if (!FLAGS_SET(master->flags, IFF_UP)) + return false; + } + if (link->state == LINK_STATE_UNMANAGED) return true; @@ -1141,46 +1107,95 @@ static bool link_is_ready_to_bring_up_or_down(Link *link) { return true; } -int request_process_link_up_or_down(Request *req) { - Link *link; - bool up; +static int link_process_up_or_down(Request *req, Link *link, void *userdata) { + bool up = PTR_TO_INT(userdata); int r; assert(req); - assert(req->link); - assert(req->type == REQUEST_TYPE_UP_DOWN); + assert(link); - link = req->link; - up = PTR_TO_INT(req->userdata); - - if (!link_is_ready_to_bring_up_or_down(link)) + if (!link_is_ready_to_bring_up_or_down(link, up)) return 0; - r = link_up_or_down(link, up, req->netlink_handler); + r = link_up_or_down(link, up, req); if (r < 0) - return log_link_error_errno(link, r, "Failed to bring %s: %m", up_or_down(up)); + return log_link_warning_errno(link, r, "Failed to bring link %s: %m", up_or_down(up)); return 1; } int link_request_to_bring_up_or_down(Link *link, bool up) { - Request *req; int r; assert(link); - r = link_queue_request(link, REQUEST_TYPE_UP_DOWN, NULL, false, &link->set_flags_messages, - up ? link_up_handler : link_down_handler, &req); + r = link_queue_request_full(link, REQUEST_TYPE_UP_DOWN, + INT_TO_PTR(up), NULL, NULL, NULL, + link_process_up_or_down, + &link->set_flags_messages, + link_up_or_down_handler, NULL); if (r < 0) - return log_link_error_errno(link, r, "Failed to request to bring %s link: %m", - up_or_down(up)); - - req->userdata = INT_TO_PTR(up); + return log_link_warning_errno(link, r, "Failed to request to bring link %s: %m", + up_or_down(up)); log_link_debug(link, "Requested to bring link %s", up_or_down(up)); return 0; } +static int link_down_now_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { + int r; + + assert(m); + assert(link); + assert(link->set_flags_messages > 0); + + link->set_flags_messages--; + + if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) + return 0; + + r = sd_netlink_message_get_errno(m); + if (r < 0) + log_link_message_warning_errno(link, m, r, "Could not bring down interface, ignoring"); + + r = link_call_getlink(link, get_link_update_flag_handler); + if (r < 0) { + link_enter_failed(link); + return 0; + } + + link->set_flags_messages++; + return 0; +} + +int link_down_now(Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(link->manager); + assert(link->manager->rtnl); + + log_link_debug(link, "Bringing link down"); + + r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return log_link_warning_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + + r = sd_rtnl_message_link_set_flags(req, 0, IFF_UP); + if (r < 0) + return log_link_warning_errno(link, r, "Could not set link flags: %m"); + + r = netlink_call_async(link->manager->rtnl, NULL, req, link_down_now_handler, + link_netlink_destroy_callback, link); + if (r < 0) + return log_link_warning_errno(link, r, "Could not send rtnetlink message: %m"); + + link->set_flags_messages++; + link_ref(link); + return 0; +} + static int link_remove_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; diff --git a/src/network/networkd-setlink.h b/src/network/networkd-setlink.h index 47d87837e..7e5ba32ef 100644 --- a/src/network/networkd-setlink.h +++ b/src/network/networkd-setlink.h @@ -2,29 +2,9 @@ #pragma once #include +#include typedef struct Link Link; -typedef struct Request Request; - -typedef enum SetLinkOperation { - SET_LINK_ADDRESS_GENERATION_MODE, /* Setting IPv6LL address generation mode. */ - SET_LINK_BOND, /* Setting bond configs. */ - SET_LINK_BRIDGE, /* Setting bridge configs. */ - SET_LINK_BRIDGE_VLAN, /* Setting bridge VLAN configs. */ - SET_LINK_CAN, /* Setting CAN interface configs. */ - SET_LINK_FLAGS, /* Setting IFF_NOARP or friends. */ - SET_LINK_GROUP, /* Setting interface group. */ - SET_LINK_IPOIB, /* Setting IPoIB configs. */ - SET_LINK_MAC, /* Setting MAC address. */ - SET_LINK_MASTER, /* Setting IFLA_MASTER. */ - SET_LINK_MTU, /* Setting MTU. */ - _SET_LINK_OPERATION_MAX, - _SET_LINK_OPERATION_INVALID = -EINVAL, -} SetLinkOperation; - -/* SetLinkOperation is casted to int, then stored in void* with INT_TO_PTR(). */ -assert_cc(sizeof(SetLinkOperation) <= sizeof(void*)); -assert_cc(sizeof(SetLinkOperation) <= sizeof(int)); int link_request_to_set_addrgen_mode(Link *link); int link_request_to_set_bond(Link *link); @@ -40,14 +20,9 @@ int link_request_to_set_mtu(Link *link, uint32_t mtu); int link_configure_mtu(Link *link); -int request_process_set_link(Request *req); - -int link_down(Link *link); - -int request_process_activation(Request *req); int link_request_to_activate(Link *link); -int request_process_link_up_or_down(Request *req); int link_request_to_bring_up_or_down(Link *link, bool up); +int link_down_now(Link *link); int link_remove(Link *link); diff --git a/src/network/networkd-sriov.c b/src/network/networkd-sriov.c index 6da0f8352..09980b425 100644 --- a/src/network/networkd-sriov.c +++ b/src/network/networkd-sriov.c @@ -1,92 +1,16 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later * Copyright © 2020 VMware, Inc. */ -#include "alloc-util.h" -#include "netlink-util.h" +#include "networkd-link.h" #include "networkd-manager.h" +#include "networkd-queue.h" #include "networkd-sriov.h" -#include "parse-util.h" -#include "set.h" -#include "string-util.h" -static int sr_iov_new(SRIOV **ret) { - SRIOV *sr_iov; - - sr_iov = new(SRIOV, 1); - if (!sr_iov) - return -ENOMEM; - - *sr_iov = (SRIOV) { - .vf = UINT32_MAX, - .vlan_proto = ETH_P_8021Q, - .vf_spoof_check_setting = -1, - .trust = -1, - .query_rss = -1, - .link_state = _SR_IOV_LINK_STATE_INVALID, - }; - - *ret = TAKE_PTR(sr_iov); - - return 0; -} - -static int sr_iov_new_static(Network *network, const char *filename, unsigned section_line, SRIOV **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; - _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL; - SRIOV *existing = NULL; - int r; - - assert(network); - assert(ret); - assert(filename); - assert(section_line > 0); - - r = network_config_section_new(filename, section_line, &n); - if (r < 0) - return r; - - existing = ordered_hashmap_get(network->sr_iov_by_section, n); - if (existing) { - *ret = existing; - return 0; - } - - r = sr_iov_new(&sr_iov); - if (r < 0) - return r; - - sr_iov->network = network; - sr_iov->section = TAKE_PTR(n); - - r = ordered_hashmap_ensure_put(&network->sr_iov_by_section, &network_config_hash_ops, sr_iov->section, sr_iov); - if (r < 0) - return r; - - *ret = TAKE_PTR(sr_iov); - return 0; -} - -SRIOV *sr_iov_free(SRIOV *sr_iov) { - if (!sr_iov) - return NULL; - - if (sr_iov->network && sr_iov->section) - ordered_hashmap_remove(sr_iov->network->sr_iov_by_section, sr_iov->section); - - network_config_section_free(sr_iov->section); - - return mfree(sr_iov); -} - -static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, SRIOV *sr_iov) { int r; + assert(m); assert(link); - assert(link->sr_iov_messages > 0); - link->sr_iov_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) { @@ -104,432 +28,78 @@ static int sr_iov_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { return 1; } -static int sr_iov_configure(Link *link, SRIOV *sr_iov) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int sr_iov_configure(SRIOV *sr_iov, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; + assert(sr_iov); assert(link); assert(link->manager); assert(link->manager->rtnl); assert(link->ifindex > 0); + assert(req); - log_link_debug(link, "Setting SR-IOV virtual function %"PRIu32, sr_iov->vf); + log_link_debug(link, "Setting SR-IOV virtual function %"PRIu32".", sr_iov->vf); - r = sd_rtnl_message_new_link(link->manager->rtnl, &req, RTM_SETLINK, link->ifindex); + r = sd_rtnl_message_new_link(link->manager->rtnl, &m, RTM_SETLINK, link->ifindex); if (r < 0) - return log_link_error_errno(link, r, "Could not allocate RTM_SETLINK message: %m"); + return r; - r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST); + r = sr_iov_set_netlink_message(sr_iov, m); if (r < 0) - return log_link_error_errno(link, r, "Could not open IFLA_VFINFO_LIST container: %m"); + return r; - r = sd_netlink_message_open_container(req, IFLA_VF_INFO); - if (r < 0) - return log_link_error_errno(link, r, "Could not open IFLA_VF_INFO container: %m"); - - if (!ether_addr_is_null(&sr_iov->mac)) { - struct ifla_vf_mac ivm = { - .vf = sr_iov->vf, - }; - - memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN); - r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_MAC: %m"); - } - - if (sr_iov->vf_spoof_check_setting >= 0) { - struct ifla_vf_spoofchk ivs = { - .vf = sr_iov->vf, - .setting = sr_iov->vf_spoof_check_setting, - }; - - r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_SPOOFCHK: %m"); - } - - if (sr_iov->query_rss >= 0) { - struct ifla_vf_rss_query_en ivs = { - .vf = sr_iov->vf, - .setting = sr_iov->query_rss, - }; - - r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_RSS_QUERY_EN: %m"); - } - - if (sr_iov->trust >= 0) { - struct ifla_vf_trust ivt = { - .vf = sr_iov->vf, - .setting = sr_iov->trust, - }; - - r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_TRUST: %m"); - } - - if (sr_iov->link_state >= 0) { - struct ifla_vf_link_state ivl = { - .vf = sr_iov->vf, - .link_state = sr_iov->link_state, - }; - - r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_LINK_STATE: %m"); - } - - if (sr_iov->vlan > 0) { - /* Because of padding, first the buffer must be initialized with 0. */ - struct ifla_vf_vlan_info ivvi = {}; - ivvi.vf = sr_iov->vf; - ivvi.vlan = sr_iov->vlan; - ivvi.qos = sr_iov->qos; - ivvi.vlan_proto = htobe16(sr_iov->vlan_proto); - - r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST); - if (r < 0) - return log_link_error_errno(link, r, "Could not open IFLA_VF_VLAN_LIST container: %m"); - - r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info)); - if (r < 0) - return log_link_error_errno(link, r, "Could not append IFLA_VF_VLAN_INFO: %m"); - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not close IFLA_VF_VLAN_LIST container: %m"); - } - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not close IFLA_VF_INFO container: %m"); - - r = sd_netlink_message_close_container(req); - if (r < 0) - return log_link_error_errno(link, r, "Could not close IFLA_VFINFO_LIST container: %m"); - - r = netlink_call_async(link->manager->rtnl, NULL, req, sr_iov_handler, - link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - link->sr_iov_messages++; - - return 0; + return request_call_netlink_async(link->manager->rtnl, m, req); } -int link_configure_sr_iov(Link *link) { +static int sr_iov_process_request(Request *req, Link *link, SRIOV *sr_iov) { + int r; + + assert(req); + assert(link); + assert(sr_iov); + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return 0; + + r = sr_iov_configure(sr_iov, link, req); + if (r < 0) + return log_link_warning_errno(link, r, + "Failed to configure SR-IOV virtual function %"PRIu32": %m", + sr_iov->vf); + + return 1; +} + +int link_request_sr_iov_vfs(Link *link) { SRIOV *sr_iov; int r; assert(link); assert(link->network); - if (link->sr_iov_messages != 0) { - log_link_debug(link, "SR-IOV is configuring."); - return 0; - } - link->sr_iov_configured = false; ORDERED_HASHMAP_FOREACH(sr_iov, link->network->sr_iov_by_section) { - r = sr_iov_configure(link, sr_iov); + r = link_queue_request_safe(link, REQUEST_TYPE_SRIOV, + sr_iov, NULL, + sr_iov_hash_func, + sr_iov_compare_func, + sr_iov_process_request, + &link->sr_iov_messages, + sr_iov_handler, + NULL); if (r < 0) - return r; + return log_link_warning_errno(link, r, + "Failed to request SR-IOV virtual function %"PRIu32": %m", + sr_iov->vf); } - if (link->sr_iov_messages == 0) + if (link->sr_iov_messages == 0) { link->sr_iov_configured = true; - else + link_check_ready(link); + } else log_link_debug(link, "Configuring SR-IOV"); return 0; } - -static int sr_iov_section_verify(SRIOV *sr_iov) { - assert(sr_iov); - - if (section_is_invalid(sr_iov->section)) - return -EINVAL; - - if (sr_iov->vf == UINT32_MAX) - return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), - "%s: [SRIOV] section without VirtualFunction= field configured. " - "Ignoring [SRIOV] section from line %u.", - sr_iov->section->filename, sr_iov->section->line); - - return 0; -} - -void network_drop_invalid_sr_iov(Network *network) { - SRIOV *sr_iov; - - assert(network); - - ORDERED_HASHMAP_FOREACH(sr_iov, network->sr_iov_by_section) - if (sr_iov_section_verify(sr_iov) < 0) - sr_iov_free(sr_iov); -} - -int config_parse_sr_iov_uint32( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; - Network *network = data; - uint32_t k; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = sr_iov_new_static(network, filename, section_line, &sr_iov); - if (r < 0) - return r; - - if (isempty(rvalue)) { - if (streq(lvalue, "VirtualFunction")) - sr_iov->vf = UINT32_MAX; - else if (streq(lvalue, "VLANId")) - sr_iov->vlan = 0; - else if (streq(lvalue, "QualityOfService")) - sr_iov->qos = 0; - else - assert_not_reached(); - - TAKE_PTR(sr_iov); - return 0; - } - - r = safe_atou32(rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - if (streq(lvalue, "VLANId")) { - if (k == 0 || k > 4095) { - log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV VLANId: %d", k); - return 0; - } - sr_iov->vlan = k; - } else if (streq(lvalue, "VirtualFunction")) { - if (k >= INT_MAX) { - log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV virtual function: %d", k); - return 0; - } - sr_iov->vf = k; - } else if (streq(lvalue, "QualityOfService")) - sr_iov->qos = k; - else - assert_not_reached(); - - TAKE_PTR(sr_iov); - return 0; -} - -int config_parse_sr_iov_vlan_proto( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; - Network *network = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = sr_iov_new_static(network, filename, section_line, &sr_iov); - if (r < 0) - return r; - - if (isempty(rvalue) || streq(rvalue, "802.1Q")) - sr_iov->vlan_proto = ETH_P_8021Q; - else if (streq(rvalue, "802.1ad")) - sr_iov->vlan_proto = ETH_P_8021AD; - else { - log_syntax(unit, LOG_WARNING, filename, line, 0, - "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - TAKE_PTR(sr_iov); - return 0; -} - -int config_parse_sr_iov_link_state( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; - Network *network = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = sr_iov_new_static(network, filename, section_line, &sr_iov); - if (r < 0) - return r; - - /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use - * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */ - - if (isempty(rvalue)) { - sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID; - TAKE_PTR(sr_iov); - return 0; - } - - if (streq(rvalue, "auto")) { - sr_iov->link_state = SR_IOV_LINK_STATE_AUTO; - TAKE_PTR(sr_iov); - return 0; - } - - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE; - TAKE_PTR(sr_iov); - return 0; -} - -int config_parse_sr_iov_boolean( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; - Network *network = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = sr_iov_new_static(network, filename, section_line, &sr_iov); - if (r < 0) - return r; - - if (isempty(rvalue)) { - if (streq(lvalue, "MACSpoofCheck")) - sr_iov->vf_spoof_check_setting = -1; - else if (streq(lvalue, "QueryReceiveSideScaling")) - sr_iov->query_rss = -1; - else if (streq(lvalue, "Trust")) - sr_iov->trust = -1; - else - assert_not_reached(); - - TAKE_PTR(sr_iov); - return 0; - } - - r = parse_boolean(rvalue); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue); - return 0; - } - - if (streq(lvalue, "MACSpoofCheck")) - sr_iov->vf_spoof_check_setting = r; - else if (streq(lvalue, "QueryReceiveSideScaling")) - sr_iov->query_rss = r; - else if (streq(lvalue, "Trust")) - sr_iov->trust = r; - else - assert_not_reached(); - - TAKE_PTR(sr_iov); - return 0; -} - -int config_parse_sr_iov_mac( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; - Network *network = data; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = sr_iov_new_static(network, filename, section_line, &sr_iov); - if (r < 0) - return r; - - if (isempty(rvalue)) { - sr_iov->mac = ETHER_ADDR_NULL; - TAKE_PTR(sr_iov); - return 0; - } - - r = parse_ether_addr(rvalue, &sr_iov->mac); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - TAKE_PTR(sr_iov); - return 0; -} diff --git a/src/network/networkd-sriov.h b/src/network/networkd-sriov.h index 950d1f9c5..539fa0609 100644 --- a/src/network/networkd-sriov.h +++ b/src/network/networkd-sriov.h @@ -2,45 +2,8 @@ * Copyright © 2020 VMware, Inc. */ #pragma once -#include +#include "netif-sriov.h" -#include "conf-parser.h" -#include "ether-addr-util.h" -#include "networkd-link.h" -#include "networkd-network.h" -#include "networkd-util.h" +typedef struct Link Link; -typedef enum SRIOVLinkState { - SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO, - SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE, - SR_IOV_LINK_STATE_DISABLE = IFLA_VF_LINK_STATE_DISABLE, - _SR_IOV_LINK_STATE_MAX, - _SR_IOV_LINK_STATE_INVALID = -EINVAL, -} SRIOVLinkState; - -typedef struct SRIOV { - NetworkConfigSection *section; - Network *network; - - uint32_t vf; /* 0 - 2147483646 */ - uint32_t vlan; /* 0 - 4095, 0 disables VLAN filter */ - uint32_t qos; - uint16_t vlan_proto; /* ETH_P_8021Q or ETH_P_8021AD */ - int vf_spoof_check_setting; - int query_rss; - int trust; - SRIOVLinkState link_state; - struct ether_addr mac; -} SRIOV; - -SRIOV *sr_iov_free(SRIOV *sr_iov); -int link_configure_sr_iov(Link *link); -void network_drop_invalid_sr_iov(Network *network); - -DEFINE_NETWORK_SECTION_FUNCTIONS(SRIOV, sr_iov_free); - -CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_uint32); -CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_boolean); -CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_link_state); -CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_vlan_proto); -CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_mac); +int link_request_sr_iov_vfs(Link *link); diff --git a/src/network/networkd-sysctl.c b/src/network/networkd-sysctl.c index 6c7a606df..4e4b285f0 100644 --- a/src/network/networkd-sysctl.c +++ b/src/network/networkd-sysctl.c @@ -11,9 +11,6 @@ #include "string-table.h" #include "sysctl-util.h" -#define STABLE_SECRET_APP_ID_1 SD_ID128_MAKE(aa,05,1d,94,43,68,45,07,b9,73,f1,e8,e4,b7,34,52) -#define STABLE_SECRET_APP_ID_2 SD_ID128_MAKE(52,c4,40,a0,9f,2f,48,58,a9,3a,f6,29,25,ba,7a,7d) - static int link_update_ipv6_sysctl(Link *link) { assert(link); @@ -214,48 +211,6 @@ int link_set_ipv6_mtu(Link *link) { return sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", mtu); } -static int link_set_ipv6ll_stable_secret(Link *link) { - _cleanup_free_ char *str = NULL; - struct in6_addr a; - int r; - - assert(link); - assert(link->network); - - if (link->network->ipv6ll_address_gen_mode != IPV6_LINK_LOCAL_ADDRESSS_GEN_MODE_STABLE_PRIVACY) - return 0; - - if (in6_addr_is_set(&link->network->ipv6ll_stable_secret)) - a = link->network->ipv6ll_stable_secret; - else { - sd_id128_t key; - le64_t v; - - /* Generate a stable secret address from machine-ID and the interface name. */ - - r = sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_1, &key); - if (r < 0) - return log_link_debug_errno(link, r, "Failed to generate key: %m"); - - v = htole64(siphash24_string(link->ifname, key.bytes)); - memcpy(a.s6_addr, &v, sizeof(v)); - - r = sd_id128_get_machine_app_specific(STABLE_SECRET_APP_ID_2, &key); - if (r < 0) - return log_link_debug_errno(link, r, "Failed to generate key: %m"); - - v = htole64(siphash24_string(link->ifname, key.bytes)); - assert_cc(sizeof(v) * 2 == sizeof(a.s6_addr)); - memcpy(a.s6_addr + sizeof(v), &v, sizeof(v)); - } - - r = in6_addr_to_string(&a, &str); - if (r < 0) - return r; - - return sysctl_write_ip_property(AF_INET6, link->ifname, "stable_secret", str); -} - static int link_set_ipv4_accept_local(Link *link) { assert(link); diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index ac6f1680c..1c0987bc3 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -243,50 +243,6 @@ int config_parse_mud_url( return free_and_replace(*url, unescaped); } -static void network_config_hash_func(const NetworkConfigSection *c, struct siphash *state) { - siphash24_compress_string(c->filename, state); - siphash24_compress(&c->line, sizeof(c->line), state); -} - -static int network_config_compare_func(const NetworkConfigSection *x, const NetworkConfigSection *y) { - int r; - - r = strcmp(x->filename, y->filename); - if (r != 0) - return r; - - return CMP(x->line, y->line); -} - -DEFINE_HASH_OPS(network_config_hash_ops, NetworkConfigSection, network_config_hash_func, network_config_compare_func); - -int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s) { - NetworkConfigSection *cs; - - cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1); - if (!cs) - return -ENOMEM; - - strcpy(cs->filename, filename); - cs->line = line; - - *s = TAKE_PTR(cs); - - return 0; -} - -unsigned hashmap_find_free_section_line(Hashmap *hashmap) { - NetworkConfigSection *cs; - unsigned n = 0; - void *entry; - - HASHMAP_FOREACH_KEY(entry, cs, hashmap) - if (n < cs->line) - n = cs->line; - - return n + 1; -} - int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg) { const char *err_msg = NULL; diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index d94243855..0a627588a 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -13,12 +13,6 @@ typedef struct Link Link; -typedef struct NetworkConfigSection { - unsigned line; - bool invalid; - char filename[]; -} NetworkConfigSection; - typedef enum NetworkConfigSource { NETWORK_CONFIG_SOURCE_FOREIGN, /* configured by kernel */ NETWORK_CONFIG_SOURCE_STATIC, @@ -141,37 +135,6 @@ AddressFamily dhcp_deprecated_address_family_from_string(const char *s) _pure_; const char *dhcp_lease_server_type_to_string(sd_dhcp_lease_server_type_t t) _const_; sd_dhcp_lease_server_type_t dhcp_lease_server_type_from_string(const char *s) _pure_; -static inline NetworkConfigSection* network_config_section_free(NetworkConfigSection *cs) { - return mfree(cs); -} -DEFINE_TRIVIAL_CLEANUP_FUNC(NetworkConfigSection*, network_config_section_free); - -int network_config_section_new(const char *filename, unsigned line, NetworkConfigSection **s); -extern const struct hash_ops network_config_hash_ops; -unsigned hashmap_find_free_section_line(Hashmap *hashmap); - -static inline bool section_is_invalid(NetworkConfigSection *section) { - /* If this returns false, then it does _not_ mean the section is valid. */ - - if (!section) - return false; - - return section->invalid; -} - -#define DEFINE_NETWORK_SECTION_FUNCTIONS(type, free_func) \ - static inline type* free_func##_or_set_invalid(type *p) { \ - assert(p); \ - \ - if (p->section) \ - p->section->invalid = true; \ - else \ - free_func(p); \ - return NULL; \ - } \ - DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \ - DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid); - int log_link_message_full_errno(Link *link, sd_netlink_message *m, int level, int err, const char *msg); #define log_link_message_error_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_ERR, err, msg) #define log_link_message_warning_errno(link, m, err, msg) log_link_message_full_errno(link, m, LOG_WARNING, err, msg) diff --git a/src/network/networkd-wifi.c b/src/network/networkd-wifi.c index 8fd1cf503..13d6734a0 100644 --- a/src/network/networkd-wifi.c +++ b/src/network/networkd-wifi.c @@ -8,6 +8,7 @@ #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-wifi.h" +#include "networkd-wiphy.h" #include "string-util.h" #include "wifi-util.h" @@ -72,6 +73,8 @@ int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *me log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m"); return 0; } + if (IN_SET(cmd, NL80211_CMD_NEW_WIPHY, NL80211_CMD_DEL_WIPHY)) + return manager_genl_process_nl80211_wiphy(genl, message, manager); if (!IN_SET(cmd, NL80211_CMD_SET_INTERFACE, NL80211_CMD_NEW_INTERFACE, NL80211_CMD_DEL_INTERFACE)) { log_debug("nl80211: ignoring nl80211 %s(%u) message.", strna(nl80211_cmd_to_string(cmd)), cmd); @@ -132,7 +135,7 @@ int manager_genl_process_nl80211_config(sd_netlink *genl, sd_netlink_message *me strna(nl80211_cmd_to_string(cmd)), cmd, strna(nl80211_iftype_to_string(wlan_iftype)), strna(ssid)); - switch(cmd) { + switch (cmd) { case NL80211_CMD_SET_INTERFACE: case NL80211_CMD_NEW_INTERFACE: link->wlan_iftype = wlan_iftype; @@ -200,7 +203,7 @@ int manager_genl_process_nl80211_mlme(sd_netlink *genl, sd_netlink_message *mess return 0; } - switch(cmd) { + switch (cmd) { case NL80211_CMD_NEW_STATION: case NL80211_CMD_DEL_STATION: { struct ether_addr bssid; diff --git a/src/network/networkd-wiphy.c b/src/network/networkd-wiphy.c new file mode 100644 index 000000000..861ebe6b6 --- /dev/null +++ b/src/network/networkd-wiphy.c @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "device-util.h" +#include "networkd-manager.h" +#include "networkd-wiphy.h" +#include "parse-util.h" +#include "wifi-util.h" + +Wiphy *wiphy_free(Wiphy *w) { + if (!w) + return NULL; + + if (w->manager) { + hashmap_remove_value(w->manager->wiphy_by_index, UINT32_TO_PTR(w->index), w); + if (w->name) + hashmap_remove_value(w->manager->wiphy_by_name, w->name, w); + } + + free(w->name); + return mfree(w); +} + +static int wiphy_new(Manager *manager, uint32_t index, Wiphy **ret) { + _cleanup_(wiphy_freep) Wiphy *w = NULL; + int r; + + assert(manager); + + w = new(Wiphy, 1); + if (!w) + return -ENOMEM; + + *w = (Wiphy) { + .index = index, + }; + + r = hashmap_ensure_put(&manager->wiphy_by_index, NULL, UINT32_TO_PTR(w->index), w); + if (r < 0) + return r; + + w->manager = manager; + + if (ret) + *ret = w; + + TAKE_PTR(w); + return 0; +} + +int wiphy_get_by_index(Manager *manager, uint32_t index, Wiphy **ret) { + Wiphy *w; + + assert(manager); + + w = hashmap_get(manager->wiphy_by_index, UINT32_TO_PTR(index)); + if (!w) + return -ENODEV; + + if (ret) + *ret = w; + + return 0; +} + +int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret) { + Wiphy *w; + + assert(manager); + assert(name); + + w = hashmap_get(manager->wiphy_by_name, name); + if (!w) + return -ENODEV; + + if (ret) + *ret = w; + + return 0; +} + +static int wiphy_update_name(Wiphy *w, sd_netlink_message *message) { + const char *name; + int r; + + assert(w); + assert(w->manager); + assert(message); + + r = sd_netlink_message_read_string(message, NL80211_ATTR_WIPHY_NAME, &name); + if (r == -ENODATA) + return 0; + if (r < 0) + return r; + + if (streq_ptr(w->name, name)) + return 0; + + if (w->name) + hashmap_remove_value(w->manager->wiphy_by_name, w->name, w); + + r = free_and_strdup(&w->name, name); + if (r < 0) + return r; + + return hashmap_ensure_put(&w->manager->wiphy_by_name, &string_hash_ops, w->name, w); +} + +static int wiphy_update(Wiphy *w, sd_netlink_message *message) { + int r; + + assert(w); + assert(message); + + r = wiphy_update_name(w, message); + if (r < 0) + return log_wiphy_debug_errno(w, r, "Failed to update wiphy name: %m"); + + return 0; +} + +int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *message, Manager *manager) { + const char *family; + uint32_t index; + uint8_t cmd; + Wiphy *w = NULL; + int r; + + assert(genl); + assert(message); + assert(manager); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "nl80211: received error message, ignoring"); + + return 0; + } + + r = sd_genl_message_get_family_name(genl, message, &family); + if (r < 0) { + log_debug_errno(r, "nl80211: failed to determine genl family, ignoring: %m"); + return 0; + } + if (!streq(family, NL80211_GENL_NAME)) { + log_debug("nl80211: Received message of unexpected genl family '%s', ignoring.", family); + return 0; + } + + r = sd_genl_message_get_command(genl, message, &cmd); + if (r < 0) { + log_debug_errno(r, "nl80211: failed to determine genl message command, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_u32(message, NL80211_ATTR_WIPHY, &index); + if (r < 0) { + log_debug_errno(r, "nl80211: received %s(%u) message without valid index, ignoring: %m", + strna(nl80211_cmd_to_string(cmd)), cmd); + return 0; + } + + (void) wiphy_get_by_index(manager, index, &w); + + switch (cmd) { + case NL80211_CMD_NEW_WIPHY: { + bool is_new = !w; + + if (!w) { + r = wiphy_new(manager, index, &w); + if (r < 0) { + log_warning_errno(r, "Failed to allocate wiphy, ignoring: %m"); + return 0; + } + } + + r = wiphy_update(w, message); + if (r < 0) { + log_wiphy_warning_errno(w, r, "Failed to update wiphy, ignoring: %m"); + return 0; + } + + log_wiphy_debug(w, "Received %s phy.", is_new ? "new" : "updated"); + break; + } + case NL80211_CMD_DEL_WIPHY: + + if (!w) { + log_debug("The kernel removes wiphy we do not know, ignoring: %m"); + return 0; + } + + log_wiphy_debug(w, "Removed."); + wiphy_free(w); + break; + + default: + log_wiphy_debug(w, "nl80211: received %s(%u) message.", + strna(nl80211_cmd_to_string(cmd)), cmd); + } + + return 0; +} diff --git a/src/network/networkd-wiphy.h b/src/network/networkd-wiphy.h new file mode 100644 index 000000000..072a7e5fd --- /dev/null +++ b/src/network/networkd-wiphy.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "sd-device.h" + +#include "macro.h" + +typedef struct Manager Manager; + +typedef struct Wiphy { + Manager *manager; + + uint32_t index; + char *name; +} Wiphy; + +Wiphy *wiphy_free(Wiphy *w); +DEFINE_TRIVIAL_CLEANUP_FUNC(Wiphy*, wiphy_free); + +int wiphy_get_by_index(Manager *manager, uint32_t index, Wiphy **ret); +int wiphy_get_by_name(Manager *manager, const char *name, Wiphy **ret); + +int manager_genl_process_nl80211_wiphy(sd_netlink *genl, sd_netlink_message *message, Manager *manager); + +#define log_wiphy_full_errno_zerook(w, level, error, ...) \ + ({ \ + const Wiphy *_w = (w); \ + log_interface_full_errno_zerook(_w ? _w->name : NULL, level, error, __VA_ARGS__); \ + }) + +#define log_wiphy_full_errno(w, level, error, ...) \ + ({ \ + int _error = (error); \ + ASSERT_NON_ZERO(_error); \ + log_wiphy_full_errno_zerook(w, level, _error, __VA_ARGS__); \ + }) + +#define log_wiphy_full(w, level, ...) (void) log_wiphy_full_errno_zerook(w, level, 0, __VA_ARGS__) + +#define log_wiphy_debug(w, ...) log_wiphy_full(w, LOG_DEBUG, __VA_ARGS__) +#define log_wiphy_info(w, ...) log_wiphy_full(w, LOG_INFO, __VA_ARGS__) +#define log_wiphy_notice(w, ...) log_wiphy_full(w, LOG_NOTICE, __VA_ARGS__) +#define log_wiphy_warning(w, ...) log_wiphy_full(w, LOG_WARNING, __VA_ARGS__) +#define log_wiphy_error(w, ...) log_wiphy_full(w, LOG_ERR, __VA_ARGS__) + +#define log_wiphy_debug_errno(w, error, ...) log_wiphy_full_errno(w, LOG_DEBUG, error, __VA_ARGS__) +#define log_wiphy_info_errno(w, error, ...) log_wiphy_full_errno(w, LOG_INFO, error, __VA_ARGS__) +#define log_wiphy_notice_errno(w, error, ...) log_wiphy_full_errno(w, LOG_NOTICE, error, __VA_ARGS__) +#define log_wiphy_warning_errno(w, error, ...) log_wiphy_full_errno(w, LOG_WARNING, error, __VA_ARGS__) +#define log_wiphy_error_errno(w, error, ...) log_wiphy_full_errno(w, LOG_ERR, error, __VA_ARGS__) diff --git a/src/network/tc/cake.c b/src/network/tc/cake.c index b4cbc4f97..d52151e75 100644 --- a/src/network/tc/cake.c +++ b/src/network/tc/cake.c @@ -39,88 +39,88 @@ static int cake_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) assert(qdisc); assert(req); - c = CAKE(qdisc); + assert_se(c = CAKE(qdisc)); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "cake"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; if (c->bandwidth > 0) { r = sd_netlink_message_append_u64(req, TCA_CAKE_BASE_RATE64, c->bandwidth); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_BASE_RATE64 attribute: %m"); + return r; } if (c->autorate >= 0) { r = sd_netlink_message_append_u32(req, TCA_CAKE_AUTORATE, c->autorate); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_AUTORATE attribute: %m"); + return r; } if (c->overhead_set) { r = sd_netlink_message_append_s32(req, TCA_CAKE_OVERHEAD, c->overhead); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_OVERHEAD attribute: %m"); + return r; } if (c->mpu > 0) { r = sd_netlink_message_append_u32(req, TCA_CAKE_MPU, c->mpu); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_MPU attribute: %m"); + return r; } if (c->compensation_mode >= 0) { r = sd_netlink_message_append_u32(req, TCA_CAKE_ATM, c->compensation_mode); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_ATM attribute: %m"); + return r; } if (c->raw > 0) { /* TCA_CAKE_RAW attribute is mostly a flag, not boolean. */ r = sd_netlink_message_append_u32(req, TCA_CAKE_RAW, 0); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_RAW attribute: %m"); + return r; } if (c->flow_isolation_mode >= 0) { r = sd_netlink_message_append_u32(req, TCA_CAKE_FLOW_MODE, c->flow_isolation_mode); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_FLOW_MODE attribute: %m"); + return r; } if (c->nat >= 0) { r = sd_netlink_message_append_u32(req, TCA_CAKE_NAT, c->nat); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_NAT attribute: %m"); + return r; } if (c->preset >= 0) { r = sd_netlink_message_append_u32(req, TCA_CAKE_DIFFSERV_MODE, c->preset); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_DIFFSERV_MODE attribute: %m"); + return r; } if (c->fwmark > 0) { r = sd_netlink_message_append_u32(req, TCA_CAKE_FWMARK, c->fwmark); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_FWMARK attribute: %m"); + return r; } if (c->wash >= 0) { r = sd_netlink_message_append_u32(req, TCA_CAKE_WASH, c->wash); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_WASH attribute: %m"); + return r; } if (c->split_gso >= 0) { r = sd_netlink_message_append_u32(req, TCA_CAKE_SPLIT_GSO, c->split_gso); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CAKE_SPLIT_GSO attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/codel.c b/src/network/tc/codel.c index 33f40d87d..23351bf3d 100644 --- a/src/network/tc/codel.c +++ b/src/network/tc/codel.c @@ -31,45 +31,45 @@ static int controlled_delay_fill_message(Link *link, QDisc *qdisc, sd_netlink_me assert(qdisc); assert(req); - cd = CODEL(qdisc); + assert_se(cd = CODEL(qdisc)); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "codel"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; if (cd->packet_limit > 0) { r = sd_netlink_message_append_u32(req, TCA_CODEL_LIMIT, cd->packet_limit); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CODEL_LIMIT attribute: %m"); + return r; } if (cd->interval_usec > 0) { r = sd_netlink_message_append_u32(req, TCA_CODEL_INTERVAL, cd->interval_usec); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CODEL_INTERVAL attribute: %m"); + return r; } if (cd->target_usec > 0) { r = sd_netlink_message_append_u32(req, TCA_CODEL_TARGET, cd->target_usec); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CODEL_TARGET attribute: %m"); + return r; } if (cd->ecn >= 0) { r = sd_netlink_message_append_u32(req, TCA_CODEL_ECN, cd->ecn); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CODEL_ECN attribute: %m"); + return r; } if (cd->ce_threshold_usec != USEC_INFINITY) { r = sd_netlink_message_append_u32(req, TCA_CODEL_CE_THRESHOLD, cd->ce_threshold_usec); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_CODEL_CE_THRESHOLD attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/drr.c b/src/network/tc/drr.c index ab67b0ad3..742a56f8c 100644 --- a/src/network/tc/drr.c +++ b/src/network/tc/drr.c @@ -23,21 +23,21 @@ static int drr_class_fill_message(Link *link, TClass *tclass, sd_netlink_message assert(tclass); assert(req); - drr = TCLASS_TO_DRR(tclass); + assert_se(drr = TCLASS_TO_DRR(tclass)); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "drr"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; if (drr->quantum > 0) { r = sd_netlink_message_append_u32(req, TCA_DRR_QUANTUM, drr->quantum); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_DRR_QUANTUM, attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/ets.c b/src/network/tc/ets.c index f8dd1adab..00c46f24a 100644 --- a/src/network/tc/ets.c +++ b/src/network/tc/ets.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "conf-parser.h" #include "ets.h" +#include "extract-word.h" #include "memory-util.h" #include "netlink-util.h" #include "parse-util.h" @@ -20,57 +21,57 @@ static int enhanced_transmission_selection_fill_message(Link *link, QDisc *qdisc assert(qdisc); assert(req); - ets = ETS(qdisc); + assert_se(ets = ETS(qdisc)); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "ets"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; r = sd_netlink_message_append_u8(req, TCA_ETS_NBANDS, ets->n_bands); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_ETS_NBANDS attribute: %m"); + return r; if (ets->n_strict > 0) { r = sd_netlink_message_append_u8(req, TCA_ETS_NSTRICT, ets->n_strict); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_ETS_NSTRICT attribute: %m"); + return r; } if (ets->n_quanta > 0) { r = sd_netlink_message_open_container(req, TCA_ETS_QUANTA); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_ETS_QUANTA: %m"); + return r; for (unsigned i = 0; i < ets->n_quanta; i++) { r = sd_netlink_message_append_u32(req, TCA_ETS_QUANTA_BAND, ets->quanta[i]); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_ETS_QUANTA_BAND attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_ETS_QUANTA: %m"); + return r; } if (ets->n_prio > 0) { r = sd_netlink_message_open_container(req, TCA_ETS_PRIOMAP); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_ETS_PRIOMAP: %m"); + return r; for (unsigned i = 0; i < ets->n_prio; i++) { r = sd_netlink_message_append_u8(req, TCA_ETS_PRIOMAP_BAND, ets->prio[i]); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_ETS_PRIOMAP_BAND attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_ETS_PRIOMAP: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/fifo.c b/src/network/tc/fifo.c index 3dd7f0b5f..948d07186 100644 --- a/src/network/tc/fifo.c +++ b/src/network/tc/fifo.c @@ -11,7 +11,6 @@ #include "string-util.h" static int fifo_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { - struct tc_fifo_qopt opt = {}; FirstInFirstOut *fifo; int r; @@ -19,25 +18,24 @@ static int fifo_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) assert(qdisc); assert(req); - switch(qdisc->kind) { + switch (qdisc->kind) { case QDISC_KIND_PFIFO: - fifo = PFIFO(qdisc); + assert_se(fifo = PFIFO(qdisc)); break; case QDISC_KIND_BFIFO: - fifo = BFIFO(qdisc); + assert_se(fifo = BFIFO(qdisc)); break; case QDISC_KIND_PFIFO_HEAD_DROP: - fifo = PFIFO_HEAD_DROP(qdisc); + assert_se(fifo = PFIFO_HEAD_DROP(qdisc)); break; default: assert_not_reached(); } - opt.limit = fifo->limit; - - r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_fifo_qopt)); + const struct tc_fifo_qopt opt = { .limit = fifo->limit }; + r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(opt)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_OPTIONS attribute: %m"); + return r; return 0; } @@ -73,7 +71,7 @@ int config_parse_pfifo_size( return 0; } - switch(qdisc->kind) { + switch (qdisc->kind) { case QDISC_KIND_PFIFO: fifo = PFIFO(qdisc); break; diff --git a/src/network/tc/fq-codel.c b/src/network/tc/fq-codel.c index 7c61cf2ac..42dfd632a 100644 --- a/src/network/tc/fq-codel.c +++ b/src/network/tc/fq-codel.c @@ -33,63 +33,63 @@ static int fair_queueing_controlled_delay_fill_message(Link *link, QDisc *qdisc, assert(qdisc); assert(req); - fqcd = FQ_CODEL(qdisc); + assert_se(fqcd = FQ_CODEL(qdisc)); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "fq_codel"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; if (fqcd->packet_limit > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_LIMIT, fqcd->packet_limit); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_LIMIT attribute: %m"); + return r; } if (fqcd->flows > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_FLOWS, fqcd->flows); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_FLOWS attribute: %m"); + return r; } if (fqcd->quantum > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_QUANTUM, fqcd->quantum); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_QUANTUM attribute: %m"); + return r; } if (fqcd->interval_usec > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_INTERVAL, fqcd->interval_usec); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_INTERVAL attribute: %m"); + return r; } if (fqcd->target_usec > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_TARGET, fqcd->target_usec); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_TARGET attribute: %m"); + return r; } if (fqcd->ecn >= 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_ECN, fqcd->ecn); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_ECN attribute: %m"); + return r; } if (fqcd->ce_threshold_usec != USEC_INFINITY) { r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_CE_THRESHOLD, fqcd->ce_threshold_usec); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_CE_THRESHOLD attribute: %m"); + return r; } if (fqcd->memory_limit != UINT32_MAX) { r = sd_netlink_message_append_u32(req, TCA_FQ_CODEL_MEMORY_LIMIT, fqcd->memory_limit); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_CODEL_MEMORY_LIMIT attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/fq-pie.c b/src/network/tc/fq-pie.c index c7d7623b4..d4a9d5966 100644 --- a/src/network/tc/fq-pie.c +++ b/src/network/tc/fq-pie.c @@ -18,21 +18,21 @@ static int fq_pie_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req assert(qdisc); assert(req); - fq_pie = FQ_PIE(qdisc); + assert_se(fq_pie = FQ_PIE(qdisc)); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "fq_pie"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; if (fq_pie->packet_limit > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_PIE_LIMIT, fq_pie->packet_limit); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_PIE_PLIMIT attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/fq.c b/src/network/tc/fq.c index d10a86349..1e69a66d7 100644 --- a/src/network/tc/fq.c +++ b/src/network/tc/fq.c @@ -32,46 +32,46 @@ static int fair_queueing_fill_message(Link *link, QDisc *qdisc, sd_netlink_messa assert(qdisc); assert(req); - fq = FQ(qdisc); + assert_se(fq = FQ(qdisc)); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "fq"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; if (fq->packet_limit > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_PLIMIT, fq->packet_limit); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_PLIMIT attribute: %m"); + return r; } if (fq->flow_limit > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_FLOW_PLIMIT, fq->flow_limit); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_FLOW_PLIMIT attribute: %m"); + return r; } if (fq->quantum > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_QUANTUM, fq->quantum); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_QUANTUM attribute: %m"); + return r; } if (fq->initial_quantum > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_INITIAL_QUANTUM, fq->initial_quantum); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_INITIAL_QUANTUM attribute: %m"); + return r; } if (fq->pacing >= 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_RATE_ENABLE, fq->pacing); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_RATE_ENABLE attribute: %m"); + return r; } if (fq->max_rate > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_FLOW_MAX_RATE, fq->max_rate); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_FLOW_MAX_RATE attribute: %m"); + return r; } if (fq->buckets > 0) { @@ -80,24 +80,24 @@ static int fair_queueing_fill_message(Link *link, QDisc *qdisc, sd_netlink_messa l = log2u(fq->buckets); r = sd_netlink_message_append_u32(req, TCA_FQ_BUCKETS_LOG, l); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_BUCKETS_LOG attribute: %m"); + return r; } if (fq->orphan_mask > 0) { r = sd_netlink_message_append_u32(req, TCA_FQ_ORPHAN_MASK, fq->orphan_mask); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_ORPHAN_MASK attribute: %m"); + return r; } if (fq->ce_threshold_usec != USEC_INFINITY) { r = sd_netlink_message_append_u32(req, TCA_FQ_CE_THRESHOLD, fq->ce_threshold_usec); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_FQ_CE_THRESHOLD attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/gred.c b/src/network/tc/gred.c index ce26e6245..96281e6bd 100644 --- a/src/network/tc/gred.c +++ b/src/network/tc/gred.c @@ -24,32 +24,31 @@ static int generic_random_early_detection_init(QDisc *qdisc) { static int generic_random_early_detection_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { GenericRandomEarlyDetection *gred; - struct tc_gred_sopt opt = {}; int r; assert(link); assert(qdisc); assert(req); - gred = GRED(qdisc); + assert_se(gred = GRED(qdisc)); - opt.DPs = gred->virtual_queues; - opt.def_DP = gred->default_virtual_queue; - - if (gred->grio >= 0) - opt.grio = gred->grio; + const struct tc_gred_sopt opt = { + .DPs = gred->virtual_queues, + .def_DP = gred->default_virtual_queue, + .grio = gred->grio, + }; r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "gred"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; - r = sd_netlink_message_append_data(req, TCA_GRED_DPS, &opt, sizeof(struct tc_gred_sopt)); + r = sd_netlink_message_append_data(req, TCA_GRED_DPS, &opt, sizeof(opt)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_GRED_DPS attribute: %m"); + return r; r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/hhf.c b/src/network/tc/hhf.c index 68a4b4571..aac4feff4 100644 --- a/src/network/tc/hhf.c +++ b/src/network/tc/hhf.c @@ -19,21 +19,21 @@ static int heavy_hitter_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink assert(qdisc); assert(req); - hhf = HHF(qdisc); + assert_se(hhf = HHF(qdisc)); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "hhf"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; if (hhf->packet_limit > 0) { r = sd_netlink_message_append_u32(req, TCA_HHF_BACKLOG_LIMIT, hhf->packet_limit); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_HHF_BACKLOG_LIMIT attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/htb.c b/src/network/tc/htb.c index 14fb1f92e..f50b0e501 100644 --- a/src/network/tc/htb.c +++ b/src/network/tc/htb.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "conf-parser.h" #include "netlink-util.h" +#include "networkd-link.h" #include "parse-util.h" #include "qdisc.h" #include "htb.h" @@ -16,31 +17,31 @@ static int hierarchy_token_bucket_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { HierarchyTokenBucket *htb; - struct tc_htb_glob opt = { - .version = 3, - }; int r; assert(link); assert(qdisc); assert(req); - htb = HTB(qdisc); + assert_se(htb = HTB(qdisc)); - opt.rate2quantum = htb->rate_to_quantum; - opt.defcls = htb->default_class; + struct tc_htb_glob opt = { + .version = 3, + .rate2quantum = htb->rate_to_quantum, + .defcls = htb->default_class, + }; r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; r = sd_netlink_message_append_data(req, TCA_HTB_INIT, &opt, sizeof(opt)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_HTB_INIT attribute: %m"); + return r; r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } @@ -171,7 +172,6 @@ const QDiscVTable htb_vtable = { static int hierarchy_token_bucket_class_fill_message(Link *link, TClass *tclass, sd_netlink_message *req) { HierarchyTokenBucketClass *htb; - struct tc_htb_opt opt = {}; uint32_t rtab[256], ctab[256]; int r; @@ -179,62 +179,65 @@ static int hierarchy_token_bucket_class_fill_message(Link *link, TClass *tclass, assert(tclass); assert(req); - htb = TCLASS_TO_HTB(tclass); + assert_se(htb = TCLASS_TO_HTB(tclass)); - opt.prio = htb->priority; - opt.quantum = htb->quantum; - opt.rate.rate = (htb->rate >= (1ULL << 32)) ? ~0U : htb->rate; - opt.ceil.rate = (htb->ceil_rate >= (1ULL << 32)) ? ~0U : htb->ceil_rate; - opt.rate.overhead = htb->overhead; - opt.ceil.overhead = htb->overhead; + struct tc_htb_opt opt = { + .prio = htb->priority, + .quantum = htb->quantum, + .rate.rate = (htb->rate >= (1ULL << 32)) ? ~0U : htb->rate, + .ceil.rate = (htb->ceil_rate >= (1ULL << 32)) ? ~0U : htb->ceil_rate, + .rate.overhead = htb->overhead, + .ceil.overhead = htb->overhead, + }; r = tc_transmit_time(htb->rate, htb->buffer, &opt.buffer); if (r < 0) - return log_link_error_errno(link, r, "Failed to calculate buffer size: %m"); + return log_link_debug_errno(link, r, "Failed to calculate buffer size: %m"); r = tc_transmit_time(htb->ceil_rate, htb->ceil_buffer, &opt.cbuffer); if (r < 0) - return log_link_error_errno(link, r, "Failed to calculate ceil buffer size: %m"); + return log_link_debug_errno(link, r, "Failed to calculate ceil buffer size: %m"); r = tc_fill_ratespec_and_table(&opt.rate, rtab, htb->mtu); if (r < 0) - return log_link_error_errno(link, r, "Failed to calculate rate table: %m"); + return log_link_debug_errno(link, r, "Failed to calculate rate table: %m"); r = tc_fill_ratespec_and_table(&opt.ceil, ctab, htb->mtu); if (r < 0) - return log_link_error_errno(link, r, "Failed to calculate ceil rate table: %m"); + return log_link_debug_errno(link, r, "Failed to calculate ceil rate table: %m"); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "htb"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; r = sd_netlink_message_append_data(req, TCA_HTB_PARMS, &opt, sizeof(opt)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_HTB_PARMS attribute: %m"); + return r; if (htb->rate >= (1ULL << 32)) { r = sd_netlink_message_append_u64(req, TCA_HTB_RATE64, htb->rate); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_HTB_RATE64 attribute: %m"); + return r; } if (htb->ceil_rate >= (1ULL << 32)) { r = sd_netlink_message_append_u64(req, TCA_HTB_CEIL64, htb->ceil_rate); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_HTB_CEIL64 attribute: %m"); + return r; } r = sd_netlink_message_append_data(req, TCA_HTB_RTAB, rtab, sizeof(rtab)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_HTB_RTAB attribute: %m"); + return r; r = sd_netlink_message_append_data(req, TCA_HTB_CTAB, ctab, sizeof(ctab)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_HTB_CTAB attribute: %m"); + return r; r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; + return 0; } diff --git a/src/network/tc/netem.c b/src/network/tc/netem.c index 2d86d5312..d4c452675 100644 --- a/src/network/tc/netem.c +++ b/src/network/tc/netem.c @@ -14,9 +14,6 @@ #include "tc-util.h" static int network_emulator_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { - struct tc_netem_qopt opt = { - .limit = 1000, - }; NetworkEmulator *ne; int r; @@ -24,16 +21,13 @@ static int network_emulator_fill_message(Link *link, QDisc *qdisc, sd_netlink_me assert(qdisc); assert(req); - ne = NETEM(qdisc); + assert_se(ne = NETEM(qdisc)); - if (ne->limit > 0) - opt.limit = ne->limit; - - if (ne->loss > 0) - opt.loss = ne->loss; - - if (ne->duplicate > 0) - opt.duplicate = ne->duplicate; + struct tc_netem_qopt opt = { + .limit = ne->limit > 0 ? ne->limit : 1000, + .loss = ne->loss, + .duplicate = ne->duplicate, + }; if (ne->delay != USEC_INFINITY) { r = tc_time_to_tick(ne->delay, &opt.latency); @@ -47,9 +41,9 @@ static int network_emulator_fill_message(Link *link, QDisc *qdisc, sd_netlink_me return log_link_error_errno(link, r, "Failed to calculate jitter in TCA_OPTION: %m"); } - r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_netem_qopt)); + r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(opt)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_OPTION attribute: %m"); + return r; return 0; } diff --git a/src/network/tc/pie.c b/src/network/tc/pie.c index 4fcfe625b..1b1a457f4 100644 --- a/src/network/tc/pie.c +++ b/src/network/tc/pie.c @@ -18,21 +18,21 @@ static int pie_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { assert(qdisc); assert(req); - pie = PIE(qdisc); + assert_se(pie = PIE(qdisc)); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "pie"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; if (pie->packet_limit > 0) { r = sd_netlink_message_append_u32(req, TCA_PIE_LIMIT, pie->packet_limit); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_PIE_PLIMIT attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/qdisc.c b/src/network/tc/qdisc.c index 2e0f0a12e..6aef268d1 100644 --- a/src/network/tc/qdisc.c +++ b/src/network/tc/qdisc.c @@ -7,7 +7,10 @@ #include "conf-parser.h" #include "in-addr-util.h" #include "netlink-util.h" +#include "networkd-link.h" #include "networkd-manager.h" +#include "networkd-network.h" +#include "networkd-queue.h" #include "parse-util.h" #include "qdisc.h" #include "set.h" @@ -49,18 +52,15 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) { return -ENOMEM; *qdisc = (QDisc) { - .meta.kind = TC_KIND_QDISC, - .family = AF_UNSPEC, .parent = TC_H_ROOT, .kind = kind, }; } else { + assert(kind >= 0 && kind < _QDISC_KIND_MAX); qdisc = malloc0(qdisc_vtable[kind]->object_size); if (!qdisc) return -ENOMEM; - qdisc->meta.kind = TC_KIND_QDISC, - qdisc->family = AF_UNSPEC; qdisc->parent = TC_H_ROOT; qdisc->kind = kind; @@ -77,10 +77,9 @@ static int qdisc_new(QDiscKind kind, QDisc **ret) { } int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(qdisc_freep) QDisc *qdisc = NULL; - TrafficControl *existing; - QDisc *q = NULL; + QDisc *existing; int r; assert(network); @@ -88,24 +87,19 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; - existing = ordered_hashmap_get(network->tc_by_section, n); + existing = hashmap_get(network->qdiscs_by_section, n); if (existing) { - if (existing->kind != TC_KIND_QDISC) - return -EINVAL; - - q = TC_TO_QDISC(existing); - - if (q->kind != _QDISC_KIND_INVALID && + if (existing->kind != _QDISC_KIND_INVALID && kind != _QDISC_KIND_INVALID && - q->kind != kind) + existing->kind != kind) return -EINVAL; - if (q->kind == kind || kind == _QDISC_KIND_INVALID) { - *ret = q; + if (existing->kind == kind || kind == _QDISC_KIND_INVALID) { + *ret = existing; return 0; } } @@ -114,19 +108,19 @@ int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, uns if (r < 0) return r; - if (q) { - qdisc->family = q->family; - qdisc->handle = q->handle; - qdisc->parent = q->parent; - qdisc->tca_kind = TAKE_PTR(q->tca_kind); + if (existing) { + qdisc->handle = existing->handle; + qdisc->parent = existing->parent; + qdisc->tca_kind = TAKE_PTR(existing->tca_kind); - qdisc_free(q); + qdisc_free(existing); } qdisc->network = network; qdisc->section = TAKE_PTR(n); + qdisc->source = NETWORK_CONFIG_SOURCE_STATIC; - r = ordered_hashmap_ensure_put(&network->tc_by_section, &network_config_hash_ops, qdisc->section, TC(qdisc)); + r = hashmap_ensure_put(&network->qdiscs_by_section, &config_section_hash_ops, qdisc->section, qdisc); if (r < 0) return r; @@ -139,23 +133,171 @@ QDisc* qdisc_free(QDisc *qdisc) { return NULL; if (qdisc->network && qdisc->section) - ordered_hashmap_remove(qdisc->network->tc_by_section, qdisc->section); + hashmap_remove(qdisc->network->qdiscs_by_section, qdisc->section); - network_config_section_free(qdisc->section); + config_section_free(qdisc->section); + + if (qdisc->link) + set_remove(qdisc->link->qdiscs, qdisc); free(qdisc->tca_kind); return mfree(qdisc); } -static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static const char *qdisc_get_tca_kind(const QDisc *qdisc) { + assert(qdisc); + + return (QDISC_VTABLE(qdisc) && QDISC_VTABLE(qdisc)->tca_kind) ? + QDISC_VTABLE(qdisc)->tca_kind : qdisc->tca_kind; +} + +static void qdisc_hash_func(const QDisc *qdisc, struct siphash *state) { + assert(qdisc); + assert(state); + + siphash24_compress(&qdisc->handle, sizeof(qdisc->handle), state); + siphash24_compress(&qdisc->parent, sizeof(qdisc->parent), state); + siphash24_compress_string(qdisc_get_tca_kind(qdisc), state); +} + +static int qdisc_compare_func(const QDisc *a, const QDisc *b) { + int r; + + assert(a); + assert(b); + + r = CMP(a->handle, b->handle); + if (r != 0) + return r; + + r = CMP(a->parent, b->parent); + if (r != 0) + return r; + + return strcmp_ptr(qdisc_get_tca_kind(a), qdisc_get_tca_kind(b)); +} + +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + qdisc_hash_ops, + QDisc, + qdisc_hash_func, + qdisc_compare_func, + qdisc_free); + +static int qdisc_get(Link *link, const QDisc *in, QDisc **ret) { + QDisc *existing; + + assert(link); + assert(in); + + existing = set_get(link->qdiscs, in); + if (!existing) + return -ENOENT; + + if (ret) + *ret = existing; + return 0; +} + +static int qdisc_add(Link *link, QDisc *qdisc) { int r; assert(link); - assert(link->tc_messages > 0); - link->tc_messages--; + assert(qdisc); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; + r = set_ensure_put(&link->qdiscs, &qdisc_hash_ops, qdisc); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; + + qdisc->link = link; + return 0; +} + +static int qdisc_dup(const QDisc *src, QDisc **ret) { + _cleanup_(qdisc_freep) QDisc *dst = NULL; + + assert(src); + assert(ret); + + if (QDISC_VTABLE(src)) + dst = memdup(src, QDISC_VTABLE(src)->object_size); + else + dst = newdup(QDisc, src, 1); + if (!dst) + return -ENOMEM; + + /* clear all pointers */ + dst->network = NULL; + dst->section = NULL; + dst->link = NULL; + dst->tca_kind = NULL; + + if (src->tca_kind) { + dst->tca_kind = strdup(src->tca_kind); + if (!dst->tca_kind) + return -ENOMEM; + } + + *ret = TAKE_PTR(dst); + return 0; +} + +static void log_qdisc_debug(QDisc *qdisc, Link *link, const char *str) { + _cleanup_free_ char *state = NULL; + + assert(qdisc); + assert(str); + + if (!DEBUG_LOGGING) + return; + + (void) network_config_state_to_string_alloc(qdisc->state, &state); + + log_link_debug(link, "%s %s QDisc (%s): handle=%"PRIx32":%"PRIx32", parent=%"PRIx32":%"PRIx32", kind=%s", + str, strna(network_config_source_to_string(qdisc->source)), strna(state), + TC_H_MAJ(qdisc->handle) >> 16, TC_H_MIN(qdisc->handle), + TC_H_MAJ(qdisc->parent) >> 16, TC_H_MIN(qdisc->parent), + strna(qdisc_get_tca_kind(qdisc))); +} + +int link_find_qdisc(Link *link, uint32_t handle, uint32_t parent, const char *kind, QDisc **ret) { + QDisc *qdisc; + + assert(link); + + handle = TC_H_MAJ(handle); + + SET_FOREACH(qdisc, link->qdiscs) { + if (qdisc->handle != handle) + continue; + + if (qdisc->parent != parent) + continue; + + if (qdisc->source == NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + + if (!qdisc_exists(qdisc)) + continue; + + if (kind && !streq_ptr(kind, qdisc_get_tca_kind(qdisc))) + continue; + + if (ret) + *ret = qdisc; + return 0; + } + + return -ENOENT; +} + +static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, QDisc *qdisc) { + int r; + + assert(m); + assert(link); r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { @@ -173,62 +315,216 @@ static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { return 1; } -int qdisc_configure(Link *link, QDisc *qdisc) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int qdisc_configure(QDisc *qdisc, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; + assert(qdisc); assert(link); assert(link->manager); assert(link->manager->rtnl); assert(link->ifindex > 0); + assert(req); - r = sd_rtnl_message_new_qdisc(link->manager->rtnl, &req, RTM_NEWQDISC, qdisc->family, link->ifindex); + log_qdisc_debug(qdisc, link, "Configuring"); + + r = sd_rtnl_message_new_traffic_control(link->manager->rtnl, &m, RTM_NEWQDISC, + link->ifindex, qdisc->handle, qdisc->parent); if (r < 0) - return log_link_error_errno(link, r, "Could not create RTM_NEWQDISC message: %m"); + return r; - r = sd_rtnl_message_set_qdisc_parent(req, qdisc->parent); + r = sd_netlink_message_append_string(m, TCA_KIND, qdisc_get_tca_kind(qdisc)); if (r < 0) - return log_link_error_errno(link, r, "Could not create tcm_parent message: %m"); + return r; - if (qdisc->handle != TC_H_UNSPEC) { - r = sd_rtnl_message_set_qdisc_handle(req, qdisc->handle); + if (QDISC_VTABLE(qdisc) && QDISC_VTABLE(qdisc)->fill_message) { + r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, m); if (r < 0) - return log_link_error_errno(link, r, "Could not set tcm_handle message: %m"); + return r; } - if (QDISC_VTABLE(qdisc)) { - if (QDISC_VTABLE(qdisc)->fill_tca_kind) { - r = QDISC_VTABLE(qdisc)->fill_tca_kind(link, qdisc, req); - if (r < 0) - return r; - } else { - r = sd_netlink_message_append_string(req, TCA_KIND, QDISC_VTABLE(qdisc)->tca_kind); - if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m"); - } - - if (QDISC_VTABLE(qdisc)->fill_message) { - r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, req); - if (r < 0) - return r; - } - } else { - r = sd_netlink_message_append_string(req, TCA_KIND, qdisc->tca_kind); - if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m"); - } - - r = netlink_call_async(link->manager->rtnl, NULL, req, qdisc_handler, link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - link->tc_messages++; - - return 0; + return request_call_netlink_async(link->manager->rtnl, m, req); } -int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) { +static bool qdisc_is_ready_to_configure(QDisc *qdisc, Link *link) { + assert(qdisc); + assert(link); + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return false; + + if (IN_SET(qdisc->parent, TC_H_ROOT, TC_H_CLSACT)) /* TC_H_CLSACT == TC_H_INGRESS */ + return true; + + return link_find_tclass(link, qdisc->parent, NULL) >= 0; +} + +static int qdisc_process_request(Request *req, Link *link, QDisc *qdisc) { + int r; + + assert(req); + assert(link); + assert(qdisc); + + if (!qdisc_is_ready_to_configure(qdisc, link)) + return 0; + + r = qdisc_configure(qdisc, link, req); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure QDisc: %m"); + + qdisc_enter_configuring(qdisc); + return 1; +} + +int link_request_qdisc(Link *link, QDisc *qdisc) { + QDisc *existing; + int r; + + assert(link); + assert(qdisc); + + if (qdisc_get(link, qdisc, &existing) < 0) { + _cleanup_(qdisc_freep) QDisc *tmp = NULL; + + r = qdisc_dup(qdisc, &tmp); + if (r < 0) + return log_oom(); + + r = qdisc_add(link, tmp); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to store QDisc: %m"); + + existing = TAKE_PTR(tmp); + } else + existing->source = qdisc->source; + + log_qdisc_debug(existing, link, "Requesting"); + r = link_queue_request_safe(link, REQUEST_TYPE_TC_QDISC, + existing, NULL, + qdisc_hash_func, + qdisc_compare_func, + qdisc_process_request, + &link->tc_messages, + qdisc_handler, + NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request QDisc: %m"); + if (r == 0) + return 0; + + qdisc_enter_requesting(existing); + return 1; +} + +int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { + _cleanup_(qdisc_freep) QDisc *tmp = NULL; + QDisc *qdisc = NULL; + Link *link; + uint16_t type; + int ifindex, r; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "rtnl: failed to receive QDisc message, ignoring"); + + return 0; + } + + r = sd_netlink_message_get_type(message, &type); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); + return 0; + } else if (!IN_SET(type, RTM_NEWQDISC, RTM_DELQDISC)) { + log_warning("rtnl: received unexpected message type %u when processing QDisc, ignoring.", type); + return 0; + } + + r = sd_rtnl_message_traffic_control_get_ifindex(message, &ifindex); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received QDisc message with invalid ifindex %d, ignoring.", ifindex); + return 0; + } + + if (link_get_by_index(m, ifindex, &link) < 0) { + if (!m->enumerating) + log_warning("rtnl: received QDisc for link '%d' we don't know about, ignoring.", ifindex); + return 0; + } + + r = qdisc_new(_QDISC_KIND_INVALID, &tmp); + if (r < 0) + return log_oom(); + + r = sd_rtnl_message_traffic_control_get_handle(message, &tmp->handle); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received QDisc message without handle, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_traffic_control_get_parent(message, &tmp->parent); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received QDisc message without parent, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_string_strdup(message, TCA_KIND, &tmp->tca_kind); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received QDisc message without kind, ignoring: %m"); + return 0; + } + + (void) qdisc_get(link, tmp, &qdisc); + + switch (type) { + case RTM_NEWQDISC: + if (qdisc) { + qdisc_enter_configured(qdisc); + log_qdisc_debug(qdisc, link, "Received remembered"); + } else { + qdisc_enter_configured(tmp); + log_qdisc_debug(tmp, link, "Received new"); + + r = qdisc_add(link, tmp); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to remember QDisc, ignoring: %m"); + return 0; + } + + qdisc = TAKE_PTR(tmp); + } + + break; + + case RTM_DELQDISC: + if (qdisc) { + qdisc_enter_removed(qdisc); + if (qdisc->state == 0) { + log_qdisc_debug(qdisc, link, "Forgetting"); + qdisc_free(qdisc); + } else + log_qdisc_debug(qdisc, link, "Removed"); + } else + log_qdisc_debug(tmp, link, "Kernel removed unknown"); + + break; + + default: + assert_not_reached(); + } + + return 1; +} + +static int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) { int r; assert(qdisc); @@ -263,6 +559,17 @@ int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) { return 0; } +void network_drop_invalid_qdisc(Network *network) { + bool has_root = false, has_clsact = false; + QDisc *qdisc; + + assert(network); + + HASHMAP_FOREACH(qdisc, network->qdiscs_by_section) + if (qdisc_section_verify(qdisc, &has_root, &has_clsact) < 0) + qdisc_free(qdisc); +} + int config_parse_qdisc_parent( const char *unit, const char *filename, @@ -293,11 +600,9 @@ int config_parse_qdisc_parent( return 0; } - if (streq(rvalue, "root")) { + if (streq(rvalue, "root")) qdisc->parent = TC_H_ROOT; - if (qdisc->handle == 0) - qdisc->handle = TC_H_UNSPEC; - } else if (streq(rvalue, "clsact")) { + else if (streq(rvalue, "clsact")) { qdisc->parent = TC_H_CLSACT; qdisc->handle = TC_H_MAKE(TC_H_CLSACT, 0); } else if (streq(rvalue, "ingress")) { diff --git a/src/network/tc/qdisc.h b/src/network/tc/qdisc.h index bf2df146a..adaaf260c 100644 --- a/src/network/tc/qdisc.h +++ b/src/network/tc/qdisc.h @@ -3,10 +3,11 @@ #pragma once #include "conf-parser.h" -#include "networkd-link.h" -#include "networkd-network.h" #include "networkd-util.h" -#include "tc.h" + +typedef struct Link Link; +typedef struct Manager Manager; +typedef struct Network Network; typedef enum QDiscKind { QDISC_KIND_BFIFO, @@ -35,12 +36,12 @@ typedef enum QDiscKind { } QDiscKind; typedef struct QDisc { - TrafficControl meta; - - NetworkConfigSection *section; + Link *link; Network *network; + ConfigSection *section; + NetworkConfigSource source; + NetworkConfigState state; - int family; uint32_t handle; uint32_t parent; @@ -53,7 +54,6 @@ typedef struct QDiscVTable { const char *tca_kind; /* called in qdisc_new() */ int (*init)(QDisc *qdisc); - int (*fill_tca_kind)(Link *link, QDisc *qdisc, sd_netlink_message *m); int (*fill_message)(Link *link, QDisc *qdisc, sd_netlink_message *m); int (*verify)(QDisc *qdisc); } QDiscVTable; @@ -71,18 +71,20 @@ extern const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX]; return (MixedCase*) q; \ } -/* For casting the various qdisc kinds into a qdisc */ -#define QDISC(q) (&(q)->meta) +DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(QDisc, qdisc); QDisc* qdisc_free(QDisc *qdisc); int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret); -int qdisc_configure(Link *link, QDisc *qdisc); -int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact); +int link_find_qdisc(Link *link, uint32_t handle, uint32_t parent, const char *kind, QDisc **qdisc); -DEFINE_NETWORK_SECTION_FUNCTIONS(QDisc, qdisc_free); +int link_request_qdisc(Link *link, QDisc *qdisc); -DEFINE_TC_CAST(QDISC, QDisc); +void network_drop_invalid_qdisc(Network *network); + +int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); + +DEFINE_SECTION_CLEANUP_FUNCTIONS(QDisc, qdisc_free); CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_parent); CONFIG_PARSER_PROTOTYPE(config_parse_qdisc_handle); diff --git a/src/network/tc/qfq.c b/src/network/tc/qfq.c index d2e7b875e..51aef0321 100644 --- a/src/network/tc/qfq.c +++ b/src/network/tc/qfq.c @@ -25,27 +25,28 @@ static int quick_fair_queueing_class_fill_message(Link *link, TClass *tclass, sd assert(tclass); assert(req); - qfq = TCLASS_TO_QFQ(tclass); + assert_se(qfq = TCLASS_TO_QFQ(tclass)); r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "qfq"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; if (qfq->weight > 0) { r = sd_netlink_message_append_u32(req, TCA_QFQ_WEIGHT, qfq->weight); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_QFQ_WEIGHT attribute: %m"); + return r; } if (qfq->max_packet > 0) { r = sd_netlink_message_append_u32(req, TCA_QFQ_LMAX, qfq->max_packet); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_QFQ_LMAX attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; + return 0; } diff --git a/src/network/tc/sfb.c b/src/network/tc/sfb.c index a4ca4884b..88b3ce55b 100644 --- a/src/network/tc/sfb.c +++ b/src/network/tc/sfb.c @@ -13,7 +13,15 @@ static int stochastic_fair_blue_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { StochasticFairBlue *sfb; - struct tc_sfb_qopt opt = { + int r; + + assert(link); + assert(qdisc); + assert(req); + + assert_se(sfb = SFB(qdisc)); + + const struct tc_sfb_qopt opt = { .rehash_interval = 600*1000, .warmup_time = 60*1000, .penalty_rate = 10, @@ -22,28 +30,20 @@ static int stochastic_fair_blue_fill_message(Link *link, QDisc *qdisc, sd_netlin .decrement = (SFB_MAX_PROB + 10000) / 20000, .max = 25, .bin_size = 20, + .limit = sfb->packet_limit, }; - int r; - - assert(link); - assert(qdisc); - assert(req); - - sfb = SFB(qdisc); - - opt.limit = sfb->packet_limit; r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "sfb"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; - r = sd_netlink_message_append_data(req, TCA_SFB_PARMS, &opt, sizeof(struct tc_sfb_qopt)); + r = sd_netlink_message_append_data(req, TCA_SFB_PARMS, &opt, sizeof(opt)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_SFB_PARMS attribute: %m"); + return r; r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/sfq.c b/src/network/tc/sfq.c index f67ffc186..45cc54d39 100644 --- a/src/network/tc/sfq.c +++ b/src/network/tc/sfq.c @@ -13,20 +13,21 @@ static int stochastic_fairness_queueing_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { StochasticFairnessQueueing *sfq; - struct tc_sfq_qopt_v1 opt = {}; int r; assert(link); assert(qdisc); assert(req); - sfq = SFQ(qdisc); + assert_se(sfq = SFQ(qdisc)); - opt.v0.perturb_period = sfq->perturb_period / USEC_PER_SEC; + const struct tc_sfq_qopt_v1 opt = { + .v0.perturb_period = sfq->perturb_period / USEC_PER_SEC, + }; - r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(struct tc_sfq_qopt_v1)); + r = sd_netlink_message_append_data(req, TCA_OPTIONS, &opt, sizeof(opt)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_OPTIONS attribute: %m"); + return r; return 0; } diff --git a/src/network/tc/tbf.c b/src/network/tc/tbf.c index 1d1bc6f0e..2aca8c1f1 100644 --- a/src/network/tc/tbf.c +++ b/src/network/tc/tbf.c @@ -17,7 +17,6 @@ static int token_bucket_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink_message *req) { uint32_t rtab[256], ptab[256]; - struct tc_tbf_qopt opt = {}; TokenBucketFilter *tbf; int r; @@ -25,10 +24,13 @@ static int token_bucket_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink assert(qdisc); assert(req); - tbf = TBF(qdisc); + assert_se(tbf = TBF(qdisc)); - opt.rate.rate = tbf->rate >= (1ULL << 32) ? ~0U : tbf->rate; - opt.peakrate.rate = tbf->peak_rate >= (1ULL << 32) ? ~0U : tbf->peak_rate; + struct tc_tbf_qopt opt = { + .rate.rate = tbf->rate >= (1ULL << 32) ? ~0U : tbf->rate, + .peakrate.rate = tbf->peak_rate >= (1ULL << 32) ? ~0U : tbf->peak_rate, + .rate.mpu = tbf->mpu, + }; if (tbf->limit > 0) opt.limit = tbf->limit; @@ -43,69 +45,67 @@ static int token_bucket_filter_fill_message(Link *link, QDisc *qdisc, sd_netlink opt.limit = lim; } - opt.rate.mpu = tbf->mpu; - r = tc_fill_ratespec_and_table(&opt.rate, rtab, tbf->mtu); if (r < 0) - return log_link_error_errno(link, r, "Failed to calculate ratespec: %m"); + return log_link_debug_errno(link, r, "Failed to calculate ratespec: %m"); r = tc_transmit_time(opt.rate.rate, tbf->burst, &opt.buffer); if (r < 0) - return log_link_error_errno(link, r, "Failed to calculate buffer size: %m"); + return log_link_debug_errno(link, r, "Failed to calculate buffer size: %m"); if (opt.peakrate.rate > 0) { opt.peakrate.mpu = tbf->mpu; r = tc_fill_ratespec_and_table(&opt.peakrate, ptab, tbf->mtu); if (r < 0) - return log_link_error_errno(link, r, "Failed to calculate ratespec: %m"); + return log_link_debug_errno(link, r, "Failed to calculate ratespec: %m"); r = tc_transmit_time(opt.peakrate.rate, tbf->mtu, &opt.mtu); if (r < 0) - return log_link_error_errno(link, r, "Failed to calculate mtu size: %m"); + return log_link_debug_errno(link, r, "Failed to calculate mtu size: %m"); } r = sd_netlink_message_open_container_union(req, TCA_OPTIONS, "tbf"); if (r < 0) - return log_link_error_errno(link, r, "Could not open container TCA_OPTIONS: %m"); + return r; - r = sd_netlink_message_append_data(req, TCA_TBF_PARMS, &opt, sizeof(struct tc_tbf_qopt)); + r = sd_netlink_message_append_data(req, TCA_TBF_PARMS, &opt, sizeof(opt)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_TBF_PARMS attribute: %m"); + return r; r = sd_netlink_message_append_data(req, TCA_TBF_BURST, &tbf->burst, sizeof(tbf->burst)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_TBF_BURST attribute: %m"); + return r; if (tbf->rate >= (1ULL << 32)) { r = sd_netlink_message_append_u64(req, TCA_TBF_RATE64, tbf->rate); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_TBF_RATE64 attribute: %m"); + return r; } r = sd_netlink_message_append_data(req, TCA_TBF_RTAB, rtab, sizeof(rtab)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_TBF_RTAB attribute: %m"); + return r; if (opt.peakrate.rate > 0) { if (tbf->peak_rate >= (1ULL << 32)) { r = sd_netlink_message_append_u64(req, TCA_TBF_PRATE64, tbf->peak_rate); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_TBF_PRATE64 attribute: %m"); + return r; } r = sd_netlink_message_append_u32(req, TCA_TBF_PBURST, tbf->mtu); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_TBF_PBURST attribute: %m"); + return r; r = sd_netlink_message_append_data(req, TCA_TBF_PTAB, ptab, sizeof(ptab)); if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_TBF_PTAB attribute: %m"); + return r; } r = sd_netlink_message_close_container(req); if (r < 0) - return log_link_error_errno(link, r, "Could not close container TCA_OPTIONS: %m"); + return r; return 0; } diff --git a/src/network/tc/tc.c b/src/network/tc/tc.c index a3cfed53f..8a1c5b3a3 100644 --- a/src/network/tc/tc.c +++ b/src/network/tc/tc.c @@ -1,88 +1,41 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "macro.h" +#include "networkd-link.h" +#include "networkd-network.h" #include "qdisc.h" #include "tc.h" #include "tclass.h" -void traffic_control_free(TrafficControl *tc) { - if (!tc) - return; - - switch (tc->kind) { - case TC_KIND_QDISC: - qdisc_free(TC_TO_QDISC(tc)); - break; - case TC_KIND_TCLASS: - tclass_free(TC_TO_TCLASS(tc)); - break; - default: - assert_not_reached(); - } -} - -static int traffic_control_configure(Link *link, TrafficControl *tc) { - assert(link); - assert(tc); - - switch(tc->kind) { - case TC_KIND_QDISC: - return qdisc_configure(link, TC_TO_QDISC(tc)); - case TC_KIND_TCLASS: - return tclass_configure(link, TC_TO_TCLASS(tc)); - default: - assert_not_reached(); - } -} - -int link_configure_traffic_control(Link *link) { - TrafficControl *tc; +int link_request_traffic_control(Link *link) { + TClass *tclass; + QDisc *qdisc; int r; assert(link); assert(link->network); - if (link->tc_messages != 0) { - log_link_debug(link, "Traffic control is configuring."); - return 0; - } - link->tc_configured = false; - ORDERED_HASHMAP_FOREACH(tc, link->network->tc_by_section) { - r = traffic_control_configure(link, tc); + HASHMAP_FOREACH(qdisc, link->network->qdiscs_by_section) { + r = link_request_qdisc(link, qdisc); if (r < 0) return r; } - if (link->tc_messages == 0) + HASHMAP_FOREACH(tclass, link->network->tclasses_by_section) { + r = link_request_tclass(link, tclass); + if (r < 0) + return r; + } + + if (link->tc_messages == 0) { link->tc_configured = true; - else - log_link_debug(link, "Configuring traffic control"); + link_check_ready(link); + } else { + log_link_debug(link, "Setting traffic control"); + link_set_state(link, LINK_STATE_CONFIGURING); + } return 0; } - -static int traffic_control_section_verify(TrafficControl *tc, bool *qdisc_has_root, bool *qdisc_has_clsact) { - assert(tc); - - switch(tc->kind) { - case TC_KIND_QDISC: - return qdisc_section_verify(TC_TO_QDISC(tc), qdisc_has_root, qdisc_has_clsact); - case TC_KIND_TCLASS: - return tclass_section_verify(TC_TO_TCLASS(tc)); - default: - assert_not_reached(); - } -} - -void network_drop_invalid_traffic_control(Network *network) { - bool has_root = false, has_clsact = false; - TrafficControl *tc; - - assert(network); - - ORDERED_HASHMAP_FOREACH(tc, network->tc_by_section) - if (traffic_control_section_verify(tc, &has_root, &has_clsact) < 0) - traffic_control_free(tc); -} diff --git a/src/network/tc/tc.h b/src/network/tc/tc.h index badd5227c..6226578ec 100644 --- a/src/network/tc/tc.h +++ b/src/network/tc/tc.h @@ -1,32 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -#include "networkd-link.h" +typedef struct Link Link; -typedef enum TrafficControlKind { - TC_KIND_QDISC, - TC_KIND_TCLASS, - TC_KIND_FILTER, - _TC_KIND_MAX, - _TC_KIND_INVALID = -EINVAL, -} TrafficControlKind; - -typedef struct TrafficControl { - TrafficControlKind kind; -} TrafficControl; - -/* For casting a tc into the various tc kinds */ -#define DEFINE_TC_CAST(UPPERCASE, MixedCase) \ - static inline MixedCase* TC_TO_##UPPERCASE(TrafficControl *tc) { \ - if (_unlikely_(!tc || tc->kind != TC_KIND_##UPPERCASE)) \ - return NULL; \ - \ - return (MixedCase*) tc; \ - } - -/* For casting the various tc kinds into a tc */ -#define TC(tc) (&(tc)->meta) - -void traffic_control_free(TrafficControl *tc); -int link_configure_traffic_control(Link *link); -void network_drop_invalid_traffic_control(Network *network); +int link_request_traffic_control(Link *link); diff --git a/src/network/tc/tclass.c b/src/network/tc/tclass.c index d8145997f..d3b3c19bd 100644 --- a/src/network/tc/tclass.c +++ b/src/network/tc/tclass.c @@ -7,7 +7,10 @@ #include "conf-parser.h" #include "in-addr-util.h" #include "netlink-util.h" +#include "networkd-link.h" #include "networkd-manager.h" +#include "networkd-network.h" +#include "networkd-queue.h" #include "parse-util.h" #include "set.h" #include "string-util.h" @@ -25,18 +28,29 @@ static int tclass_new(TClassKind kind, TClass **ret) { _cleanup_(tclass_freep) TClass *tclass = NULL; int r; - tclass = malloc0(tclass_vtable[kind]->object_size); - if (!tclass) - return -ENOMEM; + if (kind == _TCLASS_KIND_INVALID) { + tclass = new(TClass, 1); + if (!tclass) + return -ENOMEM; - tclass->meta.kind = TC_KIND_TCLASS, - tclass->parent = TC_H_ROOT; - tclass->kind = kind; + *tclass = (TClass) { + .parent = TC_H_ROOT, + .kind = kind, + }; + } else { + assert(kind >= 0 && kind < _TCLASS_KIND_MAX); + tclass = malloc0(tclass_vtable[kind]->object_size); + if (!tclass) + return -ENOMEM; - if (TCLASS_VTABLE(tclass)->init) { - r = TCLASS_VTABLE(tclass)->init(tclass); - if (r < 0) - return r; + tclass->parent = TC_H_ROOT; + tclass->kind = kind; + + if (TCLASS_VTABLE(tclass)->init) { + r = TCLASS_VTABLE(tclass)->init(tclass); + if (r < 0) + return r; + } } *ret = TAKE_PTR(tclass); @@ -45,9 +59,9 @@ static int tclass_new(TClassKind kind, TClass **ret) { } int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret) { - _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(config_section_freep) ConfigSection *n = NULL; _cleanup_(tclass_freep) TClass *tclass = NULL; - TrafficControl *existing; + TClass *existing; int r; assert(network); @@ -55,23 +69,16 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u assert(filename); assert(section_line > 0); - r = network_config_section_new(filename, section_line, &n); + r = config_section_new(filename, section_line, &n); if (r < 0) return r; - existing = ordered_hashmap_get(network->tc_by_section, n); + existing = hashmap_get(network->tclasses_by_section, n); if (existing) { - TClass *t; - - if (existing->kind != TC_KIND_TCLASS) + if (existing->kind != kind) return -EINVAL; - t = TC_TO_TCLASS(existing); - - if (t->kind != kind) - return -EINVAL; - - *ret = t; + *ret = existing; return 0; } @@ -81,8 +88,9 @@ int tclass_new_static(TClassKind kind, Network *network, const char *filename, u tclass->network = network; tclass->section = TAKE_PTR(n); + tclass->source = NETWORK_CONFIG_SOURCE_STATIC; - r = ordered_hashmap_ensure_put(&network->tc_by_section, &network_config_hash_ops, tclass->section, tclass); + r = hashmap_ensure_put(&network->tclasses_by_section, &config_section_hash_ops, tclass->section, tclass); if (r < 0) return r; @@ -95,22 +103,163 @@ TClass* tclass_free(TClass *tclass) { return NULL; if (tclass->network && tclass->section) - ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section); + hashmap_remove(tclass->network->tclasses_by_section, tclass->section); - network_config_section_free(tclass->section); + config_section_free(tclass->section); + if (tclass->link) + set_remove(tclass->link->tclasses, tclass); + + free(tclass->tca_kind); return mfree(tclass); } -static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { +static const char *tclass_get_tca_kind(const TClass *tclass) { + assert(tclass); + + return (TCLASS_VTABLE(tclass) && TCLASS_VTABLE(tclass)->tca_kind) ? + TCLASS_VTABLE(tclass)->tca_kind : tclass->tca_kind; +} + +static void tclass_hash_func(const TClass *tclass, struct siphash *state) { + assert(tclass); + assert(state); + + siphash24_compress(&tclass->classid, sizeof(tclass->classid), state); + siphash24_compress(&tclass->parent, sizeof(tclass->parent), state); + siphash24_compress_string(tclass_get_tca_kind(tclass), state); +} + +static int tclass_compare_func(const TClass *a, const TClass *b) { + int r; + + assert(a); + assert(b); + + r = CMP(a->classid, b->classid); + if (r != 0) + return r; + + r = CMP(a->parent, b->parent); + if (r != 0) + return r; + + return strcmp_ptr(tclass_get_tca_kind(a), tclass_get_tca_kind(b)); +} + +DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( + tclass_hash_ops, + TClass, + tclass_hash_func, + tclass_compare_func, + tclass_free); + +static int tclass_get(Link *link, const TClass *in, TClass **ret) { + TClass *existing; + + assert(link); + assert(in); + + existing = set_get(link->tclasses, in); + if (!existing) + return -ENOENT; + + if (ret) + *ret = existing; + return 0; +} + +static int tclass_add(Link *link, TClass *tclass) { int r; assert(link); - assert(link->tc_messages > 0); - link->tc_messages--; + assert(tclass); - if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return 1; + r = set_ensure_put(&link->tclasses, &tclass_hash_ops, tclass); + if (r < 0) + return r; + if (r == 0) + return -EEXIST; + + tclass->link = link; + return 0; +} + +static int tclass_dup(const TClass *src, TClass **ret) { + _cleanup_(tclass_freep) TClass *dst = NULL; + + assert(src); + assert(ret); + + if (TCLASS_VTABLE(src)) + dst = memdup(src, TCLASS_VTABLE(src)->object_size); + else + dst = newdup(TClass, src, 1); + if (!dst) + return -ENOMEM; + + /* clear all pointers */ + dst->network = NULL; + dst->section = NULL; + dst->link = NULL; + dst->tca_kind = NULL; + + if (src->tca_kind) { + dst->tca_kind = strdup(src->tca_kind); + if (!dst->tca_kind) + return -ENOMEM; + } + + *ret = TAKE_PTR(dst); + return 0; +} + +int link_find_tclass(Link *link, uint32_t classid, TClass **ret) { + TClass *tclass; + + assert(link); + + SET_FOREACH(tclass, link->tclasses) { + if (tclass->classid != classid) + continue; + + if (tclass->source == NETWORK_CONFIG_SOURCE_FOREIGN) + continue; + + if (!tclass_exists(tclass)) + continue; + + if (ret) + *ret = tclass; + return 0; + } + + return -ENOENT; +} + +static void log_tclass_debug(TClass *tclass, Link *link, const char *str) { + _cleanup_free_ char *state = NULL; + + assert(tclass); + assert(str); + + if (!DEBUG_LOGGING) + return; + + (void) network_config_state_to_string_alloc(tclass->state, &state); + + log_link_debug(link, "%s %s TClass (%s): classid=%"PRIx32":%"PRIx32", parent=%"PRIx32":%"PRIx32", kind=%s", + str, strna(network_config_source_to_string(tclass->source)), strna(state), + TC_H_MAJ(tclass->classid) >> 16, TC_H_MIN(tclass->classid), + TC_H_MAJ(tclass->parent) >> 16, TC_H_MIN(tclass->parent), + strna(tclass_get_tca_kind(tclass))); +} + +static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, TClass *tclass) { + int r; + + assert(m); + assert(link); r = sd_netlink_message_get_errno(m); if (r < 0 && r != -EEXIST) { @@ -128,50 +277,213 @@ static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { return 1; } -int tclass_configure(Link *link, TClass *tclass) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; +static int tclass_configure(TClass *tclass, Link *link, Request *req) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; int r; + assert(tclass); assert(link); assert(link->manager); assert(link->manager->rtnl); assert(link->ifindex > 0); + assert(req); - r = sd_rtnl_message_new_tclass(link->manager->rtnl, &req, RTM_NEWTCLASS, AF_UNSPEC, link->ifindex); + log_tclass_debug(tclass, link, "Configuring"); + + r = sd_rtnl_message_new_traffic_control(link->manager->rtnl, &m, RTM_NEWTCLASS, + link->ifindex, tclass->classid, tclass->parent); if (r < 0) - return log_link_error_errno(link, r, "Could not create RTM_NEWTCLASS message: %m"); + return r; - r = sd_rtnl_message_set_tclass_parent(req, tclass->parent); + r = sd_netlink_message_append_string(m, TCA_KIND, TCLASS_VTABLE(tclass)->tca_kind); if (r < 0) - return log_link_error_errno(link, r, "Could not create tcm_parent message: %m"); - - if (tclass->classid != TC_H_UNSPEC) { - r = sd_rtnl_message_set_tclass_handle(req, tclass->classid); - if (r < 0) - return log_link_error_errno(link, r, "Could not set tcm_handle message: %m"); - } - - r = sd_netlink_message_append_string(req, TCA_KIND, TCLASS_VTABLE(tclass)->tca_kind); - if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m"); + return r; if (TCLASS_VTABLE(tclass)->fill_message) { - r = TCLASS_VTABLE(tclass)->fill_message(link, tclass, req); + r = TCLASS_VTABLE(tclass)->fill_message(link, tclass, m); if (r < 0) return r; } - r = netlink_call_async(link->manager->rtnl, NULL, req, tclass_handler, link_netlink_destroy_callback, link); - if (r < 0) - return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); - - link_ref(link); - link->tc_messages++; - - return 0; + return request_call_netlink_async(link->manager->rtnl, m, req); } -int tclass_section_verify(TClass *tclass) { +static bool tclass_is_ready_to_configure(TClass *tclass, Link *link) { + assert(tclass); + assert(link); + + if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) + return false; + + return link_find_qdisc(link, tclass->classid, tclass->parent, tclass_get_tca_kind(tclass), NULL) >= 0; +} + +static int tclass_process_request(Request *req, Link *link, TClass *tclass) { + int r; + + assert(req); + assert(link); + assert(tclass); + + if (!tclass_is_ready_to_configure(tclass, link)) + return 0; + + r = tclass_configure(tclass, link, req); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to configure TClass: %m"); + + tclass_enter_configuring(tclass); + return 1; +} + +int link_request_tclass(Link *link, TClass *tclass) { + TClass *existing; + int r; + + assert(link); + assert(tclass); + + if (tclass_get(link, tclass, &existing) < 0) { + _cleanup_(tclass_freep) TClass *tmp = NULL; + + r = tclass_dup(tclass, &tmp); + if (r < 0) + return log_oom(); + + r = tclass_add(link, tmp); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to store TClass: %m"); + + existing = TAKE_PTR(tmp); + } else + existing->source = tclass->source; + + log_tclass_debug(existing, link, "Requesting"); + r = link_queue_request_safe(link, REQUEST_TYPE_TC_CLASS, + existing, NULL, + tclass_hash_func, + tclass_compare_func, + tclass_process_request, + &link->tc_messages, + tclass_handler, + NULL); + if (r < 0) + return log_link_warning_errno(link, r, "Failed to request TClass: %m"); + if (r == 0) + return 0; + + tclass_enter_requesting(existing); + return 1; +} + +int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { + _cleanup_(tclass_freep) TClass *tmp = NULL; + TClass *tclass = NULL; + Link *link; + uint16_t type; + int ifindex, r; + + assert(rtnl); + assert(message); + assert(m); + + if (sd_netlink_message_is_error(message)) { + r = sd_netlink_message_get_errno(message); + if (r < 0) + log_message_warning_errno(message, r, "rtnl: failed to receive TClass message, ignoring"); + + return 0; + } + + r = sd_netlink_message_get_type(message, &type); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); + return 0; + } else if (!IN_SET(type, RTM_NEWTCLASS, RTM_DELTCLASS)) { + log_warning("rtnl: received unexpected message type %u when processing TClass, ignoring.", type); + return 0; + } + + r = sd_rtnl_message_traffic_control_get_ifindex(message, &ifindex); + if (r < 0) { + log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m"); + return 0; + } else if (ifindex <= 0) { + log_warning("rtnl: received TClass message with invalid ifindex %d, ignoring.", ifindex); + return 0; + } + + if (link_get_by_index(m, ifindex, &link) < 0) { + if (!m->enumerating) + log_warning("rtnl: received TClass for link '%d' we don't know about, ignoring.", ifindex); + return 0; + } + + r = tclass_new(_TCLASS_KIND_INVALID, &tmp); + if (r < 0) + return log_oom(); + + r = sd_rtnl_message_traffic_control_get_handle(message, &tmp->classid); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received TClass message without handle, ignoring: %m"); + return 0; + } + + r = sd_rtnl_message_traffic_control_get_parent(message, &tmp->parent); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received TClass message without parent, ignoring: %m"); + return 0; + } + + r = sd_netlink_message_read_string_strdup(message, TCA_KIND, &tmp->tca_kind); + if (r < 0) { + log_link_warning_errno(link, r, "rtnl: received TClass message without kind, ignoring: %m"); + return 0; + } + + (void) tclass_get(link, tmp, &tclass); + + switch (type) { + case RTM_NEWTCLASS: + if (tclass) { + tclass_enter_configured(tclass); + log_tclass_debug(tclass, link, "Received remembered"); + } else { + tclass_enter_configured(tmp); + log_tclass_debug(tmp, link, "Received new"); + + r = tclass_add(link, tmp); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to remember TClass, ignoring: %m"); + return 0; + } + + tclass = TAKE_PTR(tmp); + } + + break; + + case RTM_DELTCLASS: + if (tclass) { + tclass_enter_removed(tclass); + if (tclass->state == 0) { + log_tclass_debug(tclass, link, "Forgetting"); + tclass_free(tclass); + } else + log_tclass_debug(tclass, link, "Removed"); + } else + log_tclass_debug(tmp, link, "Kernel removed unknown"); + + break; + + default: + assert_not_reached(); + } + + return 1; +} + +static int tclass_section_verify(TClass *tclass) { int r; assert(tclass); @@ -188,6 +500,16 @@ int tclass_section_verify(TClass *tclass) { return 0; } +void network_drop_invalid_tclass(Network *network) { + TClass *tclass; + + assert(network); + + HASHMAP_FOREACH(tclass, network->tclasses_by_section) + if (tclass_section_verify(tclass) < 0) + tclass_free(tclass); +} + int config_parse_tclass_parent( const char *unit, const char *filename, diff --git a/src/network/tc/tclass.h b/src/network/tc/tclass.h index fc91789f3..606bb3fef 100644 --- a/src/network/tc/tclass.h +++ b/src/network/tc/tclass.h @@ -3,10 +3,11 @@ #pragma once #include "conf-parser.h" -#include "networkd-link.h" -#include "networkd-network.h" #include "networkd-util.h" -#include "tc.h" + +typedef struct Link Link; +typedef struct Manager Manager; +typedef struct Network Network; typedef enum TClassKind { TCLASS_KIND_DRR, @@ -17,15 +18,17 @@ typedef enum TClassKind { } TClassKind; typedef struct TClass { - TrafficControl meta; - - NetworkConfigSection *section; + Link *link; Network *network; + ConfigSection *section; + NetworkConfigSource source; + NetworkConfigState state; uint32_t classid; uint32_t parent; TClassKind kind; + char *tca_kind; } TClass; typedef struct TClassVTable { @@ -50,18 +53,20 @@ extern const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX]; return (MixedCase*) t; \ } -/* For casting the various tclass kinds into a tclass */ -#define TCLASS(t) (&(t)->meta) +DEFINE_NETWORK_CONFIG_STATE_FUNCTIONS(TClass, tclass); TClass* tclass_free(TClass *tclass); int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret); -int tclass_configure(Link *link, TClass *tclass); -int tclass_section_verify(TClass *tclass); +int link_find_tclass(Link *link, uint32_t classid, TClass **ret); -DEFINE_NETWORK_SECTION_FUNCTIONS(TClass, tclass_free); +int link_request_tclass(Link *link, TClass *tclass); -DEFINE_TC_CAST(TCLASS, TClass); +void network_drop_invalid_tclass(Network *network); + +int manager_rtnl_process_tclass(sd_netlink *rtnl, sd_netlink_message *message, Manager *m); + +DEFINE_SECTION_CLEANUP_FUNCTIONS(TClass, tclass_free); CONFIG_PARSER_PROTOTYPE(config_parse_tclass_parent); CONFIG_PARSER_PROTOTYPE(config_parse_tclass_classid); diff --git a/src/network/tc/teql.c b/src/network/tc/teql.c index 633a7827b..1422860f5 100644 --- a/src/network/tc/teql.c +++ b/src/network/tc/teql.c @@ -1,34 +1,25 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include "macro.h" -#include "netlink-util.h" #include "parse-util.h" -#include "stdio-util.h" #include "string-util.h" #include "teql.h" -static int trivial_link_equalizer_fill_tca_kind(Link *link, QDisc *qdisc, sd_netlink_message *req) { - char kind[STRLEN("teql") + DECIMAL_STR_MAX(unsigned)]; +static int trivial_link_equalizer_verify(QDisc *qdisc) { + _cleanup_free_ char *tca_kind = NULL; TrivialLinkEqualizer *teql; - int r; - assert(link); - assert(qdisc); - assert(req); + teql = TEQL(ASSERT_PTR(qdisc)); - teql = TEQL(qdisc); + if (asprintf(&tca_kind, "teql%u", teql->id) < 0) + return log_oom(); - xsprintf(kind, "teql%u", teql->id); - r = sd_netlink_message_append_string(req, TCA_KIND, kind); - if (r < 0) - return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m"); - - return 0; + return free_and_replace(qdisc->tca_kind, tca_kind); } const QDiscVTable teql_vtable = { .object_size = sizeof(TrivialLinkEqualizer), - .fill_tca_kind = trivial_link_equalizer_fill_tca_kind, + .verify = trivial_link_equalizer_verify, }; int config_parse_trivial_link_equalizer_id( diff --git a/src/network/test-network.c b/src/network/test-network.c index 4409a47cf..aef7459b3 100644 --- a/src/network/test-network.c +++ b/src/network/test-network.c @@ -208,8 +208,10 @@ static void test_address_equality(void) { assert_se(in_addr_from_string(AF_INET, "192.168.3.9", &a2->in_addr) >= 0); assert_se(address_equal(a1, a2)); assert_se(in_addr_from_string(AF_INET, "192.168.3.10", &a1->in_addr_peer) >= 0); - assert_se(address_equal(a1, a2)); + assert_se(!address_equal(a1, a2)); assert_se(in_addr_from_string(AF_INET, "192.168.3.11", &a2->in_addr_peer) >= 0); + assert_se(!address_equal(a1, a2)); + assert_se(in_addr_from_string(AF_INET, "192.168.3.10", &a2->in_addr_peer) >= 0); assert_se(address_equal(a1, a2)); a1->prefixlen = 10; assert_se(!address_equal(a1, a2)); @@ -225,8 +227,9 @@ static void test_address_equality(void) { assert_se(address_equal(a1, a2)); a2->prefixlen = 8; - assert_se(address_equal(a1, a2)); + assert_se(!address_equal(a1, a2)); + a2->prefixlen = 10; assert_se(in_addr_from_string(AF_INET6, "2001:4ca0:4f01::1", &a2->in_addr) >= 0); assert_se(!address_equal(a1, a2)); } diff --git a/src/network/test-networkd-address.c b/src/network/test-networkd-address.c index 46e8369ab..a40c57148 100644 --- a/src/network/test-networkd-address.c +++ b/src/network/test-networkd-address.c @@ -11,12 +11,10 @@ static void test_FORMAT_LIFETIME_one(usec_t lifetime, const char *expected) { assert_se(streq(t, expected)); } -static void test_FORMAT_LIFETIME(void) { +TEST(FORMAT_LIFETIME) { usec_t now_usec; - log_info("/* %s */", __func__); - - now_usec = now(clock_boottime_or_monotonic()); + now_usec = now(CLOCK_BOOTTIME); test_FORMAT_LIFETIME_one(now_usec, "for 0"); test_FORMAT_LIFETIME_one(usec_add(now_usec, 2 * USEC_PER_SEC - 1), "for 1s"); @@ -24,10 +22,4 @@ static void test_FORMAT_LIFETIME(void) { test_FORMAT_LIFETIME_one(USEC_INFINITY, "forever"); } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_DEBUG); - - test_FORMAT_LIFETIME(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c index 4b00a9808..2e4ca0cb5 100644 --- a/src/network/test-networkd-conf.c +++ b/src/network/test-networkd-conf.c @@ -6,8 +6,10 @@ #include "net-condition.h" #include "networkd-address.h" #include "networkd-conf.h" +#include "networkd-manager.h" #include "networkd-network.h" #include "strv.h" +#include "tests.h" static void test_config_parse_duid_type_one(const char *rvalue, int ret, DUIDType expected, usec_t expected_time) { DUID actual = {}; @@ -21,7 +23,7 @@ static void test_config_parse_duid_type_one(const char *rvalue, int ret, DUIDTyp assert_se(expected_time == actual.llt_time); } -static void test_config_parse_duid_type(void) { +TEST(config_parse_duid_type) { test_config_parse_duid_type_one("", 0, 0, 0); test_config_parse_duid_type_one("link-layer-time", 0, DUID_TYPE_LLT, 0); test_config_parse_duid_type_one("link-layer-time:2000-01-01 00:00:00 UTC", 0, DUID_TYPE_LLT, (usec_t) 946684800000000); @@ -85,7 +87,7 @@ static void test_config_parse_ether_addrs_one(const char *rvalue, const struct e #define BYTES_1_128 {0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,0x80} -static void test_config_parse_duid_rawdata(void) { +TEST(config_parse_duid_rawdata) { test_config_parse_duid_rawdata_one("", 0, &(DUID){}); test_config_parse_duid_rawdata_one("00:11:22:33:44:55:66:77", 0, &(DUID){0, 8, {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77}}); @@ -101,7 +103,7 @@ static void test_config_parse_duid_rawdata(void) { test_config_parse_duid_rawdata_one(&BYTES_0_128[2], 0, &(DUID){0, 128, BYTES_1_128}); } -static void test_config_parse_ether_addr(void) { +TEST(config_parse_ether_addr) { const struct ether_addr t[] = { { .ether_addr_octet = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff } }, { .ether_addr_octet = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab } }, @@ -166,11 +168,15 @@ static void test_config_parse_ether_addr(void) { } static void test_config_parse_address_one(const char *rvalue, int family, unsigned n_addresses, const union in_addr_union *u, unsigned char prefixlen) { + _cleanup_(manager_freep) Manager *manager = NULL; _cleanup_(network_unrefp) Network *network = NULL; + assert_se(manager_new(&manager, /* test_mode = */ true) >= 0); assert_se(network = new0(Network, 1)); network->n_ref = 1; + network->manager = manager; assert_se(network->filename = strdup("hogehoge.network")); + assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "*", &network->match.ifname, network) == 0); assert_se(config_parse_address("network", "filename", 1, "section", 1, "Address", 0, rvalue, network, network) == 0); assert_se(ordered_hashmap_size(network->addresses_by_section) == 1); @@ -187,7 +193,7 @@ static void test_config_parse_address_one(const char *rvalue, int family, unsign } } -static void test_config_parse_address(void) { +TEST(config_parse_address) { test_config_parse_address_one("", AF_INET, 0, NULL, 0); test_config_parse_address_one("/", AF_INET, 0, NULL, 0); test_config_parse_address_one("/8", AF_INET, 0, NULL, 0); @@ -214,7 +220,7 @@ static void test_config_parse_address(void) { test_config_parse_address_one("::1/-1", AF_INET6, 0, NULL, 0); } -static void test_config_parse_match_ifnames(void) { +TEST(config_parse_match_ifnames) { _cleanup_strv_free_ char **names = NULL; assert_se(config_parse_match_ifnames("network", "filename", 1, "section", 1, "Name", 0, "!hoge hogehoge foo", &names, NULL) == 0); @@ -224,7 +230,7 @@ static void test_config_parse_match_ifnames(void) { assert_se(strv_equal(names, STRV_MAKE("!hoge", "!hogehoge", "!foo", "!baz", "aaa", "bbb", "ccc"))); } -static void test_config_parse_match_strv(void) { +TEST(config_parse_match_strv) { _cleanup_strv_free_ char **names = NULL; assert_se(config_parse_match_strv("network", "filename", 1, "section", 1, "Name", 0, "!hoge hogehoge foo", &names, NULL) == 0); @@ -242,16 +248,4 @@ static void test_config_parse_match_strv(void) { "KEY3=val with \\quotation\\"))); } -int main(int argc, char **argv) { - log_parse_environment(); - log_open(); - - test_config_parse_duid_type(); - test_config_parse_duid_rawdata(); - test_config_parse_ether_addr(); - test_config_parse_address(); - test_config_parse_match_ifnames(); - test_config_parse_match_strv(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/network/wait-online/link.c b/src/network/wait-online/link.c index 0f5f68e76..ce6c8b181 100644 --- a/src/network/wait-online/link.c +++ b/src/network/wait-online/link.c @@ -97,7 +97,7 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) { } int link_update_monitor(Link *l) { - _cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *required_family = NULL, + _cleanup_free_ char *required_operstate = NULL, *required_family = NULL, *ipv4_address_state = NULL, *ipv6_address_state = NULL, *state = NULL; int r, ret = 0; @@ -123,18 +123,9 @@ int link_update_monitor(Link *l) { "Failed to parse required operational state, ignoring: %m"); } - r = sd_network_link_get_operational_state(l->ifindex, &operstate); + r = network_link_get_operational_state(l->ifindex, &l->operational_state); if (r < 0) ret = log_link_debug_errno(l, r, "Failed to get operational state, ignoring: %m"); - else { - LinkOperationalState s; - - s = link_operstate_from_string(operstate); - if (s < 0) - ret = log_link_debug_errno(l, s, "Failed to parse operational state, ignoring: %m"); - else - l->operational_state = s; - } r = sd_network_link_get_required_family_for_online(l->ifindex, &required_family); if (r < 0) diff --git a/src/network/wait-online/manager.c b/src/network/wait-online/manager.c index 093622270..e5e23979e 100644 --- a/src/network/wait-online/manager.c +++ b/src/network/wait-online/manager.c @@ -380,7 +380,7 @@ int manager_new(Manager **ret, (void) sd_event_add_signal(m->event, NULL, SIGINT, NULL, NULL); if (timeout > 0) { - r = sd_event_add_time_relative(m->event, NULL, clock_boottime_or_monotonic(), timeout, 0, NULL, INT_TO_PTR(-ETIMEDOUT)); + r = sd_event_add_time_relative(m->event, NULL, CLOCK_BOOTTIME, timeout, 0, NULL, INT_TO_PTR(-ETIMEDOUT)); if (r < 0 && r != -EOVERFLOW) return r; } diff --git a/src/nspawn/fuzz-nspawn-oci.c b/src/nspawn/fuzz-nspawn-oci.c index cfebf65c0..91f2a81df 100644 --- a/src/nspawn/fuzz-nspawn-oci.c +++ b/src/nspawn/fuzz-nspawn-oci.c @@ -2,7 +2,6 @@ #include "alloc-util.h" #include "fd-util.h" -#include "fileio.h" #include "fuzz.h" #include "nspawn-oci.h" @@ -10,10 +9,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_fclose_ FILE *f = NULL; _cleanup_(settings_freep) Settings *s = NULL; - if (size == 0) - return 0; - - f = fmemopen_unlocked((char*) data, size, "re"); + f = data_to_file(data, size); assert_se(f); /* We don't want to fill the logs with messages about parse errors. diff --git a/src/nspawn/fuzz-nspawn-settings.c b/src/nspawn/fuzz-nspawn-settings.c index bd98ed26e..6b91e1506 100644 --- a/src/nspawn/fuzz-nspawn-settings.c +++ b/src/nspawn/fuzz-nspawn-settings.c @@ -2,7 +2,6 @@ #include "alloc-util.h" #include "fd-util.h" -#include "fileio.h" #include "fuzz.h" #include "nspawn-settings.h" @@ -10,10 +9,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_fclose_ FILE *f = NULL; _cleanup_(settings_freep) Settings *s = NULL; - if (size == 0) - return 0; - - f = fmemopen_unlocked((char*) data, size, "re"); + f = data_to_file(data, size); assert_se(f); /* We don't want to fill the logs with messages about parse errors. diff --git a/src/nspawn/meson.build b/src/nspawn/meson.build index dba8239a4..a42e16dd9 100644 --- a/src/nspawn/meson.build +++ b/src/nspawn/meson.build @@ -53,12 +53,12 @@ systemd_nspawn_sources = files('nspawn.c') ############################################################ tests += [ - [['src/nspawn/test-nspawn-tables.c'], + [files('test-nspawn-tables.c'), [libnspawn_core, libshared], [libseccomp]], - [['src/nspawn/test-patch-uid.c'], + [files('test-patch-uid.c'), [libnspawn_core, libshared], [libacl], @@ -66,12 +66,12 @@ tests += [ ] fuzzers += [ - [['src/nspawn/fuzz-nspawn-settings.c'], + [files('fuzz-nspawn-settings.c'), [libshared, libnspawn_core], [libseccomp]], - [['src/nspawn/fuzz-nspawn-oci.c'], + [files('fuzz-nspawn-oci.c'), [libshared, libnspawn_core], [libseccomp]], diff --git a/src/nspawn/nspawn-bind-user.c b/src/nspawn/nspawn-bind-user.c index d3113c303..45ddb59f7 100644 --- a/src/nspawn/nspawn-bind-user.c +++ b/src/nspawn/nspawn-bind-user.c @@ -10,9 +10,6 @@ #include "user-util.h" #include "userdb.h" -#define MAP_UID_START 60514 -#define MAP_UID_END 60577 - static int check_etc_passwd_collisions( const char *directory, const char *name, @@ -157,11 +154,11 @@ static int find_free_uid(const char *directory, uid_t max_uid, uid_t *current_ui assert(current_uid); for (;; (*current_uid) ++) { - if (*current_uid > MAP_UID_END || *current_uid > max_uid) + if (*current_uid > MAP_UID_MAX || *current_uid > max_uid) return log_error_errno( SYNTHETIC_ERRNO(EBUSY), "No suitable available UID in range " UID_FMT "…" UID_FMT " in container detected, can't map user.", - MAP_UID_START, MAP_UID_END); + MAP_UID_MIN, MAP_UID_MAX); r = check_etc_passwd_collisions(directory, NULL, *current_uid); if (r < 0) @@ -202,8 +199,7 @@ int bind_user_prepare( BindUserContext **ret) { _cleanup_(bind_user_context_freep) BindUserContext *c = NULL; - uid_t current_uid = MAP_UID_START; - char **n; + uid_t current_uid = MAP_UID_MIN; int r; assert(custom_mounts); diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c index d472e80c0..9e6379ae7 100644 --- a/src/nspawn/nspawn-cgroup.c +++ b/src/nspawn/nspawn-cgroup.c @@ -22,7 +22,6 @@ static int chown_cgroup_path(const char *path, uid_t uid_shift) { _cleanup_close_ int fd = -1; - const char *fn; fd = open(path, O_RDONLY|O_CLOEXEC|O_DIRECTORY); if (fd < 0) @@ -154,7 +153,7 @@ int create_subcgroup(pid_t pid, bool keep_unit, CGroupUnified unified_requested) * the unified hierarchy and the container does the same, and we did not create a scope unit for the container * move us and the container into two separate subcgroups. * - * Moreover, container payloads such as systemd try to manage the cgroup they run in in full (i.e. including + * Moreover, container payloads such as systemd try to manage the cgroup they run in full (i.e. including * its attributes), while the host systemd will only delegate cgroups for children of the cgroup created for a * delegation unit, instead of the cgroup itself. This means, if we'd pass on the cgroup allocated from the * host systemd directly to the payload, the host and payload systemd might fight for the cgroup diff --git a/src/nspawn/nspawn-expose-ports.c b/src/nspawn/nspawn-expose-ports.c index 2890190ee..166383cce 100644 --- a/src/nspawn/nspawn-expose-ports.c +++ b/src/nspawn/nspawn-expose-ports.c @@ -16,11 +16,10 @@ #include "util.h" int expose_port_parse(ExposePort **l, const char *s) { - const char *split, *e; uint16_t container_port, host_port; + ExposePort *port; int protocol; - ExposePort *p; int r; assert(l); @@ -59,17 +58,17 @@ int expose_port_parse(ExposePort **l, const char *s) { if (p->protocol == protocol && p->host_port == host_port) return -EEXIST; - p = new(ExposePort, 1); - if (!p) + port = new(ExposePort, 1); + if (!port) return -ENOMEM; - *p = (ExposePort) { + *port = (ExposePort) { .protocol = protocol, .host_port = host_port, .container_port = container_port, }; - LIST_PREPEND(ports, *l, p); + LIST_PREPEND(ports, *l, port); return 0; } @@ -84,7 +83,6 @@ void expose_port_free_all(ExposePort *p) { } int expose_port_flush(FirewallContext **fw_ctx, ExposePort* l, int af, union in_addr_union *exposed) { - ExposePort *p; int r; assert(exposed); @@ -117,7 +115,6 @@ int expose_port_flush(FirewallContext **fw_ctx, ExposePort* l, int af, union in_ int expose_port_execute(sd_netlink *rtnl, FirewallContext **fw_ctx, ExposePort *l, int af, union in_addr_union *exposed) { _cleanup_free_ struct local_address *addresses = NULL; union in_addr_union new_exposed; - ExposePort *p; bool add; int r; diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index d25bef746..a93b8c38c 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -19,64 +19,64 @@ struct ConfigPerfItem; %struct-type %includes %% -Exec.Boot, config_parse_boot, 0, 0 -Exec.Ephemeral, config_parse_tristate, 0, offsetof(Settings, ephemeral) -Exec.ProcessTwo, config_parse_pid2, 0, 0 -Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters) -Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment) -Exec.User, config_parse_string, 0, offsetof(Settings, user) -Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability) -Exec.AmbientCapability, config_parse_capability, 0, offsetof(Settings, ambient_capability) -Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability) -Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal) -Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) -Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) -Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory) -Exec.PivotRoot, config_parse_pivot_root, 0, 0 -Exec.PrivateUsers, config_parse_private_users, 0, 0 -Exec.NotifyReady, config_parse_tristate, 0, offsetof(Settings, notify_ready) -Exec.SystemCallFilter, config_parse_syscall_filter, 0, 0, -Exec.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof(Settings, rlimit) -Exec.LimitFSIZE, config_parse_rlimit, RLIMIT_FSIZE, offsetof(Settings, rlimit) -Exec.LimitDATA, config_parse_rlimit, RLIMIT_DATA, offsetof(Settings, rlimit) -Exec.LimitSTACK, config_parse_rlimit, RLIMIT_STACK, offsetof(Settings, rlimit) -Exec.LimitCORE, config_parse_rlimit, RLIMIT_CORE, offsetof(Settings, rlimit) -Exec.LimitRSS, config_parse_rlimit, RLIMIT_RSS, offsetof(Settings, rlimit) -Exec.LimitNOFILE, config_parse_rlimit, RLIMIT_NOFILE, offsetof(Settings, rlimit) -Exec.LimitAS, config_parse_rlimit, RLIMIT_AS, offsetof(Settings, rlimit) -Exec.LimitNPROC, config_parse_rlimit, RLIMIT_NPROC, offsetof(Settings, rlimit) -Exec.LimitMEMLOCK, config_parse_rlimit, RLIMIT_MEMLOCK, offsetof(Settings, rlimit) -Exec.LimitLOCKS, config_parse_rlimit, RLIMIT_LOCKS, offsetof(Settings, rlimit) -Exec.LimitSIGPENDING, config_parse_rlimit, RLIMIT_SIGPENDING, offsetof(Settings, rlimit) -Exec.LimitMSGQUEUE, config_parse_rlimit, RLIMIT_MSGQUEUE, offsetof(Settings, rlimit) -Exec.LimitNICE, config_parse_rlimit, RLIMIT_NICE, offsetof(Settings, rlimit) -Exec.LimitRTPRIO, config_parse_rlimit, RLIMIT_RTPRIO, offsetof(Settings, rlimit) -Exec.LimitRTTIME, config_parse_rlimit, RLIMIT_RTTIME, offsetof(Settings, rlimit) -Exec.Hostname, config_parse_hostname, 0, offsetof(Settings, hostname) -Exec.NoNewPrivileges, config_parse_tristate, 0, offsetof(Settings, no_new_privileges) -Exec.OOMScoreAdjust, config_parse_oom_score_adjust, 0, 0 -Exec.CPUAffinity, config_parse_cpu_affinity, 0, 0 -Exec.ResolvConf, config_parse_resolv_conf, 0, offsetof(Settings, resolv_conf) -Exec.LinkJournal, config_parse_link_journal, 0, 0 -Exec.Timezone, config_parse_timezone, 0, offsetof(Settings, timezone) -Exec.SuppressSync, config_parse_tristate, 0, offsetof(Settings, suppress_sync) -Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) -Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) -Files.Bind, config_parse_bind, 0, 0 -Files.BindReadOnly, config_parse_bind, 1, 0 -Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 -Files.Inaccessible, config_parse_inaccessible, 0, 0 -Files.Overlay, config_parse_overlay, 0, 0 -Files.OverlayReadOnly, config_parse_overlay, 1, 0 -Files.PrivateUsersChown, config_parse_userns_chown, 0, offsetof(Settings, userns_ownership) -Files.PrivateUsersOwnership, config_parse_userns_ownership, 0, offsetof(Settings, userns_ownership) -Files.BindUser, config_parse_bind_user, 0, offsetof(Settings, bind_user) -Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) -Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) -Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan) -Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan) -Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth) -Network.VirtualEthernetExtra, config_parse_veth_extra, 0, 0 -Network.Bridge, config_parse_ifname, 0, offsetof(Settings, network_bridge) -Network.Zone, config_parse_network_zone, 0, 0 -Network.Port, config_parse_expose_port, 0, 0 +Exec.Boot, config_parse_boot, 0, 0 +Exec.Ephemeral, config_parse_tristate, 0, offsetof(Settings, ephemeral) +Exec.ProcessTwo, config_parse_pid2, 0, 0 +Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters) +Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment) +Exec.User, config_parse_string, CONFIG_PARSE_STRING_SAFE, offsetof(Settings, user) +Exec.Capability, config_parse_capability, 0, offsetof(Settings, capability) +Exec.AmbientCapability, config_parse_capability, 0, offsetof(Settings, ambient_capability) +Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, drop_capability) +Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal) +Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) +Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) +Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory) +Exec.PivotRoot, config_parse_pivot_root, 0, 0 +Exec.PrivateUsers, config_parse_private_users, 0, 0 +Exec.NotifyReady, config_parse_tristate, 0, offsetof(Settings, notify_ready) +Exec.SystemCallFilter, config_parse_syscall_filter, 0, 0 +Exec.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof(Settings, rlimit) +Exec.LimitFSIZE, config_parse_rlimit, RLIMIT_FSIZE, offsetof(Settings, rlimit) +Exec.LimitDATA, config_parse_rlimit, RLIMIT_DATA, offsetof(Settings, rlimit) +Exec.LimitSTACK, config_parse_rlimit, RLIMIT_STACK, offsetof(Settings, rlimit) +Exec.LimitCORE, config_parse_rlimit, RLIMIT_CORE, offsetof(Settings, rlimit) +Exec.LimitRSS, config_parse_rlimit, RLIMIT_RSS, offsetof(Settings, rlimit) +Exec.LimitNOFILE, config_parse_rlimit, RLIMIT_NOFILE, offsetof(Settings, rlimit) +Exec.LimitAS, config_parse_rlimit, RLIMIT_AS, offsetof(Settings, rlimit) +Exec.LimitNPROC, config_parse_rlimit, RLIMIT_NPROC, offsetof(Settings, rlimit) +Exec.LimitMEMLOCK, config_parse_rlimit, RLIMIT_MEMLOCK, offsetof(Settings, rlimit) +Exec.LimitLOCKS, config_parse_rlimit, RLIMIT_LOCKS, offsetof(Settings, rlimit) +Exec.LimitSIGPENDING, config_parse_rlimit, RLIMIT_SIGPENDING, offsetof(Settings, rlimit) +Exec.LimitMSGQUEUE, config_parse_rlimit, RLIMIT_MSGQUEUE, offsetof(Settings, rlimit) +Exec.LimitNICE, config_parse_rlimit, RLIMIT_NICE, offsetof(Settings, rlimit) +Exec.LimitRTPRIO, config_parse_rlimit, RLIMIT_RTPRIO, offsetof(Settings, rlimit) +Exec.LimitRTTIME, config_parse_rlimit, RLIMIT_RTTIME, offsetof(Settings, rlimit) +Exec.Hostname, config_parse_hostname, 0, offsetof(Settings, hostname) +Exec.NoNewPrivileges, config_parse_tristate, 0, offsetof(Settings, no_new_privileges) +Exec.OOMScoreAdjust, config_parse_oom_score_adjust, 0, 0 +Exec.CPUAffinity, config_parse_cpu_affinity, 0, 0 +Exec.ResolvConf, config_parse_resolv_conf, 0, offsetof(Settings, resolv_conf) +Exec.LinkJournal, config_parse_link_journal, 0, 0 +Exec.Timezone, config_parse_timezone, 0, offsetof(Settings, timezone) +Exec.SuppressSync, config_parse_tristate, 0, offsetof(Settings, suppress_sync) +Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) +Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) +Files.Bind, config_parse_bind, 0, 0 +Files.BindReadOnly, config_parse_bind, 1, 0 +Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 +Files.Inaccessible, config_parse_inaccessible, 0, 0 +Files.Overlay, config_parse_overlay, 0, 0 +Files.OverlayReadOnly, config_parse_overlay, 1, 0 +Files.PrivateUsersChown, config_parse_userns_chown, 0, offsetof(Settings, userns_ownership) +Files.PrivateUsersOwnership, config_parse_userns_ownership, 0, offsetof(Settings, userns_ownership) +Files.BindUser, config_parse_bind_user, 0, offsetof(Settings, bind_user) +Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) +Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) +Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan) +Network.IPVLAN, config_parse_strv, 0, offsetof(Settings, network_ipvlan) +Network.VirtualEthernet, config_parse_tristate, 0, offsetof(Settings, network_veth) +Network.VirtualEthernetExtra, config_parse_veth_extra, 0, 0 +Network.Bridge, config_parse_ifname, 0, offsetof(Settings, network_bridge) +Network.Zone, config_parse_network_zone, 0, 0 +Network.Port, config_parse_expose_port, 0, 0 diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index 40773d90c..6378e1b70 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -166,8 +166,6 @@ int custom_mount_prepare_all(const char *dest, CustomMount *l, size_t n) { } if (m->type == CUSTOM_MOUNT_OVERLAY) { - char **j; - STRV_FOREACH(j, m->lower) { char *s; @@ -319,8 +317,6 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl if (!destination) return -ENOMEM; } else { - char **i; - /* If more than two parameters are specified, the last one is the destination, the second to last one * the "upper", and all before that the "lower" directories. */ @@ -408,7 +404,7 @@ int tmpfs_patch_options( } int mount_sysfs(const char *dest, MountSettingsMask mount_settings) { - const char *full, *top, *x; + const char *full, *top; int r; unsigned long extra_flags = 0; @@ -468,7 +464,7 @@ int mount_sysfs(const char *dest, MountSettingsMask mount_settings) { /* Create mountpoint for cgroups. Otherwise we are not allowed since we * remount /sys read-only. */ - x = prefix_roota(top, "/fs/cgroup"); + const char *x = prefix_roota(top, "/fs/cgroup"); (void) mkdir_p(x, 0755); return mount_nofollow_verbose(LOG_ERR, NULL, top, NULL, @@ -780,7 +776,7 @@ static int mount_bind(const char *dest, CustomMount *m, uid_t uid_shift, uid_t u } if (idmapped) { - r = remount_idmap(where, uid_shift, uid_range); + r = remount_idmap(where, uid_shift, uid_range, REMOUNT_IDMAP_HOST_ROOT); if (r < 0) return log_error_errno(r, "Failed to map ids for bind mount %s: %m", where); } diff --git a/src/nspawn/nspawn-network.c b/src/nspawn/nspawn-network.c index 023b1e7e1..fab4eb960 100644 --- a/src/nspawn/nspawn-network.c +++ b/src/nspawn/nspawn-network.c @@ -288,7 +288,6 @@ int setup_veth_extra( _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; uint64_t idx = 0; - char **a, **b; int r; assert(machine_name); @@ -495,7 +494,6 @@ int test_network_interface_initialized(const char *name) { int move_network_interfaces(int netns_fd, char **ifaces) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - char **i; int r; if (strv_isempty(ifaces)) @@ -532,7 +530,6 @@ int move_network_interfaces(int netns_fd, char **ifaces) { int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; unsigned idx = 0; - char **i; int r; if (strv_isempty(ifaces)) @@ -619,7 +616,6 @@ int setup_macvlan(const char *machine_name, pid_t pid, char **ifaces) { int setup_ipvlan(const char *machine_name, pid_t pid, char **ifaces) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - char **i; int r; if (strv_isempty(ifaces)) @@ -728,7 +724,6 @@ int veth_extra_parse(char ***l, const char *p) { int remove_veth_links(const char *primary, char **pairs) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; - char **a, **b; int r; /* In some cases the kernel might pin the veth links between host and container even after the namespace diff --git a/src/nspawn/nspawn-oci.c b/src/nspawn/nspawn-oci.c index 9e59d6a81..44564ba61 100644 --- a/src/nspawn/nspawn-oci.c +++ b/src/nspawn/nspawn-oci.c @@ -1892,7 +1892,6 @@ static int oci_seccomp_syscalls(const char *name, JsonVariant *v, JsonDispatchFl struct syscall_rule rule = { .action = UINT32_MAX, }; - char **i; r = json_dispatch(e, table, oci_unexpected, flags, &rule); if (r < 0) @@ -2006,7 +2005,7 @@ static int oci_masked_paths(const char *name, JsonVariant *v, JsonDispatchFlags if (!path_is_absolute(p)) return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), - "Path is not not absolute, refusing: %s", p); + "Path is not absolute, refusing: %s", p); if (oci_exclude_mount(p)) continue; @@ -2048,7 +2047,7 @@ static int oci_readonly_paths(const char *name, JsonVariant *v, JsonDispatchFlag if (!path_is_absolute(p)) return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), - "Path is not not absolute, refusing: %s", p); + "Path is not absolute, refusing: %s", p); if (oci_exclude_mount(p)) continue; diff --git a/src/nspawn/nspawn-seccomp.c b/src/nspawn/nspawn-seccomp.c index c94512ef3..77f4c2ac8 100644 --- a/src/nspawn/nspawn-seccomp.c +++ b/src/nspawn/nspawn-seccomp.c @@ -140,7 +140,6 @@ static int add_syscall_filters( }; _cleanup_strv_free_ char **added = NULL; - char **p; int r; for (size_t i = 0; i < ELEMENTSOF(allow_list); i++) { diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 1f58bf3ed..3a5d72549 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -710,31 +710,6 @@ int config_parse_syscall_filter( } } -int config_parse_hostname( - const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - - char **s = data; - - assert(rvalue); - assert(s); - - if (!hostname_is_valid(rvalue, 0)) { - log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid hostname, ignoring: %s", rvalue); - return 0; - } - - return free_and_strdup_warn(s, empty_to_null(rvalue)); -} - int config_parse_oom_score_adjust( const char *unit, const char *filename, diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 59397ca54..004b663e9 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -264,7 +264,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_boot); CONFIG_PARSER_PROTOTYPE(config_parse_pid2); CONFIG_PARSER_PROTOTYPE(config_parse_private_users); CONFIG_PARSER_PROTOTYPE(config_parse_syscall_filter); -CONFIG_PARSER_PROTOTYPE(config_parse_hostname); CONFIG_PARSER_PROTOTYPE(config_parse_oom_score_adjust); CONFIG_PARSER_PROTOTYPE(config_parse_cpu_affinity); CONFIG_PARSER_PROTOTYPE(config_parse_resolv_conf); diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c index 6dbd6ba4c..85c439815 100644 --- a/src/nspawn/nspawn-stub-pid1.c +++ b/src/nspawn/nspawn-stub-pid1.c @@ -142,10 +142,8 @@ int stub_pid1(sd_id128_t uuid) { if (quit_usec == USEC_INFINITY) r = sigwaitinfo(&waitmask, &si); - else { - struct timespec ts; - r = sigtimedwait(&waitmask, &si, timespec_store(&ts, quit_usec - current_usec)); - } + else + r = sigtimedwait(&waitmask, &si, TIMESPEC_STORE(quit_usec - current_usec)); if (r < 0) { if (errno == EINTR) /* strace -p attach can result in EINTR, let's handle this nicely. */ continue; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 8f17ab881..aa7367c5c 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -2564,7 +2564,7 @@ static int setup_hostname(void) { static int setup_journal(const char *directory) { _cleanup_free_ char *d = NULL; - const char *dirname, *p, *q; + const char *p, *q; sd_id128_t this_id; bool try; int r; @@ -3149,7 +3149,6 @@ static int patch_sysctl(void) { }; unsigned long flags; - char **k, **v; int r; flags = effective_clone_ns_flags(); @@ -3453,7 +3452,7 @@ static int inner_child( assert(!sd_id128_is_null(arg_uuid)); - if (asprintf(envp + n_env++, "container_uuid=%s", ID128_TO_UUID_STRING(arg_uuid)) < 0) + if (asprintf(envp + n_env++, "container_uuid=%s", SD_ID128_TO_UUID_STRING(arg_uuid)) < 0) return log_oom(); if (fdset_size(fds) > 0) { @@ -3513,7 +3512,6 @@ static int inner_child( (void) fdset_close_others(fds); if (arg_start_mode == START_BOOT) { - const char *init; char **a; size_t m; @@ -3552,10 +3550,13 @@ static int inner_child( /* If we cannot change the directory, we'll end up in /, that is expected. */ (void) chdir(home ?: "/root"); - execle("/bin/bash", "-bash", NULL, env_use); - execle("/bin/sh", "-sh", NULL, env_use); + execle(DEFAULT_USER_SHELL, "-" DEFAULT_USER_SHELL_NAME, NULL, env_use); + if (!streq(DEFAULT_USER_SHELL, "/bin/bash")) + execle("/bin/bash", "-bash", NULL, env_use); + if (!streq(DEFAULT_USER_SHELL, "/bin/sh")) + execle("/bin/sh", "-sh", NULL, env_use); - exec_target = "/bin/bash, /bin/sh"; + exec_target = DEFAULT_USER_SHELL ", /bin/bash, /bin/sh"; } return log_error_errno(errno, "execv(%s) failed: %m", exec_target); @@ -3563,7 +3564,7 @@ static int inner_child( static int setup_notify_child(void) { _cleanup_close_ int fd = -1; - union sockaddr_union sa = { + static const union sockaddr_union sa = { .un.sun_family = AF_UNIX, .un.sun_path = NSPAWN_NOTIFY_SOCKET_PATH, }; @@ -3616,10 +3617,11 @@ static int outer_child( ssize_t l; int r; - /* This is the "outer" child process, i.e the one forked off by the container manager itself. It already has - * its own CLONE_NEWNS namespace (which was created by the clone()). It still lives in the host's CLONE_NEWPID, - * CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWUSER and CLONE_NEWNET namespaces. After it completed a number of - * initializations a second child (the "inner" one) is forked off it, and it exits. */ + /* This is the "outer" child process, i.e the one forked off by the container manager itself. It + * already has its own CLONE_NEWNS namespace (which was created by the clone()). It still lives in + * the host's CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC, CLONE_NEWUSER and CLONE_NEWNET + * namespaces. After it completed a number of initializations a second child (the "inner" one) is + * forked off it, and it exits. */ assert(barrier); assert(directory); @@ -3649,10 +3651,10 @@ static int outer_child( return r; if (dissected_image) { - /* If we are operating on a disk image, then mount its root directory now, but leave out the rest. We - * can read the UID shift from it if we need to. Further down we'll mount the rest, but then with the - * uid shift known. That way we can mount VFAT file systems shifted to the right place right away. This - * makes sure ESP partitions and userns are compatible. */ + /* If we are operating on a disk image, then mount its root directory now, but leave out the + * rest. We can read the UID shift from it if we need to. Further down we'll mount the rest, + * but then with the uid shift known. That way we can mount VFAT file systems shifted to the + * right place right away. This makes sure ESP partitions and userns are compatible. */ r = dissected_image_mount_and_warn( dissected_image, @@ -3682,9 +3684,9 @@ static int outer_child( "Short write while sending UID shift."); if (arg_userns_mode == USER_NAMESPACE_PICK) { - /* When we are supposed to pick the UID shift, the parent will check now whether the UID shift - * we just read from the image is available. If yes, it will send the UID shift back to us, if - * not it will pick a different one, and send it back to us. */ + /* When we are supposed to pick the UID shift, the parent will check now whether the + * UID shift we just read from the image is available. If yes, it will send the UID + * shift back to us, if not it will pick a different one, and send it back to us. */ l = recv(uid_shift_socket, &arg_uid_shift, sizeof(arg_uid_shift), 0); if (l < 0) @@ -3740,7 +3742,8 @@ static int outer_child( return r; if (arg_userns_mode != USER_NAMESPACE_NO && bind_user_context) { - /* Send the user maps we determined to the parent, so that it installs it in our user namespace UID map table */ + /* Send the user maps we determined to the parent, so that it installs it in our user + * namespace UID map table */ for (size_t i = 0; i < bind_user_context->n_data; i++) { uid_t map[] = { @@ -3779,7 +3782,7 @@ static int outer_child( IN_SET(arg_userns_ownership, USER_NAMESPACE_OWNERSHIP_MAP, USER_NAMESPACE_OWNERSHIP_AUTO) && arg_uid_shift != 0) { - r = remount_idmap(directory, arg_uid_shift, arg_uid_range); + r = remount_idmap(directory, arg_uid_shift, arg_uid_range, REMOUNT_IDMAP_HOST_ROOT); if (r == -EINVAL || ERRNO_IS_NOT_SUPPORTED(r)) { /* This might fail because the kernel or file system doesn't support idmapping. We * can't really distinguish this nicely, nor do we have any guarantees about the @@ -3833,15 +3836,13 @@ static int outer_child( unified_cgroup_hierarchy_socket = safe_close(unified_cgroup_hierarchy_socket); } - /* Mark everything as shared so our mounts get propagated down. This is - * required to make new bind mounts available in systemd services - * inside the container that create a new mount namespace. - * See https://github.com/systemd/systemd/issues/3860 - * Further submounts (such as /dev) done after this will inherit the - * shared propagation mode. + /* Mark everything as shared so our mounts get propagated down. This is required to make new bind + * mounts available in systemd services inside the container that create a new mount namespace. See + * https://github.com/systemd/systemd/issues/3860 Further submounts (such as /dev) done after this + * will inherit the shared propagation mode. * - * IMPORTANT: Do not overmount the root directory anymore from now on to - * enable moving the root directory mount to root later on. + * IMPORTANT: Do not overmount the root directory anymore from now on to enable moving the root + * directory mount to root later on. * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251 */ r = mount_nofollow_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL); @@ -4239,7 +4240,7 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r if (!tags) return log_oom(); - if (strv_find(tags, "READY=1")) { + if (strv_contains(tags, "READY=1")) { r = sd_notify(false, "READY=1\n"); if (r < 0) log_warning_errno(r, "Failed to send readiness notification, ignoring: %m"); @@ -4604,7 +4605,7 @@ static int load_settings(void) { _cleanup_(settings_freep) Settings *settings = NULL; _cleanup_fclose_ FILE *f = NULL; _cleanup_free_ char *p = NULL; - const char *fn, *i; + const char *fn; int r; if (arg_oci_bundle) @@ -5294,25 +5295,25 @@ static int run_container( } static int initialize_rlimits(void) { - /* The default resource limits the kernel passes to PID 1, as per kernel 4.16. Let's pass our container payload + /* The default resource limits the kernel passes to PID 1, as per kernel 5.16. Let's pass our container payload * the same values as the kernel originally passed to PID 1, in order to minimize differences between host and * container execution environments. */ static const struct rlimit kernel_defaults[_RLIMIT_MAX] = { - [RLIMIT_AS] = { RLIM_INFINITY, RLIM_INFINITY }, - [RLIMIT_CORE] = { 0, RLIM_INFINITY }, - [RLIMIT_CPU] = { RLIM_INFINITY, RLIM_INFINITY }, - [RLIMIT_DATA] = { RLIM_INFINITY, RLIM_INFINITY }, - [RLIMIT_FSIZE] = { RLIM_INFINITY, RLIM_INFINITY }, - [RLIMIT_LOCKS] = { RLIM_INFINITY, RLIM_INFINITY }, - [RLIMIT_MEMLOCK] = { 65536, 65536 }, - [RLIMIT_MSGQUEUE] = { 819200, 819200 }, - [RLIMIT_NICE] = { 0, 0 }, - [RLIMIT_NOFILE] = { 1024, 4096 }, - [RLIMIT_RSS] = { RLIM_INFINITY, RLIM_INFINITY }, - [RLIMIT_RTPRIO] = { 0, 0 }, - [RLIMIT_RTTIME] = { RLIM_INFINITY, RLIM_INFINITY }, - [RLIMIT_STACK] = { 8388608, RLIM_INFINITY }, + [RLIMIT_AS] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_CORE] = { 0, RLIM_INFINITY }, + [RLIMIT_CPU] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_DATA] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_FSIZE] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_LOCKS] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_MEMLOCK] = { DEFAULT_RLIMIT_MEMLOCK, DEFAULT_RLIMIT_MEMLOCK }, + [RLIMIT_MSGQUEUE] = { 819200, 819200 }, + [RLIMIT_NICE] = { 0, 0 }, + [RLIMIT_NOFILE] = { 1024, 4096 }, + [RLIMIT_RSS] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_RTPRIO] = { 0, 0 }, + [RLIMIT_RTTIME] = { RLIM_INFINITY, RLIM_INFINITY }, + [RLIMIT_STACK] = { 8388608, RLIM_INFINITY }, /* The kernel scales the default for RLIMIT_NPROC and RLIMIT_SIGPENDING based on the system's amount of * RAM. To provide best compatibility we'll read these limits off PID 1 instead of hardcoding them @@ -5612,31 +5613,36 @@ static int run(int argc, char *argv[]) { } if (arg_start_mode == START_BOOT) { + _cleanup_free_ char *b = NULL; const char *p; - if (arg_pivot_root_new) - p = prefix_roota(arg_directory, arg_pivot_root_new); - else + if (arg_pivot_root_new) { + b = path_join(arg_directory, arg_pivot_root_new); + if (!b) + return log_oom(); + + p = b; + } else p = arg_directory; if (path_is_os_tree(p) <= 0) { - log_error("Directory %s doesn't look like an OS root directory (os-release file is missing). Refusing.", p); - r = -EINVAL; + r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Directory %s doesn't look like an OS root directory (os-release file is missing). Refusing.", p); goto finish; } } else { - const char *p, *q; + _cleanup_free_ char *p = NULL; if (arg_pivot_root_new) - p = prefix_roota(arg_directory, arg_pivot_root_new); + p = path_join(arg_directory, arg_pivot_root_new, "/usr/"); else - p = arg_directory; + p = path_join(arg_directory, "/usr/"); + if (!p) + return log_oom(); - q = strjoina(p, "/usr/"); - - if (laccess(q, F_OK) < 0) { - log_error("Directory %s doesn't look like it has an OS tree. Refusing.", p); - r = -EINVAL; + if (laccess(p, F_OK) < 0) { + r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Directory %s doesn't look like it has an OS tree (/usr/ directory is missing). Refusing.", arg_directory); goto finish; } } diff --git a/src/nss-systemd/userdb-glue.c b/src/nss-systemd/userdb-glue.c index 002e6925f..c69667d66 100644 --- a/src/nss-systemd/userdb-glue.c +++ b/src/nss-systemd/userdb-glue.c @@ -217,7 +217,7 @@ int nss_pack_group_record( char *buffer, size_t buflen) { - char **array = NULL, *p, **m; + char **array = NULL, *p; size_t required, n = 0, i = 0; assert(g); diff --git a/src/oom/meson.build b/src/oom/meson.build index 579bc0d4e..6926ae0d1 100644 --- a/src/oom/meson.build +++ b/src/oom/meson.build @@ -26,7 +26,7 @@ if conf.get('ENABLE_OOMD') == 1 endif tests += [ - [['src/oom/test-oomd-util.c', - 'src/oom/oomd-util.c', - 'src/oom/oomd-util.h']], + [files('test-oomd-util.c', + 'oomd-util.c', + 'oomd-util.h')], ] diff --git a/src/oom/oomd-util.c b/src/oom/oomd-util.c index cef7519a7..a135824c5 100644 --- a/src/oom/oomd-util.c +++ b/src/oom/oomd-util.c @@ -192,6 +192,10 @@ int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run) { if (!pids_killed) return -ENOMEM; + r = increment_oomd_xattr(path, "user.oomd_ooms", 1); + if (r < 0) + log_debug_errno(r, "Failed to set user.oomd_ooms before kill: %m"); + if (recurse) r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, path, SIGKILL, CGROUP_IGNORE_SELF, pids_killed, log_kill, NULL); else @@ -216,9 +220,34 @@ int oomd_cgroup_kill(const char *path, bool recurse, bool dry_run) { return set_size(pids_killed) != 0; } +typedef void (*dump_candidate_func)(const OomdCGroupContext *ctx, FILE *f, const char *prefix); + +static int dump_kill_candidates(OomdCGroupContext **sorted, int n, int dump_until, dump_candidate_func dump_func) { + /* Try dumping top offendors, ignoring any errors that might happen. */ + _cleanup_free_ char *dump = NULL; + _cleanup_fclose_ FILE *f = NULL; + int r; + size_t size; + + f = open_memstream_unlocked(&dump, &size); + if (!f) + return -errno; + + fprintf(f, "Considered %d cgroups for killing, top candidates were:\n", n); + for (int i = 0; i < dump_until; i++) + dump_func(sorted[i], f, "\t"); + + r = fflush_and_check(f); + if (r < 0) + return r; + + return log_dump(LOG_INFO, dump); +} + int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char **ret_selected) { _cleanup_free_ OomdCGroupContext **sorted = NULL; int n, r, ret = 0; + int dump_until; assert(h); assert(ret_selected); @@ -227,6 +256,7 @@ int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char if (n < 0) return n; + dump_until = MIN(n, DUMP_ON_KILL_COUNT); for (int i = 0; i < n; i++) { /* Skip cgroups with no reclaim and memory usage; it won't alleviate pressure. * Continue since there might be "avoid" cgroups at the end. */ @@ -242,19 +272,24 @@ int oomd_kill_by_pgscan_rate(Hashmap *h, const char *prefix, bool dry_run, char continue; /* Try to find something else to kill */ } + dump_until = MAX(dump_until, i); char *selected = strdup(sorted[i]->path); if (!selected) return -ENOMEM; *ret_selected = selected; - return r; + ret = r; + break; } + dump_kill_candidates(sorted, n, dump_until, oomd_dump_memory_pressure_cgroup_context); + return ret; } int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run, char **ret_selected) { _cleanup_free_ OomdCGroupContext **sorted = NULL; int n, r, ret = 0; + int dump_until; assert(h); assert(ret_selected); @@ -263,6 +298,7 @@ int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run, if (n < 0) return n; + dump_until = MIN(n, DUMP_ON_KILL_COUNT); /* Try to kill cgroups with non-zero swap usage until we either succeed in killing or we get to a cgroup with * no swap usage. Threshold killing only cgroups with more than threshold swap usage. */ for (int i = 0; i < n; i++) { @@ -280,13 +316,17 @@ int oomd_kill_by_swap_usage(Hashmap *h, uint64_t threshold_usage, bool dry_run, continue; /* Try to find something else to kill */ } + dump_until = MAX(dump_until, i); char *selected = strdup(sorted[i]->path); if (!selected) return -ENOMEM; *ret_selected = selected; - return r; + ret = r; + break; } + dump_kill_candidates(sorted, n, dump_until, oomd_dump_swap_cgroup_context); + return ret; } diff --git a/src/oom/oomd-util.h b/src/oom/oomd-util.h index 3a91a3135..6aada9034 100644 --- a/src/oom/oomd-util.h +++ b/src/oom/oomd-util.h @@ -7,6 +7,7 @@ #include "hashmap.h" #include "psi-util.h" +#define DUMP_ON_KILL_COUNT 10 #define GROWING_SIZE_PERCENTILE 80 extern const struct hash_ops oomd_cgroup_ctx_hash_ops; diff --git a/src/oom/test-oomd-util.c b/src/oom/test-oomd-util.c index 13d9e60f1..82a60ad88 100644 --- a/src/oom/test-oomd-util.c +++ b/src/oom/test-oomd-util.c @@ -77,12 +77,16 @@ static void test_oomd_cgroup_kill(void) { abort(); } + assert_se(cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_ooms", &v) >= 0); + assert_se(streq(v, i == 0 ? "1" : "2")); + v = mfree(v); + /* Wait a bit since processes may take some time to be cleaned up. */ sleep(2); assert_se(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, cgroup) == true); assert_se(cg_get_xattr_malloc(SYSTEMD_CGROUP_CONTROLLER, cgroup, "user.oomd_kill", &v) >= 0); - assert_se(memcmp(v, i == 0 ? "2" : "4", 2) == 0); + assert_se(streq(v, i == 0 ? "2" : "4")); } } diff --git a/src/partition/repart.c b/src/partition/repart.c index d08f47f2c..118ab6c7d 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -195,6 +195,8 @@ struct Context { uint64_t start, end, total; struct fdisk_context *fdisk_context; + uint64_t sector_size; + uint64_t grain_size; sd_id128_t seed; }; @@ -371,7 +373,6 @@ static int context_add_free_area( static bool context_drop_one_priority(Context *context) { int32_t priority = 0; - Partition *p; bool exists = false; LIST_FOREACH(partitions, p, context->partitions) { @@ -407,9 +408,12 @@ static bool context_drop_one_priority(Context *context) { return true; } -static uint64_t partition_min_size(const Partition *p) { +static uint64_t partition_min_size(Context *context, const Partition *p) { uint64_t sz; + assert(context); + assert(p); + /* Calculate the disk space we really need at minimum for this partition. If the partition already * exists the current size is what we really need. If it doesn't exist yet refuse to allocate less * than 4K. @@ -428,50 +432,60 @@ static uint64_t partition_min_size(const Partition *p) { uint64_t d = 0; if (p->encrypt != ENCRYPT_OFF) - d += round_up_size(LUKS2_METADATA_SIZE, 4096); + d += round_up_size(LUKS2_METADATA_SIZE, context->grain_size); if (p->copy_blocks_size != UINT64_MAX) - d += round_up_size(p->copy_blocks_size, 4096); + d += round_up_size(p->copy_blocks_size, context->grain_size); else if (p->format || p->encrypt != ENCRYPT_OFF) { uint64_t f; /* If we shall synthesize a file system, take minimal fs size into account (assumed to be 4K if not known) */ - f = p->format ? minimal_size_by_fs_name(p->format) : UINT64_MAX; - d += f == UINT64_MAX ? 4096 : f; + f = p->format ? round_up_size(minimal_size_by_fs_name(p->format), context->grain_size) : UINT64_MAX; + d += f == UINT64_MAX ? context->grain_size : f; } if (d > sz) sz = d; } - return MAX(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, sz); + return MAX(round_up_size(p->size_min != UINT64_MAX ? p->size_min : DEFAULT_MIN_SIZE, context->grain_size), sz); } -static uint64_t partition_max_size(const Partition *p) { +static uint64_t partition_max_size(const Context *context, const Partition *p) { + uint64_t sm; + /* Calculate how large the partition may become at max. This is generally the configured maximum * size, except when it already exists and is larger than that. In that case it's the existing size, * since we never want to shrink partitions. */ + assert(context); + assert(p); + if (PARTITION_IS_FOREIGN(p)) { /* Don't allow changing size of partitions not managed by us */ assert(p->current_size != UINT64_MAX); return p->current_size; } - if (p->current_size != UINT64_MAX) - return MAX(p->current_size, p->size_max); + sm = round_down_size(p->size_max, context->grain_size); - return p->size_max; + if (p->current_size != UINT64_MAX) + return MAX(p->current_size, sm); + + return sm; } -static uint64_t partition_min_size_with_padding(const Partition *p) { +static uint64_t partition_min_size_with_padding(Context *context, const Partition *p) { uint64_t sz; /* Calculate the disk space we need for this partition plus any free space coming after it. This * takes user configured padding into account as well as any additional whitespace needed to align * the next partition to 4K again. */ - sz = partition_min_size(p); + assert(context); + assert(p); + + sz = partition_min_size(context, p); if (p->padding_min != UINT64_MAX) sz += p->padding_min; @@ -479,11 +493,11 @@ static uint64_t partition_min_size_with_padding(const Partition *p) { if (PARTITION_EXISTS(p)) { /* If the partition wasn't aligned, add extra space so that any we might add will be aligned */ assert(p->offset != UINT64_MAX); - return round_up_size(p->offset + sz, 4096) - p->offset; + return round_up_size(p->offset + sz, context->grain_size) - p->offset; } /* If this is a new partition we'll place it aligned, hence we just need to round up the required size here */ - return round_up_size(sz, 4096); + return round_up_size(sz, context->grain_size); } static uint64_t free_area_available(const FreeArea *a) { @@ -495,9 +509,12 @@ static uint64_t free_area_available(const FreeArea *a) { return a->size - a->allocated; } -static uint64_t free_area_available_for_new_partitions(const FreeArea *a) { +static uint64_t free_area_available_for_new_partitions(Context *context, const FreeArea *a) { uint64_t avail; + assert(context); + assert(a); + /* Similar to free_area_available(), but takes into account that the required size and padding of the * preceding partition is honoured. */ @@ -505,16 +522,16 @@ static uint64_t free_area_available_for_new_partitions(const FreeArea *a) { if (a->after) { uint64_t need, space_end, new_end; - need = partition_min_size_with_padding(a->after); + need = partition_min_size_with_padding(context, a->after); assert(a->after->offset != UINT64_MAX); assert(a->after->current_size != UINT64_MAX); /* Calculate where the free area ends, based on the offset of the partition preceding it */ - space_end = round_up_size(a->after->offset + a->after->current_size, 4096) + avail; + space_end = round_up_size(a->after->offset + a->after->current_size, context->grain_size) + avail; /* Calculate where the partition would end when we give it as much as it needs */ - new_end = round_up_size(a->after->offset + need, 4096); + new_end = round_up_size(a->after->offset + need, context->grain_size); /* Calculate saturated difference of the two: that's how much we have free for other partitions */ return LESS_BY(space_end, new_end); @@ -523,15 +540,18 @@ static uint64_t free_area_available_for_new_partitions(const FreeArea *a) { return avail; } -static int free_area_compare(FreeArea *const *a, FreeArea *const*b) { - return CMP(free_area_available_for_new_partitions(*a), - free_area_available_for_new_partitions(*b)); +static int free_area_compare(FreeArea *const *a, FreeArea *const*b, Context *context) { + assert(context); + + return CMP(free_area_available_for_new_partitions(context, *a), + free_area_available_for_new_partitions(context, *b)); } -static uint64_t charge_size(uint64_t total, uint64_t amount) { +static uint64_t charge_size(Context *context, uint64_t total, uint64_t amount) { + assert(context); /* Subtract the specified amount from total, rounding up to multiple of 4K if there's room */ assert(amount <= total); - return LESS_BY(total, round_up_size(amount, 4096)); + return LESS_BY(total, round_up_size(amount, context->grain_size)); } static uint64_t charge_weight(uint64_t total, uint64_t amount) { @@ -540,19 +560,17 @@ static uint64_t charge_weight(uint64_t total, uint64_t amount) { } static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_free_area) { - Partition *p; - assert(context); /* Sort free areas by size, putting smallest first */ - typesafe_qsort(context->free_areas, context->n_free_areas, free_area_compare); + typesafe_qsort_r(context->free_areas, context->n_free_areas, free_area_compare, context); /* In any case return size of the largest free area (i.e. not the size of all free areas * combined!) */ if (ret_largest_free_area) *ret_largest_free_area = context->n_free_areas == 0 ? 0 : - free_area_available_for_new_partitions(context->free_areas[context->n_free_areas-1]); + free_area_available_for_new_partitions(context, context->free_areas[context->n_free_areas-1]); /* A simple first-fit algorithm. We return true if we can fit the partitions in, otherwise false. */ LIST_FOREACH(partitions, p, context->partitions) { @@ -565,13 +583,13 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_ continue; /* How much do we need to fit? */ - required = partition_min_size_with_padding(p); - assert(required % 4096 == 0); + required = partition_min_size_with_padding(context, p); + assert(required % context->grain_size == 0); for (size_t i = 0; i < context->n_free_areas; i++) { a = context->free_areas[i]; - if (free_area_available_for_new_partitions(a) >= required) { + if (free_area_available_for_new_partitions(context, a) >= required) { fits = true; break; } @@ -592,7 +610,6 @@ static bool context_allocate_partitions(Context *context, uint64_t *ret_largest_ static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) { uint64_t weight_sum = 0; - Partition *p; assert(context); assert(a); @@ -656,7 +673,6 @@ static int context_grow_partitions_phase( uint64_t *span, uint64_t *weight_sum) { - Partition *p; int r; assert(context); @@ -683,8 +699,8 @@ static int context_grow_partitions_phase( if (r < 0) return r; - rsz = partition_min_size(p); - xsz = partition_max_size(p); + rsz = partition_min_size(context, p); + xsz = partition_max_size(context, p); if (phase == PHASE_OVERCHARGE && rsz > share) { /* This partition needs more than its calculated share. Let's assign @@ -712,13 +728,13 @@ static int context_grow_partitions_phase( /* Never change of foreign partitions (i.e. those we don't manage) */ p->new_size = p->current_size; else - p->new_size = MAX(round_down_size(share, 4096), rsz); + p->new_size = MAX(round_down_size(share, context->grain_size), rsz); charge = true; } if (charge) { - *span = charge_size(*span, p->new_size); + *span = charge_size(context, *span, p->new_size); *weight_sum = charge_weight(*weight_sum, p->weight); } @@ -742,7 +758,7 @@ static int context_grow_partitions_phase( charge = try_again = true; } else if (phase == PHASE_DISTRIBUTE) { - p->new_padding = round_down_size(share, 4096); + p->new_padding = round_down_size(share, context->grain_size); if (p->padding_min != UINT64_MAX && p->new_padding < p->padding_min) p->new_padding = p->padding_min; @@ -750,7 +766,7 @@ static int context_grow_partitions_phase( } if (charge) { - *span = charge_size(*span, p->new_padding); + *span = charge_size(context, *span, p->new_padding); *weight_sum = charge_weight(*weight_sum, p->padding_weight); } @@ -779,7 +795,7 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) { assert(a->after->offset != UINT64_MAX); assert(a->after->current_size != UINT64_MAX); - span += round_up_size(a->after->offset + a->after->current_size, 4096) - a->after->offset; + span += round_up_size(a->after->offset + a->after->current_size, context->grain_size) - a->after->offset; } for (GrowPartitionPhase phase = 0; phase < _GROW_PARTITION_PHASE_MAX;) { @@ -799,21 +815,19 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) { assert(a->after->new_size != UINT64_MAX); /* Calculate new size and align (but ensure this doesn't shrink the size) */ - m = MAX(a->after->new_size, round_down_size(a->after->new_size + span, 4096)); + m = MAX(a->after->new_size, round_down_size(a->after->new_size + span, context->grain_size)); - xsz = partition_max_size(a->after); + xsz = partition_max_size(context, a->after); if (xsz != UINT64_MAX && m > xsz) m = xsz; - span = charge_size(span, m - a->after->new_size); + span = charge_size(context, span, m - a->after->new_size); a->after->new_size = m; } /* What? Even still some space left (maybe because there was no preceding partition, or it had a * size limit), then let's donate it to whoever wants it. */ - if (span > 0) { - Partition *p; - + if (span > 0) LIST_FOREACH(partitions, p, context->partitions) { uint64_t m, xsz; @@ -824,19 +838,18 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) { continue; assert(p->new_size != UINT64_MAX); - m = MAX(p->new_size, round_down_size(p->new_size + span, 4096)); + m = MAX(p->new_size, round_down_size(p->new_size + span, context->grain_size)); - xsz = partition_max_size(p); + xsz = partition_max_size(context, p); if (xsz != UINT64_MAX && m > xsz) m = xsz; - span = charge_size(span, m - p->new_size); + span = charge_size(context, span, m - p->new_size); p->new_size = m; if (span == 0) break; } - } /* Yuck, still no one? Then make it padding */ if (span > 0 && a->after) { @@ -848,7 +861,6 @@ static int context_grow_partitions_on_free_area(Context *context, FreeArea *a) { } static int context_grow_partitions(Context *context) { - Partition *p; int r; assert(context); @@ -882,7 +894,6 @@ static int context_grow_partitions(Context *context) { static void context_place_partitions(Context *context) { uint64_t partno = 0; - Partition *p; assert(context); @@ -910,7 +921,7 @@ static void context_place_partitions(Context *context) { } else start = context->start; - start = round_up_size(start, 4096); + start = round_up_size(start, context->grain_size); left = a->size; LIST_FOREACH(partitions, p, context->partitions) { @@ -1383,7 +1394,6 @@ static int context_read_definitions( _cleanup_strv_free_ char **files = NULL; Partition *last = NULL; - char **f; int r; assert(context); @@ -1422,6 +1432,8 @@ static int determine_current_padding( struct fdisk_context *c, struct fdisk_table *t, struct fdisk_partition *p, + uint64_t secsz, + uint64_t grainsz, uint64_t *ret) { size_t n_partitions; @@ -1435,8 +1447,8 @@ static int determine_current_padding( return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition has no end!"); offset = fdisk_partition_get_end(p); - assert(offset < UINT64_MAX / 512); - offset *= 512; + assert(offset < UINT64_MAX / secsz); + offset *= secsz; n_partitions = fdisk_table_get_nents(t); for (size_t i = 0; i < n_partitions; i++) { @@ -1454,8 +1466,8 @@ static int determine_current_padding( continue; start = fdisk_partition_get_start(q); - assert(start < UINT64_MAX / 512); - start *= 512; + assert(start < UINT64_MAX / secsz); + start *= secsz; if (start >= offset && (next == UINT64_MAX || next > start)) next = start; @@ -1467,16 +1479,16 @@ static int determine_current_padding( assert(next < UINT64_MAX); next++; /* The last LBA is one sector before the end */ - assert(next < UINT64_MAX / 512); - next *= 512; + assert(next < UINT64_MAX / secsz); + next *= secsz; if (offset > next) return log_error_errno(SYNTHETIC_ERRNO(EIO), "Partition end beyond disk end."); } assert(next >= offset); - offset = round_up_size(offset, 4096); - next = round_down_size(next, 4096); + offset = round_up_size(offset, grainsz); + next = round_down_size(next, grainsz); *ret = LESS_BY(next, offset); /* Saturated subtraction, rounding might have fucked things up */ return 0; @@ -1489,11 +1501,11 @@ static int fdisk_ask_cb(struct fdisk_context *c, struct fdisk_ask *ask, void *da if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_STRING) return -EINVAL; - ids = new(char, ID128_UUID_STRING_MAX); + ids = new(char, SD_ID128_UUID_STRING_MAX); if (!ids) return -ENOMEM; - r = fdisk_ask_string_set_result(ask, id128_to_uuid_string(*(sd_id128_t*) data, ids)); + r = fdisk_ask_string_set_result(ask, sd_id128_to_uuid_string(*(sd_id128_t*) data, ids)); if (r < 0) return r; @@ -1549,6 +1561,8 @@ static int context_load_partition_table( bool from_scratch = false; sd_id128_t disk_uuid; size_t n_partitions; + unsigned long secsz; + uint64_t grainsz; int r; assert(context); @@ -1583,8 +1597,12 @@ static int context_load_partition_table( if (r < 0) return log_error_errno(errno, "Failed to stat block device '%s': %m", node); - if (S_ISREG(st.st_mode) && st.st_size == 0) + if (S_ISREG(st.st_mode) && st.st_size == 0) { + /* User the fallback values if we have no better idea */ + context->sector_size = 512; + context->grain_size = 4096; return /* from_scratch = */ true; + } r = -EINVAL; } @@ -1602,6 +1620,23 @@ static int context_load_partition_table( if (flock(fdisk_get_devfd(c), arg_dry_run ? LOCK_SH : LOCK_EX) < 0) return log_error_errno(errno, "Failed to lock block device: %m"); + /* The offsets/sizes libfdisk returns to us will be in multiple of the sector size of the + * device. This is typically 512, and sometimes 4096. Let's query libfdisk once for it, and then use + * it for all our needs. Note that the values we use ourselves always are in bytes though, thus mean + * the same thing universally. Also note that regardless what kind of sector size is in use we'll + * place partitions at multiples of 4K. */ + secsz = fdisk_get_sector_size(c); + + /* Insist on a power of two, and that it's a multiple of 512, i.e. the traditional sector size. */ + if (secsz < 512 || secsz != 1UL << log2u64(secsz)) + return log_error_errno(errno, "Sector size %lu is not a power of two larger than 512? Refusing.", secsz); + + /* Use at least 4K, and ensure it's a multiple of the sector size, regardless if that is smaller or + * larger */ + grainsz = secsz < 4096 ? 4096 : secsz; + + log_debug("Sector size of device is %lu bytes. Using grain size of %" PRIu64 ".", secsz, grainsz); + switch (arg_empty) { case EMPTY_REFUSE: @@ -1683,7 +1718,7 @@ static int context_load_partition_table( n_partitions = fdisk_table_get_nents(t); for (size_t i = 0; i < n_partitions; i++) { _cleanup_free_ char *label_copy = NULL; - Partition *pp, *last = NULL; + Partition *last = NULL; struct fdisk_partition *p; struct fdisk_parttype *pt; const char *pts, *ids, *label; @@ -1732,12 +1767,12 @@ static int context_load_partition_table( } sz = fdisk_partition_get_size(p); - assert_se(sz <= UINT64_MAX/512); - sz *= 512; + assert(sz <= UINT64_MAX/secsz); + sz *= secsz; start = fdisk_partition_get_start(p); - assert_se(start <= UINT64_MAX/512); - start *= 512; + assert(start <= UINT64_MAX/secsz); + start *= secsz; partno = fdisk_partition_get_partno(p); @@ -1762,7 +1797,7 @@ static int context_load_partition_table( pp->current_partition = p; fdisk_ref_partition(p); - r = determine_current_padding(c, t, p, &pp->current_padding); + r = determine_current_padding(c, t, p, secsz, grainsz, &pp->current_padding); if (r < 0) return r; @@ -1795,7 +1830,7 @@ static int context_load_partition_table( np->current_partition = p; fdisk_ref_partition(p); - r = determine_current_padding(c, t, p, &np->current_padding); + r = determine_current_padding(c, t, p, secsz, grainsz, &np->current_padding); if (r < 0) return r; @@ -1812,26 +1847,26 @@ static int context_load_partition_table( add_initial_free_area: nsectors = fdisk_get_nsectors(c); - assert(nsectors <= UINT64_MAX/512); - nsectors *= 512; + assert(nsectors <= UINT64_MAX/secsz); + nsectors *= secsz; first_lba = fdisk_get_first_lba(c); - assert(first_lba <= UINT64_MAX/512); - first_lba *= 512; + assert(first_lba <= UINT64_MAX/secsz); + first_lba *= secsz; last_lba = fdisk_get_last_lba(c); assert(last_lba < UINT64_MAX); last_lba++; - assert(last_lba <= UINT64_MAX/512); - last_lba *= 512; + assert(last_lba <= UINT64_MAX/secsz); + last_lba *= secsz; assert(last_lba >= first_lba); if (left_boundary == UINT64_MAX) { /* No partitions at all? Then the whole disk is up for grabs. */ - first_lba = round_up_size(first_lba, 4096); - last_lba = round_down_size(last_lba, 4096); + first_lba = round_up_size(first_lba, grainsz); + last_lba = round_down_size(last_lba, grainsz); if (last_lba > first_lba) { r = context_add_free_area(context, last_lba - first_lba, NULL); @@ -1842,9 +1877,9 @@ add_initial_free_area: /* Add space left of first partition */ assert(left_boundary >= first_lba); - first_lba = round_up_size(first_lba, 4096); - left_boundary = round_down_size(left_boundary, 4096); - last_lba = round_down_size(last_lba, 4096); + first_lba = round_up_size(first_lba, grainsz); + left_boundary = round_down_size(left_boundary, grainsz); + last_lba = round_down_size(last_lba, grainsz); if (left_boundary > first_lba) { r = context_add_free_area(context, left_boundary - first_lba, NULL); @@ -1856,17 +1891,17 @@ add_initial_free_area: context->start = first_lba; context->end = last_lba; context->total = nsectors; + context->sector_size = secsz; + context->grain_size = grainsz; context->fdisk_context = TAKE_PTR(c); return from_scratch; } static void context_unload_partition_table(Context *context) { - Partition *p, *next; - assert(context); - LIST_FOREACH_SAFE(partitions, p, next, context->partitions) { + LIST_FOREACH(partitions, p, context->partitions) { /* Entirely remove partitions that have no configuration */ if (PARTITION_IS_FOREIGN(p)) { @@ -1921,9 +1956,9 @@ static int format_size_change(uint64_t from, uint64_t to, char **ret) { if (from == to || to == UINT64_MAX) t = strdup(FORMAT_BYTES(from)); else - t = strjoin(FORMAT_BYTES(from), " ", special_glyph(SPECIAL_GLYPH_ARROW), " ", FORMAT_BYTES(to)); + t = strjoin(FORMAT_BYTES(from), " ", special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), " ", FORMAT_BYTES(to)); } else if (to != UINT64_MAX) - t = strjoin(special_glyph(SPECIAL_GLYPH_ARROW), " ", FORMAT_BYTES(to)); + t = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), " ", FORMAT_BYTES(to)); else { *ret = NULL; return 0; @@ -1951,7 +1986,6 @@ static const char *partition_label(const Partition *p) { static int context_dump_partitions(Context *context, const char *node) { _cleanup_(table_unrefp) Table *t = NULL; uint64_t sum_padding = 0, sum_size = 0; - Partition *p; int r; if ((arg_json_format_flags & JSON_FORMAT_OFF) && context->n_partitions == 0) { @@ -1982,7 +2016,7 @@ static int context_dump_partitions(Context *context, const char *node) { LIST_FOREACH(partitions, p, context->partitions) { _cleanup_free_ char *size_change = NULL, *padding_change = NULL, *partname = NULL; - char uuid_buffer[ID128_UUID_STRING_MAX]; + char uuid_buffer[SD_ID128_UUID_STRING_MAX]; const char *label, *activity = NULL; if (p->dropped) @@ -2128,7 +2162,7 @@ static int partition_hint(const Partition *p, const char *node, char **ret) { else id = p->type_uuid; - buf = strdup(ID128_TO_UUID_STRING(id)); + buf = strdup(SD_ID128_TO_UUID_STRING(id)); done: if (!buf) @@ -2141,7 +2175,7 @@ done: static int context_dump_partition_bar(Context *context, const char *node) { _cleanup_free_ Partition **bar = NULL; _cleanup_free_ size_t *start_array = NULL; - Partition *p, *last = NULL; + Partition *last = NULL; bool z = false; size_t c, j = 0; @@ -2252,7 +2286,7 @@ static int context_dump_partition_bar(Context *context, const char *node) { } static bool context_changed(const Context *context) { - Partition *p; + assert(context); LIST_FOREACH(partitions, p, context->partitions) { if (p->dropped) @@ -2360,7 +2394,7 @@ static int context_discard_range( if (S_ISBLK(st.st_mode)) { uint64_t range[2], end; - range[0] = round_up_size(offset, 512); + range[0] = round_up_size(offset, context->sector_size); if (offset > UINT64_MAX - size) return -ERANGE; @@ -2369,7 +2403,7 @@ static int context_discard_range( if (end <= range[0]) return 0; - range[1] = round_down_size(end - range[0], 512); + range[1] = round_down_size(end - range[0], context->sector_size); if (range[1] <= 0) return 0; @@ -2422,7 +2456,6 @@ static int context_discard_partition(Context *context, Partition *p) { static int context_discard_gap_after(Context *context, Partition *p) { uint64_t gap, next = UINT64_MAX; - Partition *q; int r; assert(context); @@ -2480,7 +2513,6 @@ static int context_discard_gap_after(Context *context, Partition *p) { } static int context_wipe_and_discard(Context *context, bool from_scratch) { - Partition *p; int r; assert(context); @@ -2519,6 +2551,7 @@ static int context_wipe_and_discard(Context *context, bool from_scratch) { } static int partition_encrypt( + Context *context, Partition *p, const char *node, struct crypt_device **ret_cd, @@ -2532,6 +2565,7 @@ static int partition_encrypt( sd_id128_t uuid; int r; + assert(context); assert(p); assert(p->encrypt != ENCRYPT_OFF); @@ -2574,12 +2608,12 @@ static int partition_encrypt( CRYPT_LUKS2, "aes", "xts-plain64", - ID128_TO_UUID_STRING(uuid), + SD_ID128_TO_UUID_STRING(uuid), volume_key, volume_key_size, &(struct crypt_params_luks2) { .label = strempty(p->new_label), - .sector_size = 512U, + .sector_size = context->sector_size, }); if (r < 0) return log_error_errno(r, "Failed to LUKS2 format future partition: %m"); @@ -2606,7 +2640,7 @@ static int partition_encrypt( uint16_t pcr_bank, primary_alg; int keyslot; - r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg); + r = tpm2_seal(arg_tpm2_device, arg_tpm2_pcr_mask, NULL, &secret, &secret_size, &blob, &blob_size, &hash, &hash_size, &pcr_bank, &primary_alg); if (r < 0) return log_error_errno(r, "Failed to seal to TPM2: %m"); @@ -2628,7 +2662,7 @@ static int partition_encrypt( if (keyslot < 0) return log_error_errno(keyslot, "Failed to add new TPM2 key to %s: %m", node); - r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, &v); + r = tpm2_make_luks2_json(keyslot, arg_tpm2_pcr_mask, pcr_bank, primary_alg, blob, blob_size, hash, hash_size, 0, &v); if (r < 0) return log_error_errno(r, "Failed to prepare TPM2 JSON token object: %m"); @@ -2696,7 +2730,6 @@ static int deactivate_luks(struct crypt_device *cd, const char *node) { } static int context_copy_blocks(Context *context) { - Partition *p; int whole_fd = -1, r; assert(context); @@ -2735,7 +2768,7 @@ static int context_copy_blocks(Context *context) { if (r < 0) return log_error_errno(r, "Failed to lock loopback device: %m"); - r = partition_encrypt(p, d->node, &cd, &encrypted, &encrypted_dev_fd); + r = partition_encrypt(context, p, d->node, &cd, &encrypted, &encrypted_dev_fd); if (r < 0) return log_error_errno(r, "Failed to encrypt device: %m"); @@ -2782,7 +2815,6 @@ static int context_copy_blocks(Context *context) { } static int do_copy_files(Partition *p, const char *fs) { - char **source, **target; int r; assert(p); @@ -2879,7 +2911,6 @@ static int do_copy_files(Partition *p, const char *fs) { } static int do_make_directories(Partition *p, const char *fs) { - char **d; int r; assert(p); @@ -2946,7 +2977,6 @@ static int partition_populate(Partition *p, const char *node) { } static int context_mkfs(Context *context) { - Partition *p; int fd = -1, r; assert(context); @@ -2988,7 +3018,7 @@ static int context_mkfs(Context *context) { return log_error_errno(r, "Failed to lock loopback device: %m"); if (p->encrypt != ENCRYPT_OFF) { - r = partition_encrypt(p, d->node, &cd, &encrypted, &encrypted_dev_fd); + r = partition_encrypt(context, p, d->node, &cd, &encrypted, &encrypted_dev_fd); if (r < 0) return log_error_errno(r, "Failed to encrypt device: %m"); @@ -3064,7 +3094,6 @@ static int partition_acquire_uuid(Context *context, Partition *p, sd_id128_t *re } result; uint64_t k = 0; - Partition *q; int r; assert(context); @@ -3148,7 +3177,6 @@ static int partition_acquire_label(Context *context, Partition *p, char **ret) { for (;;) { const char *ll = label ?: prefix; bool retry = false; - Partition *q; LIST_FOREACH(partitions, q, context->partitions) { if (p == q) @@ -3180,7 +3208,6 @@ static int partition_acquire_label(Context *context, Partition *p, char **ret) { } static int context_acquire_partition_uuids_and_labels(Context *context) { - Partition *p; int r; assert(context); @@ -3254,7 +3281,7 @@ static uint64_t partition_merge_flags(Partition *p) { if (gpt_partition_type_knows_no_auto(p->type_uuid)) SET_FLAG(f, GPT_FLAG_NO_AUTO, p->no_auto); else { - char buffer[ID128_UUID_STRING_MAX]; + char buffer[SD_ID128_UUID_STRING_MAX]; log_warning("Configured NoAuto=%s for partition type '%s' that doesn't support it, ignoring.", yes_no(p->no_auto), gpt_partition_type_uuid_to_string_harder(p->type_uuid, buffer)); @@ -3265,7 +3292,7 @@ static uint64_t partition_merge_flags(Partition *p) { if (gpt_partition_type_knows_read_only(p->type_uuid)) SET_FLAG(f, GPT_FLAG_READ_ONLY, p->read_only); else { - char buffer[ID128_UUID_STRING_MAX]; + char buffer[SD_ID128_UUID_STRING_MAX]; log_warning("Configured ReadOnly=%s for partition type '%s' that doesn't support it, ignoring.", yes_no(p->read_only), gpt_partition_type_uuid_to_string_harder(p->type_uuid, buffer)); @@ -3276,7 +3303,7 @@ static uint64_t partition_merge_flags(Partition *p) { if (gpt_partition_type_knows_growfs(p->type_uuid)) SET_FLAG(f, GPT_FLAG_GROWFS, p->growfs); else { - char buffer[ID128_UUID_STRING_MAX]; + char buffer[SD_ID128_UUID_STRING_MAX]; log_warning("Configured GrowFileSystem=%s for partition type '%s' that doesn't support it, ignoring.", yes_no(p->growfs), gpt_partition_type_uuid_to_string_harder(p->type_uuid, buffer)); @@ -3287,7 +3314,6 @@ static uint64_t partition_merge_flags(Partition *p) { } static int context_mangle_partitions(Context *context) { - Partition *p; int r; assert(context); @@ -3307,13 +3333,13 @@ static int context_mangle_partitions(Context *context) { if (p->new_size != p->current_size) { assert(p->new_size >= p->current_size); - assert(p->new_size % 512 == 0); + assert(p->new_size % context->sector_size == 0); r = fdisk_partition_size_explicit(p->current_partition, true); if (r < 0) return log_error_errno(r, "Failed to enable explicit sizing: %m"); - r = fdisk_partition_set_size(p->current_partition, p->new_size / 512); + r = fdisk_partition_set_size(p->current_partition, p->new_size / context->sector_size); if (r < 0) return log_error_errno(r, "Failed to grow partition: %m"); @@ -3324,7 +3350,7 @@ static int context_mangle_partitions(Context *context) { if (!sd_id128_equal(p->new_uuid, p->current_uuid)) { assert(!sd_id128_is_null(p->new_uuid)); - r = fdisk_partition_set_uuid(p->current_partition, ID128_TO_UUID_STRING(p->new_uuid)); + r = fdisk_partition_set_uuid(p->current_partition, SD_ID128_TO_UUID_STRING(p->new_uuid)); if (r < 0) return log_error_errno(r, "Failed to set partition UUID: %m"); @@ -3353,8 +3379,8 @@ static int context_mangle_partitions(Context *context) { _cleanup_(fdisk_unref_parttypep) struct fdisk_parttype *t = NULL; assert(!p->new_partition); - assert(p->offset % 512 == 0); - assert(p->new_size % 512 == 0); + assert(p->offset % context->sector_size == 0); + assert(p->new_size % context->sector_size == 0); assert(!sd_id128_is_null(p->new_uuid)); assert(p->new_label); @@ -3362,7 +3388,7 @@ static int context_mangle_partitions(Context *context) { if (!t) return log_oom(); - r = fdisk_parttype_set_typestr(t, ID128_TO_UUID_STRING(p->type_uuid)); + r = fdisk_parttype_set_typestr(t, SD_ID128_TO_UUID_STRING(p->type_uuid)); if (r < 0) return log_error_errno(r, "Failed to initialize partition type: %m"); @@ -3378,11 +3404,11 @@ static int context_mangle_partitions(Context *context) { if (r < 0) return log_error_errno(r, "Failed to enable explicit sizing: %m"); - r = fdisk_partition_set_start(q, p->offset / 512); + r = fdisk_partition_set_start(q, p->offset / context->sector_size); if (r < 0) return log_error_errno(r, "Failed to position partition: %m"); - r = fdisk_partition_set_size(q, p->new_size / 512); + r = fdisk_partition_set_size(q, p->new_size / context->sector_size); if (r < 0) return log_error_errno(r, "Failed to grow partition: %m"); @@ -3390,7 +3416,7 @@ static int context_mangle_partitions(Context *context) { if (r < 0) return log_error_errno(r, "Failed to set partition number: %m"); - r = fdisk_partition_set_uuid(q, ID128_TO_UUID_STRING(p->new_uuid)); + r = fdisk_partition_set_uuid(q, SD_ID128_TO_UUID_STRING(p->new_uuid)); if (r < 0) return log_error_errno(r, "Failed to set partition UUID: %m"); @@ -3554,7 +3580,6 @@ static int context_read_seed(Context *context, const char *root) { } static int context_factory_reset(Context *context, bool from_scratch) { - Partition *p; size_t n = 0; int r; @@ -3603,8 +3628,6 @@ static int context_factory_reset(Context *context, bool from_scratch) { } static int context_can_factory_reset(Context *context) { - Partition *p; - assert(context); LIST_FOREACH(partitions, p, context->partitions) @@ -3919,7 +3942,6 @@ static int context_open_copy_block_paths( const char *root, dev_t restrict_devno) { - Partition *p; int r; assert(context); @@ -4511,9 +4533,8 @@ static int acquire_root_devno( } static int find_root(char **ret, int *ret_fd) { - const char *p; - int r; _cleanup_free_ char *device = NULL; + int r; assert(ret); assert(ret_fd); @@ -4746,10 +4767,11 @@ done: } static int determine_auto_size(Context *c) { - uint64_t sum = round_up_size(GPT_METADATA_SIZE, 4096); - Partition *p; + uint64_t sum; - assert_se(c); + assert(c); + + sum = round_up_size(GPT_METADATA_SIZE, 4096); LIST_FOREACH(partitions, p, c->partitions) { uint64_t m; @@ -4757,7 +4779,7 @@ static int determine_auto_size(Context *c) { if (p->dropped) continue; - m = partition_min_size_with_padding(p); + m = partition_min_size_with_padding(c, p); if (m > UINT64_MAX - sum) return log_error_errno(SYNTHETIC_ERRNO(EOVERFLOW), "Image would grow too large, refusing."); @@ -4929,7 +4951,7 @@ static int run(int argc, char *argv[]) { /* Flush out everything again, and let's grow the file first, then start fresh */ context_unload_partition_table(context); - assert_se(arg_size != UINT64_MAX); + assert(arg_size != UINT64_MAX); r = resize_backing_fd( node, &backing_fd, diff --git a/src/partition/test-repart.sh b/src/partition/test-repart.sh index d50a79a15..178379312 100755 --- a/src/partition/test-repart.sh +++ b/src/partition/test-repart.sh @@ -199,6 +199,11 @@ $D/zzz7 : start= 6291416, size= 98304, type=0FC63DAF-8483-4772-8E79-3D EOF LOOP="$(losetup -P --show --find "$D/zzz")" + while : ; do + test -e "$LOOP" && break + sleep .2 + done + VOLUME="test-repart-$RANDOM" touch "$D/empty-password" diff --git a/src/portable/portable.c b/src/portable/portable.c index 0e6461ba9..0c10b161f 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -58,7 +58,6 @@ static bool prefix_match(const char *unit, const char *prefix) { static bool unit_match(const char *unit, char **matches) { const char *dot; - char **i; dot = strrchr(unit, '.'); if (!dot) @@ -182,7 +181,6 @@ static int extract_now( _cleanup_close_ int os_release_fd = -1; _cleanup_free_ char *os_release_path = NULL; const char *os_release_id; - char **i; int r; /* Extracts the metadata from a directory tree 'where'. Extracts two kinds of information: the /etc/os-release @@ -233,7 +231,7 @@ static int extract_now( /* Then, send unit file data to the parent (or/and add it to the hashmap). For that we use our usual unit * discovery logic. Note that we force looking inside of /lib/systemd/system/ for units too, as we mightbe * compiled for a split-usr system but the image might be a legacy-usr one. */ - r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, where); + r = lookup_paths_init(&paths, LOOKUP_SCOPE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, where); if (r < 0) return log_debug_errno(r, "Failed to acquire lookup paths: %m"); @@ -253,6 +251,7 @@ static int extract_now( FOREACH_DIRENT(de, d, return log_debug_errno(errno, "Failed to read directory: %m")) { _cleanup_(portable_metadata_unrefp) PortableMetadata *m = NULL; + _cleanup_(mac_selinux_freep) char *con = NULL; _cleanup_close_ int fd = -1; if (!unit_name_is_valid(de->d_name, UNIT_NAME_ANY)) @@ -274,16 +273,16 @@ static int extract_now( continue; } - if (socket_fd >= 0) { - _cleanup_(mac_selinux_freep) char *con = NULL; #if HAVE_SELINUX - /* The units will be copied on the host's filesystem, so if they had a SELinux label - * we have to preserve it. Copy it out so that it can be applied later. */ + /* The units will be copied on the host's filesystem, so if they had a SELinux label + * we have to preserve it. Copy it out so that it can be applied later. */ - r = fgetfilecon_raw(fd, &con); - if (r < 0 && errno != ENODATA) - log_debug_errno(errno, "Failed to get SELinux file context from '%s', ignoring: %m", de->d_name); + r = fgetfilecon_raw(fd, &con); + if (r < 0 && errno != ENODATA) + log_debug_errno(errno, "Failed to get SELinux file context from '%s', ignoring: %m", de->d_name); #endif + + if (socket_fd >= 0) { struct iovec iov[] = { IOVEC_MAKE_STRING(de->d_name), IOVEC_MAKE((char *)"\0", sizeof(char)), @@ -295,7 +294,7 @@ static int extract_now( return log_debug_errno(r, "Failed to send unit metadata to parent: %m"); } - m = portable_metadata_new(de->d_name, NULL, NULL, fd); + m = portable_metadata_new(de->d_name, where, con, fd); if (!m) return -ENOMEM; fd = -1; @@ -336,10 +335,16 @@ static int portable_extract_by_path( r = loop_device_make_by_path(path, O_RDONLY, LO_FLAGS_PARTSCAN, &d); if (r == -EISDIR) { + _cleanup_free_ char *image_name = NULL; + /* We can't turn this into a loop-back block device, and this returns EISDIR? Then this is a directory * tree and not a raw device. It's easy then. */ - r = extract_now(path, matches, NULL, path_is_extension, -1, &os_release, &unit_files); + r = path_extract_filename(path, &image_name); + if (r < 0) + return log_error_errno(r, "Failed to extract image name from path '%s': %m", path); + + r = extract_now(path, matches, image_name, path_is_extension, -1, &os_release, &unit_files); if (r < 0) return r; @@ -528,8 +533,6 @@ static int extract_image_and_extensions( return r; if (!strv_isempty(extension_image_paths)) { - char **p; - extension_images = ordered_hashmap_new(&image_hash_ops); if (!extension_images) return -ENOMEM; @@ -604,7 +607,7 @@ static int extract_image_and_extensions( continue; /* We need to keep the fd valid, to return the PortableMetadata to the caller. */ - extension_release_fd = fd_reopen(extension_release_meta->fd, O_CLOEXEC); + extension_release_fd = fd_reopen(extension_release_meta->fd, O_CLOEXEC|O_RDONLY); if (extension_release_fd < 0) return extension_release_fd; @@ -898,6 +901,10 @@ static const char *root_setting_from_image(ImageType type) { return IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "RootDirectory=" : "RootImage="; } +static const char *extension_setting_from_image(ImageType type) { + return IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) ? "ExtensionDirectories=" : "ExtensionImages="; +} + static int make_marker_text(const char *image_path, OrderedHashmap *extension_images, char **ret_text) { _cleanup_free_ char *text = NULL, *escaped_image_path = NULL; Image *ext; @@ -944,7 +951,6 @@ static int install_chroot_dropin( size_t *n_changes) { _cleanup_free_ char *text = NULL, *dropin = NULL; - Image *ext; int r; assert(image_path); @@ -962,6 +968,7 @@ static int install_chroot_dropin( if (endswith(m->name, ".service")) { const char *os_release_source, *root_type; _cleanup_free_ char *base_name = NULL; + Image *ext; root_type = root_setting_from_image(type); @@ -988,7 +995,7 @@ static int install_chroot_dropin( if (m->image_path && !path_equal(m->image_path, image_path)) ORDERED_HASHMAP_FOREACH(ext, extension_images) - if (!strextend(&text, "ExtensionImages=", ext->path, "\n")) + if (!strextend(&text, extension_setting_from_image(ext->type), ext->path, "\n")) return -ENOMEM; } @@ -1041,14 +1048,14 @@ static int install_profile_dropin( r = copy_file_atomic(from, dropin, 0644, 0, 0, COPY_REFLINK); if (r < 0) - return log_debug_errno(r, "Failed to copy %s %s %s: %m", from, special_glyph(SPECIAL_GLYPH_ARROW), dropin); + return log_debug_errno(r, "Failed to copy %s %s %s: %m", from, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), dropin); (void) portable_changes_add(changes, n_changes, PORTABLE_COPY, dropin, from); } else { if (symlink(from, dropin) < 0) - return log_debug_errno(errno, "Failed to link %s %s %s: %m", from, special_glyph(SPECIAL_GLYPH_ARROW), dropin); + return log_debug_errno(errno, "Failed to link %s %s %s: %m", from, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), dropin); (void) portable_changes_add(changes, n_changes, PORTABLE_SYMLINK, dropin, from); } @@ -1218,7 +1225,7 @@ static int install_image_symlink( (void) mkdir_parents(sl, 0755); if (symlink(image_path, sl) < 0) - return log_debug_errno(errno, "Failed to link %s %s %s: %m", image_path, special_glyph(SPECIAL_GLYPH_ARROW), sl); + return log_debug_errno(errno, "Failed to link %s %s %s: %m", image_path, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), sl); (void) portable_changes_add(changes, n_changes, PORTABLE_SYMLINK, sl, image_path); return 0; @@ -1250,8 +1257,6 @@ static int install_image_and_extensions_symlinks( } static bool prefix_matches_compatible(char **matches, char **valid_prefixes) { - char **m; - /* Checks if all 'matches' are included in the list of 'valid_prefixes' */ STRV_FOREACH(m, matches) @@ -1335,12 +1340,12 @@ int portable_attach( strempty(extensions_joined)); } - r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); + r = lookup_paths_init(&paths, LOOKUP_SCOPE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); if (r < 0) return r; HASHMAP_FOREACH(item, unit_files) { - r = unit_file_exists(UNIT_FILE_SYSTEM, &paths, item->name); + r = unit_file_exists(LOOKUP_SCOPE_SYSTEM, &paths, item->name); if (r < 0) return sd_bus_error_set_errnof(error, r, "Failed to determine whether unit '%s' exists on the host: %m", item->name); if (!FLAGS_SET(flags, PORTABLE_REATTACH) && r > 0) @@ -1369,7 +1374,6 @@ int portable_attach( static bool marker_matches_images(const char *marker, const char *name_or_path, char **extension_image_paths) { _cleanup_strv_free_ char **root_and_extensions = NULL; - char **image_name_or_path; const char *a; int r; @@ -1425,17 +1429,28 @@ static bool marker_matches_images(const char *marker, const char *name_or_path, size_t l; /* We shall match against a path. Let's ignore any prefix here though, as often there are many ways to - * reach the same file. However, in this mode, let's validate any file suffix. */ + * reach the same file. However, in this mode, let's validate any file suffix. + * But also ensure that we don't fail if both components don't have a '/' at all + * (strcspn returns the full length of the string in that case, which might not + * match as the versions might differ). */ l = strcspn(a, "/"); b = last_path_component(*image_name_or_path); - if (strcspn(b, "/") != l) + if ((a[l] != '/') != !strchr(b, '/')) /* One is a directory, the other is not */ + return false; + + if (a[l] != 0 && strcspn(b, "/") != l) return false; underscore = strchr(b, '_'); if (underscore) l = underscore - b; + else { /* Either component could be versioned */ + underscore = strchr(a, '_'); + if (underscore) + l = underscore - a; + } if (!strneq(a, b, l)) return false; @@ -1516,6 +1531,7 @@ int portable_detach( _cleanup_(lookup_paths_free) LookupPaths paths = {}; _cleanup_set_free_ Set *unit_files = NULL, *markers = NULL; + _cleanup_free_ char *extensions = NULL; _cleanup_closedir_ DIR *d = NULL; const char *where, *item; int ret = 0; @@ -1523,7 +1539,7 @@ int portable_detach( assert(name_or_path); - r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); + r = lookup_paths_init(&paths, LOOKUP_SCOPE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); if (r < 0) return r; @@ -1557,7 +1573,7 @@ int portable_detach( if (r == 0) continue; - r = unit_file_lookup_state(UNIT_FILE_SYSTEM, &paths, de->d_name, &state); + r = unit_file_lookup_state(LOOKUP_SCOPE_SYSTEM, &paths, de->d_name, &state); if (r < 0) return log_debug_errno(r, "Failed to determine unit file state of '%s': %m", de->d_name); if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_RUNTIME, UNIT_FILE_LINKED_RUNTIME)) @@ -1595,7 +1611,6 @@ int portable_detach( SET_FOREACH(item, unit_files) { _cleanup_free_ char *md = NULL; - const char *suffix; if (unlinkat(dirfd(d), item, 0) < 0) { log_debug_errno(errno, "Can't remove unit file %s/%s: %m", where, item); @@ -1671,13 +1686,23 @@ int portable_detach( return ret; not_found: - log_debug("No unit files associated with '%s' found. Image not attached?", name_or_path); - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "No unit files associated with '%s' found. Image not attached?", name_or_path); + extensions = strv_join(extension_image_paths, ", "); + if (!extensions) + return -ENOMEM; + + r = sd_bus_error_setf(error, + BUS_ERROR_NO_SUCH_UNIT, + "No unit files associated with '%s%s%s' found attached to the system. Image not attached?", + name_or_path, + isempty(extensions) ? "" : "' or any of its extensions '", + isempty(extensions) ? "" : extensions); + return log_debug_errno(r, "%s", error->message); } static int portable_get_state_internal( sd_bus *bus, const char *name_or_path, + char **extension_image_paths, PortableFlags flags, PortableState *ret, sd_bus_error *error) { @@ -1692,7 +1717,7 @@ static int portable_get_state_internal( assert(name_or_path); assert(ret); - r = lookup_paths_init(&paths, UNIT_FILE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); + r = lookup_paths_init(&paths, LOOKUP_SCOPE_SYSTEM, LOOKUP_PATHS_SPLIT_USR, NULL); if (r < 0) return r; @@ -1722,13 +1747,13 @@ static int portable_get_state_internal( if (!IN_SET(de->d_type, DT_LNK, DT_REG)) continue; - r = test_chroot_dropin(d, where, de->d_name, name_or_path, NULL, NULL); + r = test_chroot_dropin(d, where, de->d_name, name_or_path, extension_image_paths, NULL); if (r < 0) return r; if (r == 0) continue; - r = unit_file_lookup_state(UNIT_FILE_SYSTEM, &paths, de->d_name, &state); + r = unit_file_lookup_state(LOOKUP_SCOPE_SYSTEM, &paths, de->d_name, &state); if (r < 0) return log_debug_errno(r, "Failed to determine unit file state of '%s': %m", de->d_name); if (!IN_SET(state, UNIT_FILE_STATIC, UNIT_FILE_DISABLED, UNIT_FILE_LINKED, UNIT_FILE_LINKED_RUNTIME)) @@ -1755,6 +1780,7 @@ static int portable_get_state_internal( int portable_get_state( sd_bus *bus, const char *name_or_path, + char **extension_image_paths, PortableFlags flags, PortableState *ret, sd_bus_error *error) { @@ -1768,12 +1794,12 @@ int portable_get_state( /* We look for matching units twice: once in the regular directories, and once in the runtime directories — but * the latter only if we didn't find anything in the former. */ - r = portable_get_state_internal(bus, name_or_path, flags & ~PORTABLE_RUNTIME, &state, error); + r = portable_get_state_internal(bus, name_or_path, extension_image_paths, flags & ~PORTABLE_RUNTIME, &state, error); if (r < 0) return r; if (state == PORTABLE_DETACHED) { - r = portable_get_state_internal(bus, name_or_path, flags | PORTABLE_RUNTIME, &state, error); + r = portable_get_state_internal(bus, name_or_path, extension_image_paths, flags | PORTABLE_RUNTIME, &state, error); if (r < 0) return r; } diff --git a/src/portable/portable.h b/src/portable/portable.h index dff87857b..c6061cc58 100644 --- a/src/portable/portable.h +++ b/src/portable/portable.h @@ -70,7 +70,7 @@ int portable_extract(const char *image, char **matches, char **extension_image_p int portable_attach(sd_bus *bus, const char *name_or_path, char **matches, const char *profile, char **extension_images, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error); int portable_detach(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableChange **changes, size_t *n_changes, sd_bus_error *error); -int portable_get_state(sd_bus *bus, const char *name_or_path, PortableFlags flags, PortableState *ret, sd_bus_error *error); +int portable_get_state(sd_bus *bus, const char *name_or_path, char **extension_image_paths, PortableFlags flags, PortableState *ret, sd_bus_error *error); int portable_get_profiles(char ***ret); diff --git a/src/portable/portablectl.c b/src/portable/portablectl.c index ee9e3732d..9fb1cdffe 100644 --- a/src/portable/portablectl.c +++ b/src/portable/portablectl.c @@ -90,7 +90,6 @@ static int determine_image(const char *image, bool permit_non_existing, char **r } static int attach_extensions_to_message(sd_bus_message *m, char **extensions) { - char **p; int r; assert(m); @@ -513,12 +512,12 @@ static int print_changes(sd_bus_message *m) { break; if (streq(type, "symlink")) - log_info("Created symlink %s %s %s.", path, special_glyph(SPECIAL_GLYPH_ARROW), source); + log_info("Created symlink %s %s %s.", path, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), source); else if (streq(type, "copy")) { if (isempty(source)) log_info("Copied %s.", path); else - log_info("Copied %s %s %s.", source, special_glyph(SPECIAL_GLYPH_ARROW), path); + log_info("Copied %s %s %s.", source, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), path); } else if (streq(type, "unlink")) log_info("Removed %s.", path); else if (streq(type, "write")) @@ -1121,11 +1120,11 @@ static int set_limit(int argc, char *argv[], void *userdata) { } static int is_image_attached(int argc, char *argv[], void *userdata) { - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_free_ char *image = NULL; - const char *state; + const char *state, *method; int r; r = determine_image(argv[1], true, &image); @@ -1136,9 +1135,29 @@ static int is_image_attached(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = bus_call_method(bus, bus_portable_mgr, "GetImageState", &error, &reply, "s", image); + method = strv_isempty(arg_extension_images) ? "GetImageState" : "GetImageStateWithExtensions"; + + r = bus_message_new_method_call(bus, &m, bus_portable_mgr, method); if (r < 0) - return log_error_errno(r, "Failed to get image state: %s", bus_error_message(&error, r)); + return bus_log_create_error(r); + + r = sd_bus_message_append(m, "s", image); + if (r < 0) + return bus_log_create_error(r); + + r = attach_extensions_to_message(m, arg_extension_images); + if (r < 0) + return r; + + if (!strv_isempty(arg_extension_images)) { + r = sd_bus_message_append(m, "t", 0); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_call(bus, m, 0, &error, &reply); + if (r < 0) + return log_error_errno(r, "%s failed: %s", method, bus_error_message(&error, r)); r = sd_bus_message_read(reply, "s", &state); if (r < 0) @@ -1154,7 +1173,6 @@ static int dump_profiles(void) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; _cleanup_strv_free_ char **l = NULL; - char **i; int r; r = acquire_bus(&bus); diff --git a/src/portable/portabled-bus.c b/src/portable/portabled-bus.c index db71057bb..8f8a8fbdc 100644 --- a/src/portable/portabled-bus.c +++ b/src/portable/portabled-bus.c @@ -169,6 +169,7 @@ static int method_list_images(sd_bus_message *message, void *userdata, sd_bus_er r = portable_get_state( sd_bus_message_get_bus(message), image->path, + NULL, 0, &state, &error_state); @@ -225,6 +226,7 @@ static int method_get_image_metadata(sd_bus_message *message, void *userdata, sd } static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **extension_images = NULL; const char *name_or_path; PortableState state; int r; @@ -235,9 +237,28 @@ static int method_get_image_state(sd_bus_message *message, void *userdata, sd_bu if (r < 0) return r; + if (sd_bus_message_is_method_call(message, NULL, "GetImageStateWithExtensions")) { + uint64_t input_flags = 0; + + r = sd_bus_message_read_strv(message, &extension_images); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "t", &input_flags); + if (r < 0) + return r; + + /* No flags are supported by this method for now. */ + if (input_flags != 0) + return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, + "Invalid 'flags' parameter '%" PRIu64 "'", + input_flags); + } + r = portable_get_state( sd_bus_message_get_bus(message), name_or_path, + extension_images, 0, &state, error); @@ -429,6 +450,13 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_RESULT("s", state), method_get_image_state, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("GetImageStateWithExtensions", + SD_BUS_ARGS("s", image, + "as", extensions, + "t", flags), + SD_BUS_RESULT("s", state), + method_get_image_state, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_ARGS("AttachImage", SD_BUS_ARGS("s", image, "as", matches, diff --git a/src/portable/portabled-image-bus.c b/src/portable/portabled-image-bus.c index 7bbe4663f..3fe0eca4c 100644 --- a/src/portable/portabled-image-bus.c +++ b/src/portable/portabled-image-bus.c @@ -258,6 +258,7 @@ static int bus_image_method_get_state( void *userdata, sd_bus_error *error) { + _cleanup_strv_free_ char **extension_images = NULL; Image *image = userdata; PortableState state; int r; @@ -265,9 +266,28 @@ static int bus_image_method_get_state( assert(message); assert(image); + if (sd_bus_message_is_method_call(message, NULL, "GetStateWithExtensions")) { + uint64_t input_flags = 0; + + r = sd_bus_message_read_strv(message, &extension_images); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "t", &input_flags); + if (r < 0) + return r; + + /* No flags are supported by this method for now. */ + if (input_flags != 0) + return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, + "Invalid 'flags' parameter '%" PRIu64 "'", + input_flags); + } + r = portable_get_state( sd_bus_message_get_bus(message), image->path, + extension_images, 0, &state, error); @@ -498,6 +518,7 @@ int bus_image_common_remove( r = portable_get_state( sd_bus_message_get_bus(message), image->path, + NULL, 0, &state, error); @@ -883,6 +904,12 @@ const sd_bus_vtable image_vtable[] = { SD_BUS_RESULT("s", state), bus_image_method_get_state, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD_WITH_ARGS("GetStateWithExtensions", + SD_BUS_ARGS("as", extensions, + "t", flags), + SD_BUS_RESULT("s", state), + bus_image_method_get_state, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD_WITH_ARGS("Attach", SD_BUS_ARGS("as", matches, "s", profile, diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c index 7724e0365..b265b7f85 100644 --- a/src/random-seed/random-seed.c +++ b/src/random-seed/random-seed.c @@ -26,6 +26,7 @@ #include "random-util.h" #include "string-util.h" #include "sync-util.h" +#include "sha256.h" #include "util.h" #include "xattr-util.h" @@ -103,12 +104,13 @@ static CreditEntropy may_credit(int seed_fd) { } static int run(int argc, char *argv[]) { + bool read_seed_file, write_seed_file, synchronous, hashed_old_seed = false; _cleanup_close_ int seed_fd = -1, random_fd = -1; - bool read_seed_file, write_seed_file, synchronous; _cleanup_free_ void* buf = NULL; + struct sha256_ctx hash_state; size_t buf_size; struct stat st; - ssize_t k; + ssize_t k, l; int r; log_setup(); @@ -211,6 +213,16 @@ static int run(int argc, char *argv[]) { else { CreditEntropy lets_credit; + /* If we're going to later write out a seed file, initialize a hash state with + * the contents of the seed file we just read, so that the new one can't regress + * in entropy. */ + if (write_seed_file) { + sha256_init_ctx(&hash_state); + sha256_process_bytes(&k, sizeof(k), &hash_state); /* Hash length to distinguish from new seed. */ + sha256_process_bytes(buf, k, &hash_state); + hashed_old_seed = true; + } + (void) lseek(seed_fd, 0, SEEK_SET); lets_credit = may_credit(seed_fd); @@ -251,7 +263,7 @@ static int run(int argc, char *argv[]) { * ourselves the mode and owner should be correct anyway. */ r = fchmod_and_chown(seed_fd, 0600, 0, 0); if (r < 0) - return log_error_errno(r, "Failed to adjust seed file ownership and access mode."); + return log_error_errno(r, "Failed to adjust seed file ownership and access mode: %m"); /* Let's make this whole job asynchronous, i.e. let's make ourselves a barrier for * proper initialization of the random pool. */ @@ -277,6 +289,18 @@ static int run(int argc, char *argv[]) { "Got EOF while reading from /dev/urandom."); } + /* If we previously read in a seed file, then hash the new seed into the old one, + * and replace the last 32 bytes of the seed with the hash output, so that the + * new seed file can't regress in entropy. */ + if (hashed_old_seed) { + uint8_t hash[32]; + sha256_process_bytes(&k, sizeof(k), &hash_state); /* Hash length to distinguish from old seed. */ + sha256_process_bytes(buf, k, &hash_state); + sha256_finish_ctx(&hash_state, hash); + l = MIN((size_t)k, sizeof(hash)); + memcpy((uint8_t *)buf + k - l, hash, l); + } + r = loop_write(seed_fd, buf, (size_t) k, false); if (r < 0) return log_error_errno(r, "Failed to write new random seed file: %m"); diff --git a/src/resolve/fuzz-etc-hosts.c b/src/resolve/fuzz-etc-hosts.c new file mode 100644 index 000000000..050c85dab --- /dev/null +++ b/src/resolve/fuzz-etc-hosts.c @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "fd-util.h" +#include "fuzz.h" +#include "resolved-etc-hosts.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_(etc_hosts_free) EtcHosts h = {}; + + if (!getenv("SYSTEMD_LOG_LEVEL")) + log_set_max_level(LOG_CRIT); + + f = data_to_file(data, size); + assert_se(f); + + (void) etc_hosts_parse(&h, f); + + return 0; +} diff --git a/src/resolve/meson.build b/src/resolve/meson.build index 770ed77cf..b2da249c8 100644 --- a/src/resolve/meson.build +++ b/src/resolve/meson.build @@ -70,7 +70,6 @@ systemd_resolved_sources = files( 'resolved-socket-graveyard.h', 'resolved-varlink.c', 'resolved-varlink.h', - 'resolved.c', ) resolvectl_sources = files( @@ -176,40 +175,48 @@ custom_target( ############################################################ tests += [ - [['src/resolve/test-resolve-tables.c'], + [files('test-resolve-tables.c'), [libsystemd_resolve_core, libshared], [lib_openssl_or_gcrypt, libm]], - [['src/resolve/test-dns-packet.c'], + [files('test-dns-packet.c'), [libsystemd_resolve_core, libshared], [lib_openssl_or_gcrypt, libm]], - [['src/resolve/test-resolved-etc-hosts.c', - 'src/resolve/resolved-etc-hosts.c', - 'src/resolve/resolved-etc-hosts.h'], + [files('test-resolved-etc-hosts.c', + 'resolved-etc-hosts.c', + 'resolved-etc-hosts.h'), [libsystemd_resolve_core, libshared], [lib_openssl_or_gcrypt, libm]], - [['src/resolve/test-resolved-packet.c'], + [files('test-resolved-packet.c'), [libsystemd_resolve_core, libshared], [lib_openssl_or_gcrypt, libm]], - [['src/resolve/test-dnssec.c'], + [files('test-resolved-stream.c') + + basic_dns_sources + systemd_resolved_sources, + [libshared], + [lib_openssl_or_gcrypt, + libm] + + systemd_resolved_dependencies, + resolve_includes], + + [files('test-dnssec.c'), [libsystemd_resolve_core, libshared], [lib_openssl_or_gcrypt, libm], [], 'HAVE_OPENSSL_OR_GCRYPT'], - [['src/resolve/test-dnssec-complex.c'], + [files('test-dnssec-complex.c'), [libsystemd_resolve_core, libshared], [lib_openssl_or_gcrypt, @@ -218,9 +225,18 @@ tests += [ ] fuzzers += [ - [['src/resolve/fuzz-dns-packet.c'], + [files('fuzz-dns-packet.c'), + [libsystemd_resolve_core, + libshared], + [lib_openssl_or_gcrypt, + libm]], + [files('fuzz-etc-hosts.c', + 'resolved-etc-hosts.c', + 'resolved-etc-hosts.h'), [libsystemd_resolve_core, libshared], [lib_openssl_or_gcrypt, libm]], ] + +systemd_resolved_sources += files('resolved.c') diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index 5b3ceeff3..96a505f5e 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -705,7 +705,6 @@ invalid: static int verb_query(int argc, char **argv, void *userdata) { sd_bus *bus = userdata; - char **p; int q, r = 0; if (arg_type != 0) @@ -975,7 +974,6 @@ static int resolve_openpgp(sd_bus *bus, const char *address) { static int verb_openpgp(int argc, char **argv, void *userdata) { sd_bus *bus = userdata; - char **p; int q, r = 0; STRV_FOREACH(p, argv + 1) { @@ -1025,7 +1023,7 @@ static bool service_family_is_valid(const char *s) { static int verb_tlsa(int argc, char **argv, void *userdata) { sd_bus *bus = userdata; - char **p, **args = argv + 1; + char **args = argv + 1; const char *family = "tcp"; int q, r = 0; @@ -1389,7 +1387,6 @@ static int status_print_strv_ifindex(int ifindex, const char *ifname, char **p) printf("%s%nGlobal%n%s:", ansi_highlight(), &pos1, &pos2, ansi_normal()); size_t cols = columns(), position = pos2 - pos1 + 2; - char **i; STRV_FOREACH(i, p) { size_t our_len = utf8_console_width(*i); /* This returns -1 on invalid utf-8 (which shouldn't happen). @@ -2025,7 +2022,6 @@ static int verb_status(int argc, char **argv, void *userdata) { int r = 0; if (argc > 1) { - char **ifname; bool empty_line = false; STRV_FOREACH(ifname, argv + 1) { @@ -2049,7 +2045,6 @@ static int verb_status(int argc, char **argv, void *userdata) { static int call_dns(sd_bus *bus, char **dns, const BusLocator *locator, sd_bus_error *error, bool extended) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL; - char **p; int r; r = bus_message_new_method_call(bus, &req, locator, extended ? "SetLinkDNSEx" : "SetLinkDNS"); @@ -2157,7 +2152,6 @@ static int verb_dns(int argc, char **argv, void *userdata) { static int call_domain(sd_bus *bus, char **domain, const BusLocator *locator, sd_bus_error *error) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL; - char **p; int r; r = bus_message_new_method_call(bus, &req, locator, "SetLinkDomains"); @@ -2454,7 +2448,6 @@ static int call_nta(sd_bus *bus, char **nta, const BusLocator *locator, sd_bus_ static int verb_nta(int argc, char **argv, void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus = userdata; - char **p; int r; bool clear; diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 88c67e1c3..4425a2f32 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -59,57 +59,122 @@ static int dns_query_bus_track(DnsQuery *q, sd_bus_message *m) { return 0; } -static int reply_query_state(DnsQuery *q) { - +static sd_bus_message *dns_query_steal_request(DnsQuery *q) { + assert(q); + + /* Find the main query, it's the one that owns the message */ + while (q->auxiliary_for) + q = q->auxiliary_for; + + /* Let's take the request message out of the DnsQuery object, so that we never send requests twice */ + return TAKE_PTR(q->bus_request); +} + +_sd_printf_(3, 4) static int reply_method_errorf( + DnsQuery *query, + const char *error_name, + const char *format, + ...) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL; + va_list ap; + int r; + + assert(query); + assert(format); + + req = dns_query_steal_request(query); + if (!req) /* No bus message set anymore? then we already replied already, let's not answer a second time */ + return 0; + + va_start(ap, format); + r = sd_bus_reply_method_errorfv(req, error_name, format, ap); + va_end(ap); + + return r; +} + +_sd_printf_(3, 4) static int reply_method_errnof( + DnsQuery *query, + int err, + const char *format, + ...) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL; + int r; + + assert(query); + + req = dns_query_steal_request(query); + if (!req) /* No bus message set anymore? then we already replied already, let's not answer a second time */ + return 0; + + if (format) { + va_list ap; + + va_start(ap, format); + r = sd_bus_reply_method_errnofv(req, err, format, ap); + va_end(ap); + } else + r = sd_bus_reply_method_errno(req, err, NULL); + + return r; +} + +static int reply_query_state(DnsQuery *q) { assert(q); - assert(q->bus_request); switch (q->state) { case DNS_TRANSACTION_NO_SERVERS: - return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); + return reply_method_errorf(q, BUS_ERROR_NO_NAME_SERVERS, "No appropriate name servers or networks for name found"); case DNS_TRANSACTION_TIMEOUT: - return sd_bus_reply_method_errorf(q->bus_request, SD_BUS_ERROR_TIMEOUT, "Query timed out"); + return reply_method_errorf(q, SD_BUS_ERROR_TIMEOUT, "Query timed out"); case DNS_TRANSACTION_ATTEMPTS_MAX_REACHED: - return sd_bus_reply_method_errorf(q->bus_request, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed"); + return reply_method_errorf(q, SD_BUS_ERROR_TIMEOUT, "All attempts to contact name servers or networks failed"); case DNS_TRANSACTION_INVALID_REPLY: - return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_INVALID_REPLY, "Received invalid reply"); + return reply_method_errorf(q, BUS_ERROR_INVALID_REPLY, "Received invalid reply"); case DNS_TRANSACTION_ERRNO: - return sd_bus_reply_method_errnof(q->bus_request, q->answer_errno, "Lookup failed due to system error: %m"); + return reply_method_errnof(q, q->answer_errno, "Lookup failed due to system error: %m"); case DNS_TRANSACTION_ABORTED: - return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_ABORTED, "Query aborted"); + return reply_method_errorf(q, BUS_ERROR_ABORTED, "Query aborted"); case DNS_TRANSACTION_DNSSEC_FAILED: - return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s", - dnssec_result_to_string(q->answer_dnssec_result)); + return reply_method_errorf(q, BUS_ERROR_DNSSEC_FAILED, "DNSSEC validation failed: %s", + dnssec_result_to_string(q->answer_dnssec_result)); case DNS_TRANSACTION_NO_TRUST_ANCHOR: - return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_TRUST_ANCHOR, "No suitable trust anchor known"); + return reply_method_errorf(q, BUS_ERROR_NO_TRUST_ANCHOR, "No suitable trust anchor known"); case DNS_TRANSACTION_RR_TYPE_UNSUPPORTED: - return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_RR_TYPE_UNSUPPORTED, "Server does not support requested resource record type"); + return reply_method_errorf(q, BUS_ERROR_RR_TYPE_UNSUPPORTED, "Server does not support requested resource record type"); case DNS_TRANSACTION_NETWORK_DOWN: - return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NETWORK_DOWN, "Network is down"); + return reply_method_errorf(q, BUS_ERROR_NETWORK_DOWN, "Network is down"); case DNS_TRANSACTION_NOT_FOUND: /* We return this as NXDOMAIN. This is only generated when a host doesn't implement LLMNR/TCP, and we * thus quickly know that we cannot resolve an in-addr.arpa or ip6.arpa address. */ - return sd_bus_reply_method_errorf(q->bus_request, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q)); + return reply_method_errorf(q, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q)); case DNS_TRANSACTION_NO_SOURCE: - return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SOURCE, "All suitable resolution sources turned off"); + return reply_method_errorf(q, BUS_ERROR_NO_SOURCE, "All suitable resolution sources turned off"); case DNS_TRANSACTION_STUB_LOOP: - return sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_STUB_LOOP, "Configured DNS server loops back to us"); + return reply_method_errorf(q, BUS_ERROR_STUB_LOOP, "Configured DNS server loops back to us"); case DNS_TRANSACTION_RCODE_FAILURE: { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *req = NULL; + + req = dns_query_steal_request(q); + if (!req) /* No bus message set anymore? then we already replied already, let's not answer a second time */ + return 0; if (q->answer_rcode == DNS_RCODE_NXDOMAIN) sd_bus_error_setf(&error, _BUS_ERROR_DNS "NXDOMAIN", "'%s' not found", dns_query_string(q)); @@ -127,7 +192,7 @@ static int reply_query_state(DnsQuery *q) { sd_bus_error_setf(&error, n, "Could not resolve '%s', server or network returned error %s", dns_query_string(q), rc); } - return sd_bus_reply_method_error(q->bus_request, &error); + return sd_bus_reply_method_error(req, &error); } case DNS_TRANSACTION_NULL: @@ -198,7 +263,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *query) { r = dns_query_process_cname_many(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); + r = reply_method_errorf(q, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -238,7 +303,7 @@ static void bus_method_resolve_hostname_complete(DnsQuery *query) { } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); + r = reply_method_errorf(q, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -261,12 +326,13 @@ static void bus_method_resolve_hostname_complete(DnsQuery *query) { if (r < 0) goto finish; + q->bus_request = sd_bus_message_unref(q->bus_request); r = sd_bus_send(q->manager->bus, reply, NULL); finish: if (r < 0) { log_error_errno(r, "Failed to send hostname reply: %m"); - sd_bus_reply_method_errno(q->bus_request, r, NULL); + (void) reply_method_errnof(q, r, NULL); } } @@ -488,7 +554,7 @@ static void bus_method_resolve_address_complete(DnsQuery *query) { r = dns_query_process_cname_many(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); + r = reply_method_errorf(q, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -533,7 +599,7 @@ static void bus_method_resolve_address_complete(DnsQuery *query) { _cleanup_free_ char *ip = NULL; (void) in_addr_to_string(q->request_family, &q->request_address, &ip); - r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_RR, + r = reply_method_errorf(q, BUS_ERROR_NO_SUCH_RR, "Address '%s' does not have any RR of requested type", strnull(ip)); goto finish; } @@ -546,12 +612,13 @@ static void bus_method_resolve_address_complete(DnsQuery *query) { if (r < 0) goto finish; + q->bus_request = sd_bus_message_unref(q->bus_request); r = sd_bus_send(q->manager->bus, reply, NULL); finish: if (r < 0) { log_error_errno(r, "Failed to send address reply: %m"); - sd_bus_reply_method_errno(q->bus_request, r, NULL); + (void) reply_method_errnof(q, r, NULL); } } @@ -661,7 +728,7 @@ static void bus_method_resolve_record_complete(DnsQuery *query) { r = dns_query_process_cname_many(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); + r = reply_method_errorf(q, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -697,7 +764,7 @@ static void bus_method_resolve_record_complete(DnsQuery *query) { } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_query_string(q)); + r = reply_method_errorf(q, BUS_ERROR_NO_SUCH_RR, "Name '%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -709,12 +776,13 @@ static void bus_method_resolve_record_complete(DnsQuery *query) { if (r < 0) goto finish; + q->bus_request = sd_bus_message_unref(q->bus_request); r = sd_bus_send(q->manager->bus, reply, NULL); finish: if (r < 0) { log_error_errno(r, "Failed to send record reply: %m"); - sd_bus_reply_method_errno(q->bus_request, r, NULL); + (void) reply_method_errnof(q, r, NULL); } } @@ -795,7 +863,6 @@ static int bus_method_resolve_record(sd_bus_message *message, void *userdata, sd static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) { _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *canonical = NULL; _cleanup_free_ char *normalized = NULL; - DnsQuery *aux; int r; assert(q); @@ -926,7 +993,6 @@ static int append_srv(DnsQuery *q, sd_bus_message *reply, DnsResourceRecord *rr) } static int append_txt(sd_bus_message *reply, DnsResourceRecord *rr) { - DnsTxtItem *i; int r; assert(reply); @@ -957,7 +1023,6 @@ static void resolve_service_all_complete(DnsQuery *query) { DnsQuestion *question; DnsResourceRecord *rr; unsigned added = 0; - DnsQuery *aux; int r; assert(q); @@ -1002,7 +1067,7 @@ static void resolve_service_all_complete(DnsQuery *query) { assert(bad->auxiliary_result != 0); if (bad->auxiliary_result == -ELOOP) { - r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(bad)); + r = reply_method_errorf(q, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(bad)); goto finish; } @@ -1046,7 +1111,7 @@ static void resolve_service_all_complete(DnsQuery *query) { } if (added <= 0) { - r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); + r = reply_method_errorf(q, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -1087,12 +1152,13 @@ static void resolve_service_all_complete(DnsQuery *query) { if (r < 0) goto finish; + q->bus_request = sd_bus_message_unref(q->bus_request); r = sd_bus_send(q->manager->bus, reply, NULL); finish: if (r < 0) { log_error_errno(r, "Failed to send service reply: %m"); - sd_bus_reply_method_errno(q->bus_request, r, NULL); + (void) reply_method_errnof(q, r, NULL); } } @@ -1137,7 +1203,6 @@ static int resolve_service_hostname(DnsQuery *q, DnsResourceRecord *rr, int ifin if (r < 0) return r; - aux->bus_request = sd_bus_message_ref(q->bus_request); aux->request_family = q->request_family; aux->complete = resolve_service_hostname_complete; @@ -1178,7 +1243,7 @@ static void bus_method_resolve_service_complete(DnsQuery *query) { r = dns_query_process_cname_many(q); if (r == -ELOOP) { - r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); + r = reply_method_errorf(q, BUS_ERROR_CNAME_LOOP, "CNAME loop detected, or CNAME resolving disabled on '%s'", dns_query_string(q)); goto finish; } if (r < 0) @@ -1219,18 +1284,15 @@ static void bus_method_resolve_service_complete(DnsQuery *query) { } if (has_root_domain && found <= 0) { - /* If there's exactly one SRV RR and it uses - * the root domain as hostname, then the - * service is explicitly not offered on the - * domain. Report this as a recognizable - * error. See RFC 2782, Section "Usage - * Rules". */ - r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_query_string(q)); + /* If there's exactly one SRV RR and it uses the root domain as hostname, then the service is + * explicitly not offered on the domain. Report this as a recognizable error. See RFC 2782, + * Section "Usage Rules". */ + r = reply_method_errorf(q, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_query_string(q)); goto finish; } if (found <= 0) { - r = sd_bus_reply_method_errorf(q->bus_request, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); + r = reply_method_errorf(q, BUS_ERROR_NO_SUCH_RR, "'%s' does not have any RR of the requested type", dns_query_string(q)); goto finish; } @@ -1241,7 +1303,7 @@ static void bus_method_resolve_service_complete(DnsQuery *query) { finish: if (r < 0) { log_error_errno(r, "Failed to send service reply: %m"); - sd_bus_reply_method_errno(q->bus_request, r, NULL); + (void) reply_method_errnof(q, r, NULL); } } @@ -1383,7 +1445,6 @@ static int bus_property_get_dns_servers_internal( bool extended) { Manager *m = userdata; - DnsServer *s; Link *l; int r; @@ -1442,7 +1503,7 @@ static int bus_property_get_fallback_dns_servers_internal( sd_bus_error *error, bool extended) { - DnsServer *s, **f = userdata; + DnsServer **f = userdata; int r; assert(reply); @@ -1535,7 +1596,6 @@ static int bus_property_get_domains( sd_bus_error *error) { Manager *m = userdata; - DnsSearchDomain *d; Link *l; int r; @@ -1593,7 +1653,6 @@ static int bus_property_get_cache_statistics( uint64_t size = 0, hit = 0, miss = 0; Manager *m = userdata; - DnsScope *s; assert(reply); assert(m); @@ -1686,7 +1745,6 @@ static int bus_property_get_resolv_conf_mode( static int bus_method_reset_statistics(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; - DnsScope *s; assert(message); assert(m); diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c index 7873c363b..930313b84 100644 --- a/src/resolve/resolved-conf.c +++ b/src/resolve/resolved-conf.c @@ -35,16 +35,15 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons if (r < 0) return r; - /* By default, the port number is determined by the transaction feature level. + /* Silently filter out 0.0.0.0, 127.0.0.53, 127.0.0.54 (our own stub DNS listener) */ + if (!dns_server_address_valid(family, &address)) + return 0; + + /* By default, the port number is determined with the transaction feature level. * See dns_transaction_port() and dns_server_port(). */ if (IN_SET(port, 53, 853)) port = 0; - /* Refuse 0.0.0.0, 127.0.0.53, 127.0.0.54 and the rest of our own stub DNS listeners. */ - if (!dns_server_address_valid(family, &address) || - manager_server_address_is_stub(m, family, &address, port ?: 53)) - return -ELOOP; - /* Filter out duplicates */ s = dns_server_find(manager_get_first_dns_server(m, type), family, &address, port, ifindex, server_name); if (s) { @@ -57,7 +56,7 @@ static int manager_add_dns_server_by_string(Manager *m, DnsServerType type, cons return dns_server_new(m, NULL, type, NULL, family, &address, port, ifindex, server_name); } -int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string, bool ignore_self_quietly) { +int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string) { int r; assert(m); @@ -71,10 +70,7 @@ int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, con return r; r = manager_add_dns_server_by_string(m, type, word); - if (r == -ELOOP) - log_full(ignore_self_quietly ? LOG_DEBUG : LOG_INFO, - "DNS server string '%s' points to our own listener, ignoring.", word); - else if (r < 0) + if (r < 0) log_warning_errno(r, "Failed to add DNS server address '%s', ignoring: %m", word); } } @@ -155,7 +151,7 @@ int config_parse_dns_servers( dns_server_unlink_all(manager_get_first_dns_server(m, ltype)); else { /* Otherwise, add to the list */ - r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue, false); + r = manager_parse_dns_server_string_and_warn(m, ltype, rvalue); if (r < 0) { log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse DNS server string '%s', ignoring.", rvalue); @@ -163,7 +159,8 @@ int config_parse_dns_servers( } } - /* If we have a manual setting, then we stop reading /etc/resolv.conf */ + /* If we have a manual setting, then we stop reading + * /etc/resolv.conf */ if (ltype == DNS_SERVER_SYSTEM) m->read_resolv_conf = false; if (ltype == DNS_SERVER_FALLBACK) @@ -205,7 +202,8 @@ int config_parse_search_domains( } } - /* If we have a manual setting, then we stop reading /etc/resolv.conf */ + /* If we have a manual setting, then we stop reading + * /etc/resolv.conf */ m->read_resolv_conf = false; return 0; @@ -487,7 +485,7 @@ int manager_parse_config_file(Manager *m) { return r; if (m->need_builtin_fallbacks) { - r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS, false); + r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_FALLBACK, DNS_SERVERS); if (r < 0) return r; } diff --git a/src/resolve/resolved-conf.h b/src/resolve/resolved-conf.h index 4639cefbd..07ce2591a 100644 --- a/src/resolve/resolved-conf.h +++ b/src/resolve/resolved-conf.h @@ -8,7 +8,7 @@ int manager_parse_config_file(Manager *m); int manager_parse_search_domains_and_warn(Manager *m, const char *string); -int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string, bool ignore_self_quietly); +int manager_parse_dns_server_string_and_warn(Manager *m, DnsServerType type, const char *string); const struct ConfigPerfItem* resolved_gperf_lookup(const char *key, GPERF_LEN_TYPE length); const struct ConfigPerfItem* resolved_dnssd_gperf_lookup(const char *key, GPERF_LEN_TYPE length); diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 57e0ac3ac..45bcbfd9d 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -117,7 +117,7 @@ static void dns_cache_item_unlink_and_free(DnsCache *c, DnsCacheItem *i) { } static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) { - DnsCacheItem *first, *i; + DnsCacheItem *first; int r; first = hashmap_get(c->by_key, rr->key); @@ -135,7 +135,7 @@ static bool dns_cache_remove_by_rr(DnsCache *c, DnsResourceRecord *rr) { } static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) { - DnsCacheItem *first, *i, *n; + DnsCacheItem *first; assert(c); assert(key); @@ -144,7 +144,7 @@ static bool dns_cache_remove_by_key(DnsCache *c, DnsResourceKey *key) { if (!first) return false; - LIST_FOREACH_SAFE(by_key, i, n, first) { + LIST_FOREACH(by_key, i, first) { prioq_remove(c->by_expiry, i, &i->prioq_idx); dns_cache_item_free(i); } @@ -214,7 +214,7 @@ void dns_cache_prune(DnsCache *c) { break; if (t <= 0) - t = now(clock_boottime_or_monotonic()); + t = now(CLOCK_BOOTTIME); if (i->until > t) break; @@ -301,12 +301,10 @@ static int dns_cache_link_item(DnsCache *c, DnsCacheItem *i) { } static DnsCacheItem* dns_cache_get(DnsCache *c, DnsResourceRecord *rr) { - DnsCacheItem *i; - assert(c); assert(rr); - LIST_FOREACH(by_key, i, hashmap_get(c->by_key, rr->key)) + LIST_FOREACH(by_key, i, (DnsCacheItem*) hashmap_get(c->by_key, rr->key)) if (i->rr && dns_resource_record_equal(i->rr, rr) > 0) return i; @@ -737,7 +735,7 @@ int dns_cache_put( /* Make some space for our new entries */ dns_cache_make_space(c, cache_keys); - timestamp = now(clock_boottime_or_monotonic()); + timestamp = now(CLOCK_BOOTTIME); /* Second, add in positive entries for all contained RRs */ DNS_ANSWER_FOREACH_ITEM(item, answer) { @@ -987,7 +985,7 @@ int dns_cache_lookup( unsigned n = 0; int r; bool nxdomain = false; - DnsCacheItem *j, *first, *nsec = NULL; + DnsCacheItem *first, *nsec = NULL; bool have_authenticated = false, have_non_authenticated = false, have_confidential = false, have_non_confidential = false; usec_t current = 0; int found_rcode = -1; @@ -1018,7 +1016,7 @@ int dns_cache_lookup( if (FLAGS_SET(query_flags, SD_RESOLVED_CLAMP_TTL)) { /* 'current' is always passed to answer_add_clamp_ttl(), but is only used conditionally. * We'll do the same assert there to make sure that it was initialized properly. */ - current = now(clock_boottime_or_monotonic()); + current = now(CLOCK_BOOTTIME); assert(current > 0); } @@ -1223,7 +1221,7 @@ miss: } int dns_cache_check_conflicts(DnsCache *cache, DnsResourceRecord *rr, int owner_family, const union in_addr_union *owner_address) { - DnsCacheItem *i, *first; + DnsCacheItem *first; bool same_owner = true; assert(cache); @@ -1266,9 +1264,7 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { assert(cache); assert(p); - HASHMAP_FOREACH(i, cache->by_key) { - DnsCacheItem *j; - + HASHMAP_FOREACH(i, cache->by_key) LIST_FOREACH(by_key, j, i) { if (!j->rr) continue; @@ -1299,7 +1295,6 @@ int dns_cache_export_shared_to_packet(DnsCache *cache, DnsPacket *p) { ancount++; } - } DNS_PACKET_HEADER(p)->ancount = htobe16(ancount); @@ -1315,9 +1310,7 @@ void dns_cache_dump(DnsCache *cache, FILE *f) { if (!f) f = stdout; - HASHMAP_FOREACH(i, cache->by_key) { - DnsCacheItem *j; - + HASHMAP_FOREACH(i, cache->by_key) LIST_FOREACH(by_key, j, i) { fputc('\t', f); @@ -1341,7 +1334,6 @@ void dns_cache_dump(DnsCache *cache, FILE *f) { fputc('\n', f); } } - } } bool dns_cache_is_empty(DnsCache *cache) { diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index d45f87ff5..48b91d687 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -941,15 +941,12 @@ int dns_packet_append_rr(DnsPacket *p, const DnsResourceRecord *rr, const DnsAns r = dns_packet_append_raw_string(p, NULL, 0, NULL); if (r < 0) goto fail; - } else { - DnsTxtItem *i; - + } else LIST_FOREACH(items, i, rr->txt.items) { r = dns_packet_append_raw_string(p, i->data, i->length, NULL); if (r < 0) goto fail; } - } r = 0; break; @@ -2505,7 +2502,7 @@ int dns_packet_patch_ttls(DnsPacket *p, usec_t timestamp) { usec_t k; int r; - k = now(clock_boottime_or_monotonic()); + k = now(CLOCK_BOOTTIME); assert(k >= timestamp); k -= timestamp; diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index c0bb40937..b62c5c349 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -345,8 +345,6 @@ fail: } static void dns_query_stop(DnsQuery *q) { - DnsQueryCandidate *c; - assert(q); event_source_disable(q->timeout_event_source); @@ -718,8 +716,7 @@ static int dns_query_try_etc_hosts(DnsQuery *q) { int dns_query_go(DnsQuery *q) { DnsScopeMatch found = DNS_SCOPE_NO; - DnsScope *s, *first = NULL; - DnsQueryCandidate *c; + DnsScope *first = NULL; int r; assert(q); @@ -780,7 +777,7 @@ int dns_query_go(DnsQuery *q) { r = event_reset_time_relative( q->manager->event, &q->timeout_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, SD_RESOLVED_QUERY_TIMEOUT_USEC, 0, on_query_timeout, q, 0, "query-timeout", true); @@ -938,8 +935,7 @@ fail: } void dns_query_ready(DnsQuery *q) { - - DnsQueryCandidate *bad = NULL, *c; + DnsQueryCandidate *bad = NULL; bool pending = false; assert(q); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index 720b4c58d..83140d79f 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -784,7 +784,6 @@ static char *format_types(Bitmap *types) { } static char *format_txt(DnsTxtItem *first) { - DnsTxtItem *i; size_t c = 1; char *p, *s; @@ -1358,8 +1357,6 @@ void dns_resource_record_hash_func(const DnsResourceRecord *rr, struct siphash * case DNS_TYPE_TXT: case DNS_TYPE_SPF: { - DnsTxtItem *j; - LIST_FOREACH(items, j, rr->txt.items) { siphash24_compress_safe(j->data, j->length, state); @@ -1813,7 +1810,7 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) { } DnsTxtItem *dns_txt_item_copy(DnsTxtItem *first) { - DnsTxtItem *i, *copy = NULL, *end = NULL; + DnsTxtItem *copy = NULL, *end = NULL; LIST_FOREACH(items, i, first) { DnsTxtItem *j; diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index ab40d692a..a872e9d25 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -529,7 +529,6 @@ static DnsScopeMatch match_subnet_reverse_lookups( bool exclude_own) { union in_addr_union ia; - LinkAddress *a; int f, r; assert(s); @@ -587,7 +586,6 @@ DnsScopeMatch dns_scope_good_domain( DnsQuery *q) { DnsQuestion *question; - DnsSearchDomain *d; const char *domain; uint64_t flags; int ifindex; @@ -633,14 +631,8 @@ DnsScopeMatch dns_scope_good_domain( dns_name_equal(domain, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) return DNS_SCOPE_NO; - /* Never respond to some of the domains listed in RFC6303 */ - if (dns_name_endswith(domain, "0.in-addr.arpa") > 0 || - dns_name_equal(domain, "255.255.255.255.in-addr.arpa") > 0 || - dns_name_equal(domain, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) - return DNS_SCOPE_NO; - - /* Never respond to some of the domains listed in RFC6761 */ - if (dns_name_endswith(domain, "invalid") > 0) + /* Never respond to some of the domains listed in RFC6303 + RFC6761 */ + if (dns_name_dont_resolve(domain)) return DNS_SCOPE_NO; /* Never go to network for the _gateway or _outbound domain — they're something special, synthesized locally. */ @@ -1088,7 +1080,7 @@ DnsTransaction *dns_scope_find_transaction( DnsResourceKey *key, uint64_t query_flags) { - DnsTransaction *first, *t; + DnsTransaction *first; assert(scope); assert(key); @@ -1247,7 +1239,7 @@ int dns_scope_notify_conflict(DnsScope *scope, DnsResourceRecord *rr) { r = sd_event_add_time_relative( scope->manager->event, &scope->conflict_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, jitter, LLMNR_JITTER_INTERVAL_USEC, on_conflict_dispatch, scope); @@ -1404,8 +1396,7 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL; _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; _cleanup_set_free_ Set *types = NULL; - DnsTransaction *t; - DnsZoneItem *z, *i; + DnsZoneItem *z; unsigned size = 0; char *service_type; int r; @@ -1483,12 +1474,16 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) { "_services._dns-sd._udp.local"); if (!rr) return log_oom(); + rr->ptr.name = strdup(service_type); + if (!rr->ptr.name) + return log_oom(); + rr->ttl = MDNS_DEFAULT_TTL; r = dns_zone_put(&scope->zone, scope, rr, false); if (r < 0) - log_warning_errno(r, "Failed to add DNS-SD PTR record to MDNS zone: %m"); + log_warning_errno(r, "Failed to add DNS-SD PTR record to MDNS zone, ignoring: %m"); r = dns_answer_add(answer, rr, 0, 0, NULL); if (r < 0) @@ -1514,7 +1509,7 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) { r = sd_event_add_time_relative( scope->manager->event, &scope->announce_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, MDNS_ANNOUNCE_DELAY, MDNS_JITTER_RANGE_USEC, on_announcement_timeout, scope); @@ -1529,7 +1524,6 @@ int dns_scope_announce(DnsScope *scope, bool goodbye) { int dns_scope_add_dnssd_services(DnsScope *scope) { DnssdService *service; - DnssdTxtData *txt_data; int r; assert(scope); @@ -1563,7 +1557,6 @@ int dns_scope_add_dnssd_services(DnsScope *scope) { int dns_scope_remove_dnssd_services(DnsScope *scope) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; DnssdService *service; - DnssdTxtData *txt_data; int r; assert(scope); @@ -1588,7 +1581,7 @@ int dns_scope_remove_dnssd_services(DnsScope *scope) { } static bool dns_scope_has_route_only_domains(DnsScope *scope) { - DnsSearchDomain *domain, *first; + DnsSearchDomain *first; bool route_only = false; assert(scope); diff --git a/src/resolve/resolved-dns-search-domain.c b/src/resolve/resolved-dns-search-domain.c index c9f148a2b..0cc50d647 100644 --- a/src/resolve/resolved-dns-search-domain.c +++ b/src/resolve/resolved-dns-search-domain.c @@ -178,7 +178,6 @@ void dns_search_domain_mark_all(DnsSearchDomain *first) { } int dns_search_domain_find(DnsSearchDomain *first, const char *name, DnsSearchDomain **ret) { - DnsSearchDomain *d; int r; assert(name); diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c index cd755b13d..2ef2568da 100644 --- a/src/resolve/resolved-dns-server.c +++ b/src/resolve/resolved-dns-server.c @@ -230,7 +230,7 @@ static void dns_server_verified(DnsServer *s, DnsServerFeatureLevel level) { s->verified_feature_level = level; } - assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &s->verified_usec) >= 0); + assert_se(sd_event_now(s->manager->event, CLOCK_BOOTTIME, &s->verified_usec) >= 0); } static void dns_server_reset_counters(DnsServer *s) { @@ -405,7 +405,7 @@ static bool dns_server_grace_period_expired(DnsServer *s) { if (s->verified_usec == 0) return false; - assert_se(sd_event_now(s->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); + assert_se(sd_event_now(s->manager->event, CLOCK_BOOTTIME, &ts) >= 0); if (s->verified_usec + s->features_grace_period_usec > ts) return false; @@ -815,8 +815,6 @@ void dns_server_mark_all(DnsServer *server) { } DnsServer *dns_server_find(DnsServer *first, int family, const union in_addr_union *in_addr, uint16_t port, int ifindex, const char *name) { - DnsServer *s; - LIST_FOREACH(servers, s, first) if (s->family == family && in_addr_equal(family, &s->address, in_addr) > 0 && @@ -992,8 +990,6 @@ void dns_server_reset_features(DnsServer *s) { } void dns_server_reset_features_all(DnsServer *s) { - DnsServer *i; - LIST_FOREACH(servers, i, s) dns_server_reset_features(i); } diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c index f48e2a802..4018113d5 100644 --- a/src/resolve/resolved-dns-stream.c +++ b/src/resolve/resolved-dns-stream.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "fd-util.h" #include "io-util.h" +#include "macro.h" #include "missing_network.h" #include "resolved-dns-stream.h" #include "resolved-manager.h" @@ -26,7 +27,7 @@ static void dns_stream_stop(DnsStream *s) { } static int dns_stream_update_io(DnsStream *s) { - int f = 0; + uint32_t f = 0; assert(s); @@ -46,6 +47,8 @@ static int dns_stream_update_io(DnsStream *s) { set_size(s->queries) < DNS_QUERIES_PER_STREAM) f |= EPOLLIN; + s->requested_events = f; + #if ENABLE_DNS_OVER_TLS /* For handshake and clean closing purposes, TLS can override requested events */ if (s->dnstls_events != 0) @@ -207,22 +210,10 @@ ssize_t dns_stream_writev(DnsStream *s, const struct iovec *iov, size_t iovcnt, assert(iov); #if ENABLE_DNS_OVER_TLS - if (s->encrypted && !(flags & DNS_STREAM_WRITE_TLS_DATA)) { - ssize_t ss; - size_t i; - - m = 0; - for (i = 0; i < iovcnt; i++) { - ss = dnstls_stream_write(s, iov[i].iov_base, iov[i].iov_len); - if (ss < 0) - return ss; - - m += ss; - if (ss != (ssize_t) iov[i].iov_len) - continue; - } - } else + if (s->encrypted && !(flags & DNS_STREAM_WRITE_TLS_DATA)) + return dnstls_stream_writev(s, iov, iovcnt); #endif + if (s->tfo_salen > 0) { struct msghdr hdr = { .msg_iov = (struct iovec*) iov, @@ -280,6 +271,29 @@ static int on_stream_timeout(sd_event_source *es, usec_t usec, void *userdata) { return dns_stream_complete(s, ETIMEDOUT); } +static DnsPacket *dns_stream_take_read_packet(DnsStream *s) { + assert(s); + + /* Note, dns_stream_update() should be called after this is called. When this is called, the + * stream may be already full and the EPOLLIN flag is dropped from the stream IO event source. + * Even this makes a room to read in the stream, this does not call dns_stream_update(), hence + * EPOLLIN flag is not set automatically. So, to read further packets from the stream, + * dns_stream_update() must be called explicitly. Currently, this is only called from + * on_stream_io(), and there dns_stream_update() is called. */ + + if (!s->read_packet) + return NULL; + + if (s->n_read < sizeof(s->read_size)) + return NULL; + + if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) + return NULL; + + s->n_read = 0; + return TAKE_PTR(s->read_packet); +} + static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *userdata) { _cleanup_(dns_stream_unrefp) DnsStream *s = dns_stream_ref(userdata); /* Protect stream while we process it */ bool progressed = false; @@ -338,9 +352,9 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use } } - if ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) && - (!s->read_packet || - s->n_read < sizeof(s->read_size) + s->read_packet->size)) { + while ((revents & (EPOLLIN|EPOLLHUP|EPOLLRDHUP)) && + (!s->read_packet || + s->n_read < sizeof(s->read_size) + s->read_packet->size)) { if (s->n_read < sizeof(s->read_size)) { ssize_t ss; @@ -349,6 +363,7 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use if (ss < 0) { if (!ERRNO_IS_TRANSIENT(ss)) return dns_stream_complete(s, -ss); + break; } else if (ss == 0) return dns_stream_complete(s, ECONNRESET); else { @@ -375,7 +390,7 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use s->read_packet->family = s->peer.sa.sa_family; s->read_packet->ttl = s->ttl; s->read_packet->ifindex = s->ifindex; - s->read_packet->timestamp = now(clock_boottime_or_monotonic()); + s->read_packet->timestamp = now(CLOCK_BOOTTIME); if (s->read_packet->family == AF_INET) { s->read_packet->sender.in = s->peer.in.sin_addr; @@ -402,36 +417,39 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use if (ss < 0) { if (!ERRNO_IS_TRANSIENT(ss)) return dns_stream_complete(s, -ss); + break; } else if (ss == 0) return dns_stream_complete(s, ECONNRESET); else s->n_read += ss; } - /* Are we done? If so, disable the event source for EPOLLIN */ - if (s->n_read >= sizeof(s->read_size) + be16toh(s->read_size)) { - /* If there's a packet handler - * installed, call that. Note that - * this is optional... */ - if (s->on_packet) { - r = s->on_packet(s); - if (r < 0) - return r; - } + /* Are we done? If so, call the packet handler and re-enable EPOLLIN for the + * event source if necessary. */ + _cleanup_(dns_packet_unrefp) DnsPacket *p = dns_stream_take_read_packet(s); + if (p) { + assert(s->on_packet); + r = s->on_packet(s, p); + if (r < 0) + return r; r = dns_stream_update_io(s); if (r < 0) return dns_stream_complete(s, -r); + + s->packet_received = true; + + /* If we just disabled the read event, stop reading */ + if (!FLAGS_SET(s->requested_events, EPOLLIN)) + break; } } } - /* Call "complete" callback if finished reading and writing one packet, and there's nothing else left - * to write. */ - if (s->type == DNS_STREAM_LLMNR_SEND && - (s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) && - ordered_set_isempty(s->write_queue) && - (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size)) + /* Complete the stream if finished reading and writing one packet, and there's nothing + * else left to write. */ + if (s->type == DNS_STREAM_LLMNR_SEND && s->packet_received && + !FLAGS_SET(s->requested_events, EPOLLOUT)) return dns_stream_complete(s, 0); /* If we did something, let's restart the timeout event source */ @@ -482,6 +500,8 @@ int dns_stream_new( DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address, + int (on_packet)(DnsStream*, DnsPacket*), + int (complete)(DnsStream*, int), /* optional */ usec_t connect_timeout_usec) { _cleanup_(dns_stream_unrefp) DnsStream *s = NULL; @@ -494,6 +514,7 @@ int dns_stream_new( assert(protocol >= 0); assert(protocol < _DNS_PROTOCOL_MAX); assert(fd >= 0); + assert(on_packet); if (m->n_dns_streams[type] > DNS_STREAMS_MAX) return -EBUSY; @@ -522,7 +543,7 @@ int dns_stream_new( r = sd_event_add_time_relative( m->event, &s->timeout_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, connect_timeout_usec, 0, on_stream_timeout, s); if (r < 0) @@ -535,6 +556,8 @@ int dns_stream_new( s->manager = m; s->fd = fd; + s->on_packet = on_packet; + s->complete = complete; if (tfo_address) { s->tfo_address = *tfo_address; @@ -561,22 +584,6 @@ int dns_stream_write_packet(DnsStream *s, DnsPacket *p) { return dns_stream_update_io(s); } -DnsPacket *dns_stream_take_read_packet(DnsStream *s) { - assert(s); - - if (!s->read_packet) - return NULL; - - if (s->n_read < sizeof(s->read_size)) - return NULL; - - if (s->n_read < sizeof(s->read_size) + be16toh(s->read_size)) - return NULL; - - s->n_read = 0; - return TAKE_PTR(s->read_packet); -} - void dns_stream_detach(DnsStream *s) { assert(s); diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h index 96b977f62..ba4a59e41 100644 --- a/src/resolve/resolved-dns-stream.h +++ b/src/resolve/resolved-dns-stream.h @@ -60,6 +60,8 @@ struct DnsStream { int ifindex; uint32_t ttl; bool identified; + bool packet_received; /* At least one packet is received. Used by LLMNR. */ + uint32_t requested_events; /* only when using TCP fast open */ union sockaddr_union tfo_address; @@ -67,7 +69,7 @@ struct DnsStream { #if ENABLE_DNS_OVER_TLS DnsTlsStreamData dnstls_data; - int dnstls_events; + uint32_t dnstls_events; #endif sd_event_source *io_event_source; @@ -78,7 +80,7 @@ struct DnsStream { size_t n_written, n_read; OrderedSet *write_queue; - int (*on_packet)(DnsStream *s); + int (*on_packet)(DnsStream *s, DnsPacket *p); int (*complete)(DnsStream *s, int error); LIST_HEAD(DnsTransaction, transactions); /* when used by the transaction logic */ @@ -93,7 +95,16 @@ struct DnsStream { LIST_FIELDS(DnsStream, streams); }; -int dns_stream_new(Manager *m, DnsStream **s, DnsStreamType type, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address, usec_t timeout); +int dns_stream_new( + Manager *m, + DnsStream **ret, + DnsStreamType type, + DnsProtocol protocol, + int fd, + const union sockaddr_union *tfo_address, + int (on_packet)(DnsStream*, DnsPacket*), + int (complete)(DnsStream*, int), /* optional */ + usec_t connect_timeout_usec); #if ENABLE_DNS_OVER_TLS int dns_stream_connect_tls(DnsStream *s, void *tls_session); #endif @@ -114,6 +125,4 @@ static inline bool DNS_STREAM_QUEUED(DnsStream *s) { return !!s->write_packet; } -DnsPacket *dns_stream_take_read_packet(DnsStream *s); - void dns_stream_detach(DnsStream *s); diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index 1fd7e69ea..9e34161eb 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -1037,12 +1037,9 @@ static int on_dns_stub_packet_extra(sd_event_source *s, int fd, uint32_t revents return on_dns_stub_packet_internal(s, fd, revents, l->manager, l); } -static int on_dns_stub_stream_packet(DnsStream *s) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; - +static int on_dns_stub_stream_packet(DnsStream *s, DnsPacket *p) { assert(s); - - p = dns_stream_take_read_packet(s); + assert(s->manager); assert(p); if (dns_packet_validate_query(p) > 0) { @@ -1067,15 +1064,14 @@ static int on_dns_stub_stream_internal(sd_event_source *s, int fd, uint32_t reve return -errno; } - r = dns_stream_new(m, &stream, DNS_STREAM_STUB, DNS_PROTOCOL_DNS, cfd, NULL, DNS_STREAM_STUB_TIMEOUT_USEC); + r = dns_stream_new(m, &stream, DNS_STREAM_STUB, DNS_PROTOCOL_DNS, cfd, NULL, + on_dns_stub_stream_packet, dns_stub_stream_complete, DNS_STREAM_STUB_TIMEOUT_USEC); if (r < 0) { safe_close(cfd); return r; } stream->stub_listener_extra = l; - stream->on_packet = on_dns_stub_stream_packet; - stream->complete = dns_stub_stream_complete; /* We let the reference to the stream dangle here, it will be dropped later by the complete callback. */ diff --git a/src/resolve/resolved-dns-synthesize.c b/src/resolve/resolved-dns-synthesize.c index 0914515fd..9712322a0 100644 --- a/src/resolve/resolved-dns-synthesize.c +++ b/src/resolve/resolved-dns-synthesize.c @@ -397,11 +397,8 @@ int dns_synthesize_answer( if (dns_name_is_empty(name)) { /* Do nothing. */ - } else if (dns_name_endswith(name, "0.in-addr.arpa") > 0 || - dns_name_equal(name, "255.255.255.255.in-addr.arpa") > 0 || - dns_name_equal(name, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0 || - dns_name_endswith(name, "invalid") > 0) { - + } else if (dns_name_dont_resolve(name)) { + /* Synthesize NXDOMAIN for some of the domains in RFC6303 + RFC6761 */ nxdomain = true; continue; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 0cf991271..cec3cea7c 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -634,24 +634,19 @@ static int on_stream_complete(DnsStream *s, int error) { } } - if (error != 0) { - DnsTransaction *t, *n; - - LIST_FOREACH_SAFE(transactions_by_stream, t, n, s->transactions) + if (error != 0) + LIST_FOREACH(transactions_by_stream, t, s->transactions) on_transaction_stream_error(t, error); - } return 0; } -static int on_stream_packet(DnsStream *s) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; +static int on_stream_packet(DnsStream *s, DnsPacket *p) { DnsTransaction *t; assert(s); - - /* Take ownership of packet to be able to receive new packets */ - assert_se(p = dns_stream_take_read_packet(s)); + assert(s->manager); + assert(p); t = hashmap_get(s->manager->dns_transactions, UINT_TO_PTR(DNS_PACKET_ID(p))); if (t && t->stream == s) /* Validate that the stream we got this on actually is the stream the @@ -754,7 +749,8 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) { if (fd < 0) return fd; - r = dns_stream_new(t->scope->manager, &s, type, t->scope->protocol, fd, &sa, stream_timeout_usec); + r = dns_stream_new(t->scope->manager, &s, type, t->scope->protocol, fd, &sa, + on_stream_packet, on_stream_complete, stream_timeout_usec); if (r < 0) return r; @@ -777,9 +773,6 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) { t->server->stream = dns_stream_ref(s); } - s->complete = on_stream_complete; - s->on_packet = on_stream_packet; - /* The interface index is difficult to determine if we are * connecting to the local host, hence fill this in right away * instead of determining it from the socket */ @@ -1429,7 +1422,7 @@ static int on_dns_packet(sd_event_source *s, int fd, uint32_t revents, void *use * next recvmsg(). Treat this like a lost packet. */ log_debug_errno(r, "Connection failure for DNS UDP packet: %m"); - assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &usec) >= 0); + assert_se(sd_event_now(t->scope->manager->event, CLOCK_BOOTTIME, &usec) >= 0); dns_server_packet_lost(t->server, IPPROTO_UDP, t->current_feature_level); dns_transaction_close_connection(t, /* use_graveyard = */ false); @@ -1766,7 +1759,6 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) { static int dns_transaction_make_packet_mdns(DnsTransaction *t) { _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; bool add_known_answers = false; - DnsTransaction *other; DnsResourceKey *tkey; _cleanup_set_free_ Set *keys = NULL; unsigned qdcount; @@ -1805,7 +1797,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) { * in our current scope, and see whether their timing constraints allow them to be sent. */ - assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); + assert_se(sd_event_now(t->scope->manager->event, CLOCK_BOOTTIME, &ts) >= 0); LIST_FOREACH(transactions_by_scope, other, t->scope->transactions) { @@ -1843,7 +1835,7 @@ static int dns_transaction_make_packet_mdns(DnsTransaction *t) { r = sd_event_add_time( other->scope->manager->event, &other->timeout_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, ts, 0, on_transaction_timeout, other); if (r < 0) @@ -1945,7 +1937,7 @@ int dns_transaction_go(DnsTransaction *t) { * finished now. In the latter case, the transaction and query candidate objects must not be accessed. */ - assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); + assert_se(sd_event_now(t->scope->manager->event, CLOCK_BOOTTIME, &ts) >= 0); r = dns_transaction_prepare(t, ts); if (r <= 0) @@ -1989,7 +1981,7 @@ int dns_transaction_go(DnsTransaction *t) { r = sd_event_add_time_relative( t->scope->manager->event, &t->timeout_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, jitter, accuracy, on_transaction_timeout, t); if (r < 0) @@ -2079,7 +2071,7 @@ int dns_transaction_go(DnsTransaction *t) { r = sd_event_add_time( t->scope->manager->event, &t->timeout_event_source, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, ts, 0, on_transaction_timeout, t); if (r < 0) diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index b036aa402..4a6c06d13 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -409,7 +409,6 @@ static int dns_trust_anchor_load_files( int (*loader)(DnsTrustAnchor *d, const char *path, unsigned n, const char *line)) { _cleanup_strv_free_ char **files = NULL; - char **f; int r; assert(d); diff --git a/src/resolve/resolved-dns-zone.c b/src/resolve/resolved-dns-zone.c index 6b3f5f707..f533f972f 100644 --- a/src/resolve/resolved-dns-zone.c +++ b/src/resolve/resolved-dns-zone.c @@ -78,12 +78,10 @@ void dns_zone_flush(DnsZone *z) { } DnsZoneItem* dns_zone_get(DnsZone *z, DnsResourceRecord *rr) { - DnsZoneItem *i; - assert(z); assert(rr); - LIST_FOREACH(by_key, i, hashmap_get(z->by_key, rr->key)) + LIST_FOREACH(by_key, i, (DnsZoneItem*) hashmap_get(z->by_key, rr->key)) if (dns_resource_record_equal(i->rr, rr) > 0) return i; @@ -250,21 +248,15 @@ int dns_zone_put(DnsZone *z, DnsScope *s, DnsResourceRecord *rr, bool probe) { return r; if (probe) { - DnsZoneItem *first, *j; bool established = false; /* Check if there's already an RR with the same name * established. If so, it has been probed already, and * we don't need to probe again. */ - LIST_FIND_HEAD(by_name, i, first); - LIST_FOREACH(by_name, j, first) { - if (i == j) - continue; - + LIST_FOREACH_OTHERS(by_name, j, i) if (j->state == DNS_ZONE_ITEM_ESTABLISHED) established = true; - } if (established) i->state = DNS_ZONE_ITEM_ESTABLISHED; @@ -306,7 +298,7 @@ static int dns_zone_add_authenticated_answer(DnsAnswer *a, DnsZoneItem *i, int i int dns_zone_lookup(DnsZone *z, DnsResourceKey *key, int ifindex, DnsAnswer **ret_answer, DnsAnswer **ret_soa, bool *ret_tentative) { _cleanup_(dns_answer_unrefp) DnsAnswer *answer = NULL, *soa = NULL; unsigned n_answer = 0; - DnsZoneItem *j, *first; + DnsZoneItem *first; bool tentative = true, need_soa = false; int r; @@ -576,7 +568,7 @@ static int dns_zone_item_verify(DnsZoneItem *i) { } int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) { - DnsZoneItem *i, *first; + DnsZoneItem *first; int c = 0; assert(zone); @@ -614,7 +606,7 @@ int dns_zone_check_conflicts(DnsZone *zone, DnsResourceRecord *rr) { } int dns_zone_verify_conflicts(DnsZone *zone, DnsResourceKey *key) { - DnsZoneItem *i, *first; + DnsZoneItem *first; int c = 0; assert(zone); @@ -639,12 +631,9 @@ void dns_zone_verify_all(DnsZone *zone) { assert(zone); - HASHMAP_FOREACH(i, zone->by_key) { - DnsZoneItem *j; - + HASHMAP_FOREACH(i, zone->by_key) LIST_FOREACH(by_key, j, i) dns_zone_item_verify(j); - } } void dns_zone_dump(DnsZone *zone, FILE *f) { @@ -656,9 +645,7 @@ void dns_zone_dump(DnsZone *zone, FILE *f) { if (!f) f = stdout; - HASHMAP_FOREACH(i, zone->by_key) { - DnsZoneItem *j; - + HASHMAP_FOREACH(i, zone->by_key) LIST_FOREACH(by_key, j, i) { const char *t; @@ -672,7 +659,6 @@ void dns_zone_dump(DnsZone *zone, FILE *f) { fputs(t, f); fputc('\n', f); } - } } bool dns_zone_is_empty(DnsZone *zone) { @@ -683,7 +669,7 @@ bool dns_zone_is_empty(DnsZone *zone) { } bool dns_zone_contains_name(DnsZone *z, const char *name) { - DnsZoneItem *i, *first; + DnsZoneItem *first; first = hashmap_get(z->by_name, name); if (!first) diff --git a/src/resolve/resolved-dnssd-bus.c b/src/resolve/resolved-dnssd-bus.c index d908cc64e..84a51ba2e 100644 --- a/src/resolve/resolved-dnssd-bus.c +++ b/src/resolve/resolved-dnssd-bus.c @@ -12,7 +12,6 @@ int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error) { DnssdService *s = userdata; - DnssdTxtData *txt_data; Manager *m; Link *l; int r; diff --git a/src/resolve/resolved-dnssd.c b/src/resolve/resolved-dnssd.c index ab2773e4e..6d77aa817 100644 --- a/src/resolve/resolved-dnssd.c +++ b/src/resolve/resolved-dnssd.c @@ -186,7 +186,6 @@ int dnssd_render_instance_name(DnssdService *s, char **ret_name) { int dnssd_load(Manager *manager) { _cleanup_strv_free_ char **files = NULL; - char **f; int r; assert(manager); @@ -211,7 +210,6 @@ int dnssd_update_rrs(DnssdService *s) { _cleanup_free_ char *n = NULL; _cleanup_free_ char *service_name = NULL; _cleanup_free_ char *full_name = NULL; - DnssdTxtData *txt_data; int r; assert(s); diff --git a/src/resolve/resolved-dnstls-gnutls.c b/src/resolve/resolved-dnstls-gnutls.c index e7ccba934..8c8628ebb 100644 --- a/src/resolve/resolved-dnstls-gnutls.c +++ b/src/resolve/resolved-dnstls-gnutls.c @@ -6,6 +6,7 @@ #include +#include "io-util.h" #include "resolved-dns-stream.h" #include "resolved-dnstls.h" #include "resolved-manager.h" @@ -13,7 +14,7 @@ #define TLS_PROTOCOL_PRIORITY "NORMAL:-VERS-ALL:+VERS-TLS1.3:+VERS-TLS1.2" DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(gnutls_session_t, gnutls_deinit, NULL); -static ssize_t dnstls_stream_writev(gnutls_transport_ptr_t p, const giovec_t *iov, int iovcnt) { +static ssize_t dnstls_stream_vec_push(gnutls_transport_ptr_t p, const giovec_t *iov, int iovcnt) { int r; assert(p); @@ -81,7 +82,7 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server) { gnutls_handshake_set_timeout(gs, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); gnutls_transport_set_ptr2(gs, (gnutls_transport_ptr_t) (long) stream->fd, stream); - gnutls_transport_set_vec_push_function(gs, &dnstls_stream_writev); + gnutls_transport_set_vec_push_function(gs, &dnstls_stream_vec_push); stream->encrypted = true; stream->dnstls_data.handshake = gnutls_handshake(gs); @@ -163,15 +164,26 @@ int dnstls_stream_shutdown(DnsStream *stream, int error) { return 0; } -ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) { +ssize_t dnstls_stream_writev(DnsStream *stream, const struct iovec *iov, size_t iovcnt) { ssize_t ss; assert(stream); assert(stream->encrypted); assert(stream->dnstls_data.session); - assert(buf); + assert(iov); + assert(IOVEC_TOTAL_SIZE(iov, iovcnt) > 0); - ss = gnutls_record_send(stream->dnstls_data.session, buf, count); + gnutls_record_cork(stream->dnstls_data.session); + + for (size_t i = 0; i < iovcnt; i++) { + ss = gnutls_record_send( + stream->dnstls_data.session, + iov[i].iov_base, iov[i].iov_len); + if (ss < 0) + break; + } + + ss = gnutls_record_uncork(stream->dnstls_data.session, 0); if (ss < 0) switch(ss) { case GNUTLS_E_INTERRUPTED: diff --git a/src/resolve/resolved-dnstls-openssl.c b/src/resolve/resolved-dnstls-openssl.c index cba3f14f2..4d3a88c8d 100644 --- a/src/resolve/resolved-dnstls-openssl.c +++ b/src/resolve/resolved-dnstls-openssl.c @@ -292,15 +292,10 @@ int dnstls_stream_shutdown(DnsStream *stream, int error) { return 0; } -ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) { +static ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) { int error, r; ssize_t ss; - assert(stream); - assert(stream->encrypted); - assert(stream->dnstls_data.ssl); - assert(buf); - ERR_clear_error(); ss = r = SSL_write(stream->dnstls_data.ssl, buf, count); if (r <= 0) { @@ -329,6 +324,29 @@ ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count) { return ss; } +ssize_t dnstls_stream_writev(DnsStream *stream, const struct iovec *iov, size_t iovcnt) { + _cleanup_free_ char *buf = NULL; + size_t count; + + assert(stream); + assert(stream->encrypted); + assert(stream->dnstls_data.ssl); + assert(iov); + assert(IOVEC_TOTAL_SIZE(iov, iovcnt) > 0); + + if (iovcnt == 1) + return dnstls_stream_write(stream, iov[0].iov_base, iov[0].iov_len); + + /* As of now, OpenSSL can not accumulate multiple writes, so join into a + single buffer. Suboptimal, but better than multiple SSL_write calls. */ + count = IOVEC_TOTAL_SIZE(iov, iovcnt); + buf = new(char, count); + for (size_t i = 0, pos = 0; i < iovcnt; pos += iov[i].iov_len, i++) + memcpy(buf + pos, iov[i].iov_base, iov[i].iov_len); + + return dnstls_stream_write(stream, buf, count); +} + ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) { int error, r; ssize_t ss; @@ -343,7 +361,15 @@ ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count) { if (r <= 0) { error = SSL_get_error(stream->dnstls_data.ssl, r); if (IN_SET(error, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE)) { - stream->dnstls_events = error == SSL_ERROR_WANT_READ ? EPOLLIN : EPOLLOUT; + /* If we receive SSL_ERROR_WANT_READ here, there are two possible scenarios: + * OpenSSL needs to renegotiate (so we want to get an EPOLLIN event), or + * There is no more application data is available, so we can just return + And apparently there's no nice way to distinguish between the two. + To handle this, never set EPOLLIN and just continue as usual. + If OpenSSL really wants to read due to renegotiation, it will tell us + again on SSL_write (at which point we will request EPOLLIN force a read); + or we will just eventually read data anyway while we wait for a packet */ + stream->dnstls_events = error == SSL_ERROR_WANT_READ ? 0 : EPOLLOUT; ss = -EAGAIN; } else if (error == SSL_ERROR_ZERO_RETURN) { stream->dnstls_events = 0; diff --git a/src/resolve/resolved-dnstls.h b/src/resolve/resolved-dnstls.h index b638d61ec..cda97e0b1 100644 --- a/src/resolve/resolved-dnstls.h +++ b/src/resolve/resolved-dnstls.h @@ -4,6 +4,7 @@ #if ENABLE_DNS_OVER_TLS #include +#include typedef struct DnsServer DnsServer; typedef struct DnsStream DnsStream; @@ -26,7 +27,7 @@ int dnstls_stream_connect_tls(DnsStream *stream, DnsServer *server); void dnstls_stream_free(DnsStream *stream); int dnstls_stream_on_io(DnsStream *stream, uint32_t revents); int dnstls_stream_shutdown(DnsStream *stream, int error); -ssize_t dnstls_stream_write(DnsStream *stream, const char *buf, size_t count); +ssize_t dnstls_stream_writev(DnsStream *stream, const struct iovec *iov, size_t iovcnt); ssize_t dnstls_stream_read(DnsStream *stream, void *buf, size_t count); void dnstls_server_free(DnsServer *server); diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c index a8da6c3d8..a1d797b11 100644 --- a/src/resolve/resolved-etc-hosts.c +++ b/src/resolve/resolved-etc-hosts.c @@ -192,7 +192,6 @@ static void strip_localhost(EtcHosts *hosts) { for (size_t j = 0; j < ELEMENTSOF(local_in_addrs); j++) { bool all_localhost, in_order; - char **i; item = hashmap_get(hosts->by_address, local_in_addrs + j); if (!item) @@ -295,7 +294,7 @@ static int manager_etc_hosts_read(Manager *m) { usec_t ts; int r; - assert_se(sd_event_now(m->event, clock_boottime_or_monotonic(), &ts) >= 0); + assert_se(sd_event_now(m->event, CLOCK_BOOTTIME, &ts) >= 0); /* See if we checked /etc/hosts recently already */ if (m->etc_hosts_last != USEC_INFINITY && m->etc_hosts_last + ETC_HOSTS_RECHECK_USEC > ts) @@ -392,8 +391,6 @@ int manager_etc_hosts_lookup(Manager *m, DnsQuestion* q, DnsAnswer **answer) { } if (found_ptr) { - char **n; - r = dns_answer_reserve(answer, strv_length(item->names)); if (r < 0) return r; diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c index 8d533d7ec..881b65bb2 100644 --- a/src/resolve/resolved-link-bus.c +++ b/src/resolve/resolved-link-bus.c @@ -51,7 +51,6 @@ static int property_get_dns_internal( bool extended) { Link *l = userdata; - DnsServer *s; int r; assert(reply); @@ -144,7 +143,6 @@ static int property_get_domains( sd_bus_error *error) { Link *l = userdata; - DnsSearchDomain *d; int r; assert(reply); @@ -706,7 +704,6 @@ int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, v _cleanup_free_ char *j = NULL; Link *l = userdata; int r; - char **i; assert(message); assert(l); diff --git a/src/resolve/resolved-link.c b/src/resolve/resolved-link.c index 6c910498a..8027eb6f9 100644 --- a/src/resolve/resolved-link.c +++ b/src/resolve/resolved-link.c @@ -11,6 +11,7 @@ #include "fileio.h" #include "log-link.h" #include "mkdir.h" +#include "netif-util.h" #include "parse-util.h" #include "resolved-link.h" #include "resolved-llmnr.h" @@ -133,7 +134,7 @@ void link_allocate_scopes(Link *l) { r = dns_scope_new(l->manager, &l->unicast_scope, l, DNS_PROTOCOL_DNS, AF_UNSPEC); if (r < 0) - log_warning_errno(r, "Failed to allocate DNS scope: %m"); + log_link_warning_errno(l, r, "Failed to allocate DNS scope, ignoring: %m"); } } else l->unicast_scope = dns_scope_free(l->unicast_scope); @@ -144,7 +145,7 @@ void link_allocate_scopes(Link *l) { if (!l->llmnr_ipv4_scope) { r = dns_scope_new(l->manager, &l->llmnr_ipv4_scope, l, DNS_PROTOCOL_LLMNR, AF_INET); if (r < 0) - log_warning_errno(r, "Failed to allocate LLMNR IPv4 scope: %m"); + log_link_warning_errno(l, r, "Failed to allocate LLMNR IPv4 scope, ignoring: %m"); } } else l->llmnr_ipv4_scope = dns_scope_free(l->llmnr_ipv4_scope); @@ -156,7 +157,7 @@ void link_allocate_scopes(Link *l) { if (!l->llmnr_ipv6_scope) { r = dns_scope_new(l->manager, &l->llmnr_ipv6_scope, l, DNS_PROTOCOL_LLMNR, AF_INET6); if (r < 0) - log_warning_errno(r, "Failed to allocate LLMNR IPv6 scope: %m"); + log_link_warning_errno(l, r, "Failed to allocate LLMNR IPv6 scope, ignoring: %m"); } } else l->llmnr_ipv6_scope = dns_scope_free(l->llmnr_ipv6_scope); @@ -167,7 +168,7 @@ void link_allocate_scopes(Link *l) { if (!l->mdns_ipv4_scope) { r = dns_scope_new(l->manager, &l->mdns_ipv4_scope, l, DNS_PROTOCOL_MDNS, AF_INET); if (r < 0) - log_warning_errno(r, "Failed to allocate mDNS IPv4 scope: %m"); + log_link_warning_errno(l, r, "Failed to allocate mDNS IPv4 scope, ignoring: %m"); } } else l->mdns_ipv4_scope = dns_scope_free(l->mdns_ipv4_scope); @@ -178,14 +179,13 @@ void link_allocate_scopes(Link *l) { if (!l->mdns_ipv6_scope) { r = dns_scope_new(l->manager, &l->mdns_ipv6_scope, l, DNS_PROTOCOL_MDNS, AF_INET6); if (r < 0) - log_warning_errno(r, "Failed to allocate mDNS IPv6 scope: %m"); + log_link_warning_errno(l, r, "Failed to allocate mDNS IPv6 scope, ignoring: %m"); } } else l->mdns_ipv6_scope = dns_scope_free(l->mdns_ipv6_scope); } void link_add_rrs(Link *l, bool force_remove) { - LinkAddress *a; int r; LIST_FOREACH(addresses, a, l->addresses) @@ -198,13 +198,13 @@ void link_add_rrs(Link *l, bool force_remove) { if (l->mdns_ipv4_scope) { r = dns_scope_add_dnssd_services(l->mdns_ipv4_scope); if (r < 0) - log_warning_errno(r, "Failed to add IPv4 DNS-SD services: %m"); + log_link_warning_errno(l, r, "Failed to add IPv4 DNS-SD services, ignoring: %m"); } if (l->mdns_ipv6_scope) { r = dns_scope_add_dnssd_services(l->mdns_ipv6_scope); if (r < 0) - log_warning_errno(r, "Failed to add IPv6 DNS-SD services: %m"); + log_link_warning_errno(l, r, "Failed to add IPv6 DNS-SD services, ignoring: %m"); } } else { @@ -212,13 +212,13 @@ void link_add_rrs(Link *l, bool force_remove) { if (l->mdns_ipv4_scope) { r = dns_scope_remove_dnssd_services(l->mdns_ipv4_scope); if (r < 0) - log_warning_errno(r, "Failed to remove IPv4 DNS-SD services: %m"); + log_link_warning_errno(l, r, "Failed to remove IPv4 DNS-SD services, ignoring: %m"); } if (l->mdns_ipv6_scope) { r = dns_scope_remove_dnssd_services(l->mdns_ipv6_scope); if (r < 0) - log_warning_errno(r, "Failed to remove IPv6 DNS-SD services: %m"); + log_link_warning_errno(l, r, "Failed to remove IPv6 DNS-SD services, ignoring: %m"); } } } @@ -237,15 +237,16 @@ int link_process_rtnl(Link *l, sd_netlink_message *m) { (void) sd_netlink_message_read_u32(m, IFLA_MTU, &l->mtu); (void) sd_netlink_message_read_u8(m, IFLA_OPERSTATE, &l->operstate); - if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0) { + if (sd_netlink_message_read_string(m, IFLA_IFNAME, &n) >= 0 && + !streq_ptr(l->ifname, n)) { + if (l->ifname) + log_link_debug(l, "Interface name change detected: %s -> %s", l->ifname, n); + r = free_and_strdup(&l->ifname, n); if (r < 0) return r; } - link_allocate_scopes(l); - link_add_rrs(l, false); - return 0; } @@ -282,7 +283,6 @@ static int link_update_dns_server_one(Link *l, const char *str) { static int link_update_dns_servers(Link *l) { _cleanup_strv_free_ char **nameservers = NULL; - char **nameserver; int r; assert(l); @@ -382,7 +382,10 @@ void link_set_dns_over_tls_mode(Link *l, DnsOverTlsMode mode) { #if ! ENABLE_DNS_OVER_TLS if (mode != DNS_OVER_TLS_NO) - log_warning("DNS-over-TLS option for the link cannot be enabled or set to opportunistic when systemd-resolved is built without DNS-over-TLS support. Turning off DNS-over-TLS support."); + log_link_warning(l, + "DNS-over-TLS option for the link cannot be enabled or set to opportunistic " + "when systemd-resolved is built without DNS-over-TLS support. " + "Turning off DNS-over-TLS support."); return; #endif @@ -417,7 +420,10 @@ void link_set_dnssec_mode(Link *l, DnssecMode mode) { #if !HAVE_OPENSSL_OR_GCRYPT if (IN_SET(mode, DNSSEC_YES, DNSSEC_ALLOW_DOWNGRADE)) - log_warning("DNSSEC option for the link cannot be enabled or set to allow-downgrade when systemd-resolved is built without a cryptographic library. Turning off DNSSEC support."); + log_link_warning(l, + "DNSSEC option for the link cannot be enabled or set to allow-downgrade " + "when systemd-resolved is built without a cryptographic library. " + "Turning off DNSSEC support."); return; #endif @@ -511,7 +517,6 @@ static int link_update_search_domain_one(Link *l, const char *name, bool route_o static int link_update_search_domains(Link *l) { _cleanup_strv_free_ char **sdomains = NULL, **rdomains = NULL; - char **i; int r, q; assert(l); @@ -608,6 +613,10 @@ static void link_read_settings(Link *l) { l->is_managed = true; + r = network_link_get_operational_state(l->ifindex, &l->networkd_operstate); + if (r < 0) + log_link_warning_errno(l, r, "Failed to read networkd's link operational state, ignoring: %m"); + r = link_update_dns_servers(l); if (r < 0) log_link_warning_errno(l, r, "Failed to read DNS servers for the interface, ignoring: %m"); @@ -670,9 +679,6 @@ int link_update(Link *l) { } bool link_relevant(Link *l, int family, bool local_multicast) { - _cleanup_free_ char *state = NULL; - LinkAddress *a; - assert(l); /* A link is relevant for local multicast traffic if it isn't a loopback device, has a link @@ -681,24 +687,21 @@ bool link_relevant(Link *l, int family, bool local_multicast) { * A link is relevant for non-multicast traffic if it isn't a loopback device, has a link beat, and has at * least one routable address. */ - if (l->flags & (IFF_LOOPBACK|IFF_DORMANT)) + if ((l->flags & (IFF_LOOPBACK | IFF_DORMANT)) != 0) return false; - if ((l->flags & (IFF_UP|IFF_LOWER_UP)) != (IFF_UP|IFF_LOWER_UP)) + if (!FLAGS_SET(l->flags, IFF_UP | IFF_LOWER_UP)) return false; - if (local_multicast) { - if ((l->flags & IFF_MULTICAST) != IFF_MULTICAST) - return false; - } - - /* Check kernel operstate - * https://www.kernel.org/doc/Documentation/networking/operstates.txt */ - if (!IN_SET(l->operstate, IF_OPER_UNKNOWN, IF_OPER_UP)) + if (local_multicast && + !FLAGS_SET(l->flags, IFF_MULTICAST)) return false; - (void) sd_network_link_get_operational_state(l->ifindex, &state); - if (state && !STR_IN_SET(state, "unknown", "degraded", "degraded-carrier", "routable")) + if (!netif_has_carrier(l->operstate, l->flags)) + return false; + + if (l->is_managed && + !IN_SET(l->networkd_operstate, LINK_OPERSTATE_DEGRADED_CARRIER, LINK_OPERSTATE_DEGRADED, LINK_OPERSTATE_ROUTABLE)) return false; LIST_FOREACH(addresses, a, l->addresses) @@ -709,8 +712,6 @@ bool link_relevant(Link *l, int family, bool local_multicast) { } LinkAddress *link_find_address(Link *l, int family, const union in_addr_union *in_addr) { - LinkAddress *a; - assert(l); if (!IN_SET(family, AF_INET, AF_INET6)) @@ -733,7 +734,7 @@ DnsServer* link_set_dns_server(Link *l, DnsServer *s) { return s; if (s) - log_debug("Switching to DNS server %s for interface %s.", strna(dns_server_string_full(s)), l->ifname); + log_link_debug(l, "Switching to DNS server %s.", strna(dns_server_string_full(s))); dns_server_unref(l->current_dns_server); l->current_dns_server = dns_server_ref(s); @@ -925,11 +926,11 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_address_rr, true); if (r < 0) - log_warning_errno(r, "Failed to add A record to LLMNR zone: %m"); + log_link_warning_errno(a->link, r, "Failed to add A record to LLMNR zone, ignoring: %m"); r = dns_zone_put(&a->link->llmnr_ipv4_scope->zone, a->link->llmnr_ipv4_scope, a->llmnr_ptr_rr, false); if (r < 0) - log_warning_errno(r, "Failed to add IPv4 PTR record to LLMNR zone: %m"); + log_link_warning_errno(a->link, r, "Failed to add IPv4 PTR record to LLMNR zone, ignoring: %m"); } else { if (a->llmnr_address_rr) { if (a->link->llmnr_ipv4_scope) @@ -978,11 +979,11 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_address_rr, true); if (r < 0) - log_warning_errno(r, "Failed to add A record to MDNS zone: %m"); + log_link_warning_errno(a->link, r, "Failed to add A record to MDNS zone, ignoring: %m"); r = dns_zone_put(&a->link->mdns_ipv4_scope->zone, a->link->mdns_ipv4_scope, a->mdns_ptr_rr, false); if (r < 0) - log_warning_errno(r, "Failed to add IPv4 PTR record to MDNS zone: %m"); + log_link_warning_errno(a->link, r, "Failed to add IPv4 PTR record to MDNS zone, ignoring: %m"); } else { if (a->mdns_address_rr) { if (a->link->mdns_ipv4_scope) @@ -1035,11 +1036,11 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_address_rr, true); if (r < 0) - log_warning_errno(r, "Failed to add AAAA record to LLMNR zone: %m"); + log_link_warning_errno(a->link, r, "Failed to add AAAA record to LLMNR zone, ignoring: %m"); r = dns_zone_put(&a->link->llmnr_ipv6_scope->zone, a->link->llmnr_ipv6_scope, a->llmnr_ptr_rr, false); if (r < 0) - log_warning_errno(r, "Failed to add IPv6 PTR record to LLMNR zone: %m"); + log_link_warning_errno(a->link, r, "Failed to add IPv6 PTR record to LLMNR zone, ignoring: %m"); } else { if (a->llmnr_address_rr) { if (a->link->llmnr_ipv6_scope) @@ -1089,11 +1090,11 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_address_rr, true); if (r < 0) - log_warning_errno(r, "Failed to add AAAA record to MDNS zone: %m"); + log_link_warning_errno(a->link, r, "Failed to add AAAA record to MDNS zone, ignoring: %m"); r = dns_zone_put(&a->link->mdns_ipv6_scope->zone, a->link->mdns_ipv6_scope, a->mdns_ptr_rr, false); if (r < 0) - log_warning_errno(r, "Failed to add IPv6 PTR record to MDNS zone: %m"); + log_link_warning_errno(a->link, r, "Failed to add IPv6 PTR record to MDNS zone, ignoring: %m"); } else { if (a->mdns_address_rr) { if (a->link->mdns_ipv6_scope) @@ -1112,7 +1113,7 @@ void link_address_add_rrs(LinkAddress *a, bool force_remove) { return; fail: - log_debug_errno(r, "Failed to update address RRs: %m"); + log_link_debug_errno(a->link, r, "Failed to update address RRs, ignoring: %m"); } int link_address_update_rtnl(LinkAddress *a, sd_netlink_message *m) { @@ -1215,8 +1216,6 @@ int link_save_user(Link *l) { fprintf(f, "DEFAULT_ROUTE=%s\n", yes_no(l->default_route)); if (l->dns_servers) { - DnsServer *server; - fputs("SERVERS=", f); LIST_FOREACH(servers, server, l->dns_servers) { @@ -1235,8 +1234,6 @@ int link_save_user(Link *l) { } if (l->search_domains) { - DnsSearchDomain *domain; - fputs("DOMAINS=", f); LIST_FOREACH(domains, domain, l->search_domains) { @@ -1284,7 +1281,7 @@ fail: if (temp_path) (void) unlink(temp_path); - return log_error_errno(r, "Failed to save link data %s: %m", l->state_file); + return log_link_error_errno(l, r, "Failed to save link data %s: %m", l->state_file); } int link_load_user(Link *l) { @@ -1354,7 +1351,7 @@ int link_load_user(Link *l) { r = link_update_dns_server_one(l, word); if (r < 0) { - log_debug_errno(r, "Failed to load DNS server '%s', ignoring: %m", word); + log_link_debug_errno(l, r, "Failed to load DNS server '%s', ignoring: %m", word); continue; } } @@ -1375,7 +1372,7 @@ int link_load_user(Link *l) { r = link_update_search_domain_one(l, n, is_route); if (r < 0) { - log_debug_errno(r, "Failed to load search domain '%s', ignoring: %m", word); + log_link_debug_errno(l, r, "Failed to load search domain '%s', ignoring: %m", word); continue; } } @@ -1399,7 +1396,7 @@ int link_load_user(Link *l) { return 0; fail: - return log_error_errno(r, "Failed to load link data %s: %m", l->state_file); + return log_link_error_errno(l, r, "Failed to load link data %s: %m", l->state_file); } void link_remove_user(Link *l) { diff --git a/src/resolve/resolved-link.h b/src/resolve/resolved-link.h index f65718ce3..b5299e0b5 100644 --- a/src/resolve/resolved-link.h +++ b/src/resolve/resolved-link.h @@ -6,6 +6,7 @@ #include "sd-netlink.h" #include "in-addr-util.h" +#include "network-util.h" #include "ratelimit.h" #include "resolve-util.h" @@ -68,6 +69,7 @@ struct Link { DnsScope *mdns_ipv6_scope; struct stat networkd_state_file_stat; + LinkOperationalState networkd_operstate; bool is_managed; char *ifname; diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index 32483006b..76e42940f 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -277,13 +277,11 @@ int manager_llmnr_ipv6_udp_fd(Manager *m) { return m->llmnr_ipv6_udp_fd = TAKE_FD(s); } -static int on_llmnr_stream_packet(DnsStream *s) { - _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; +static int on_llmnr_stream_packet(DnsStream *s, DnsPacket *p) { DnsScope *scope; assert(s); - - p = dns_stream_take_read_packet(s); + assert(s->manager); assert(p); scope = manager_find_scope(s->manager, p); @@ -296,7 +294,6 @@ static int on_llmnr_stream_packet(DnsStream *s) { } else log_debug("Invalid LLMNR TCP packet, ignoring."); - dns_stream_unref(s); return 0; } @@ -313,15 +310,14 @@ static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *u return -errno; } - r = dns_stream_new(m, &stream, DNS_STREAM_LLMNR_RECV, DNS_PROTOCOL_LLMNR, cfd, NULL, DNS_STREAM_DEFAULT_TIMEOUT_USEC); + /* We don't configure a "complete" handler here, we rely on the default handler, thus freeing it */ + r = dns_stream_new(m, &stream, DNS_STREAM_LLMNR_RECV, DNS_PROTOCOL_LLMNR, cfd, NULL, + on_llmnr_stream_packet, NULL, DNS_STREAM_DEFAULT_TIMEOUT_USEC); if (r < 0) { safe_close(cfd); return r; } - stream->on_packet = on_llmnr_stream_packet; - /* We don't configure a "complete" handler here, we rely on the default handler than simply drops the - * reference to the stream, thus freeing it */ return 0; } diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index d10fd73d4..12e7d87f2 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -13,6 +13,7 @@ #include "bus-polkit.h" #include "dirent-util.h" #include "dns-domain.h" +#include "event-util.h" #include "fd-util.h" #include "fileio.h" #include "hostname-util.h" @@ -338,28 +339,16 @@ static int on_clock_change(sd_event_source *source, int fd, uint32_t revents, vo } static int manager_clock_change_listen(Manager *m) { - _cleanup_close_ int fd = -1; int r; assert(m); m->clock_change_event_source = sd_event_source_disable_unref(m->clock_change_event_source); - fd = time_change_fd(); - if (fd < 0) - return log_error_errno(fd, "Failed to allocate clock change timer fd: %m"); - - r = sd_event_add_io(m->event, &m->clock_change_event_source, fd, EPOLLIN, on_clock_change, m); + r = event_add_time_change(m->event, &m->clock_change_event_source, on_clock_change, m); if (r < 0) return log_error_errno(r, "Failed to create clock change event source: %m"); - r = sd_event_source_set_io_fd_own(m->clock_change_event_source, true); - if (r < 0) - return log_error_errno(r, "Failed to pass ownership of clock fd to event source: %m"); - TAKE_FD(fd); - - (void) sd_event_source_set_description(m->clock_change_event_source, "clock-change"); - return 0; } @@ -514,9 +503,7 @@ static int manager_sigusr1(sd_event_source *s, const struct signalfd_siginfo *si _cleanup_free_ char *buffer = NULL; _cleanup_fclose_ FILE *f = NULL; Manager *m = userdata; - DnsServer *server; size_t size = 0; - DnsScope *scope; Link *l; assert(s); @@ -816,7 +803,7 @@ int manager_recv(Manager *m, int fd, DnsProtocol protocol, DnsPacket **ret) { } else return -EAFNOSUPPORT; - p->timestamp = now(clock_boottime_or_monotonic()); + p->timestamp = now(CLOCK_BOOTTIME); CMSG_FOREACH(cmsg, &mh) { @@ -1316,8 +1303,6 @@ DnsScope* manager_find_scope(Manager *m, DnsPacket *p) { } void manager_verify_all(Manager *m) { - DnsScope *s; - assert(m); LIST_FOREACH(scopes, s, m->dns_scopes) @@ -1349,7 +1334,6 @@ int manager_is_own_hostname(Manager *m, const char *name) { } int manager_compile_dns_servers(Manager *m, OrderedSet **dns) { - DnsServer *s; Link *l; int r; @@ -1400,7 +1384,6 @@ int manager_compile_dns_servers(Manager *m, OrderedSet **dns) { * > 0 or true: return only domains which are for routing only */ int manager_compile_search_domains(Manager *m, OrderedSet **domains, int filter_route) { - DnsSearchDomain *d; Link *l; int r; @@ -1512,8 +1495,6 @@ bool manager_routable(Manager *m) { } void manager_flush_caches(Manager *m, int log_level) { - DnsScope *scope; - assert(m); LIST_FOREACH(scopes, scope, m->dns_scopes) @@ -1619,37 +1600,30 @@ bool manager_next_dnssd_names(Manager *m) { return tried; } -bool manager_server_address_is_stub(Manager *m, int family, const union in_addr_union *address, uint16_t port) { +bool manager_server_is_stub(Manager *m, DnsServer *s) { DnsStubListenerExtra *l; assert(m); - assert(address); + assert(s); /* Safety check: we generally already skip the main stub when parsing configuration. But let's be * extra careful, and check here again */ - if (family == AF_INET && - address->in.s_addr == htobe32(INADDR_DNS_STUB) && - port == 53) + if (s->family == AF_INET && + s->address.in.s_addr == htobe32(INADDR_DNS_STUB) && + dns_server_port(s) == 53) return true; /* Main reason to call this is to check server data against the extra listeners, and filter things * out. */ ORDERED_SET_FOREACH(l, m->dns_extra_stub_listeners) - if (family == l->family && - in_addr_equal(family, address, &l->address) && - port == dns_stub_listener_extra_port(l)) + if (s->family == l->family && + in_addr_equal(s->family, &s->address, &l->address) && + dns_server_port(s) == dns_stub_listener_extra_port(l)) return true; return false; } -bool manager_server_is_stub(Manager *m, DnsServer *s) { - assert(m); - assert(s); - - return manager_server_address_is_stub(m, s->family, &s->address, dns_server_port(s)); -} - int socket_disable_pmtud(int fd, int af) { int r; diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index e96973897..35e0068a8 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -207,7 +207,6 @@ void manager_cleanup_saved_user(Manager *m); bool manager_next_dnssd_names(Manager *m); -bool manager_server_address_is_stub(Manager *m, int family, const union in_addr_union *address, uint16_t port); bool manager_server_is_stub(Manager *m, DnsServer *s); int socket_disable_pmtud(int fd, int af); diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index 0d19d0845..cf855a614 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -203,7 +203,6 @@ static bool mdns_should_reply_using_unicast(DnsPacket *p) { } static bool sender_on_local_subnet(DnsScope *s, DnsPacket *p) { - LinkAddress *a; int r; /* Check whether the sender is on a local subnet. */ @@ -359,7 +358,6 @@ static int on_mdns_packet(sd_event_source *s, int fd, uint32_t revents, void *us if (dns_packet_validate_reply(p) > 0) { DnsResourceRecord *rr; - DnsTransaction *t; log_debug("Got mDNS reply packet"); @@ -494,6 +492,8 @@ int manager_mdns_ipv4_fd(Manager *m) { if (r < 0) return log_error_errno(r, "mDNS-IPv4: Failed to create event source: %m"); + (void) sd_event_source_set_description(m->mdns_ipv4_event_source, "mdns-ipv4"); + return m->mdns_ipv4_fd = TAKE_FD(s); } @@ -518,7 +518,7 @@ int manager_mdns_ipv6_fd(Manager *m) { if (r < 0) return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_UNICAST_HOPS: %m"); - /* RFC 4795, section 2.5 recommends setting the TTL of UDP packets to 255. */ + /* RFC 6762, section 11 recommends setting the TTL of UDP packets to 255. */ r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, 255); if (r < 0) return log_error_errno(r, "mDNS-IPv6: Failed to set IPV6_MULTICAST_HOPS: %m"); @@ -567,5 +567,7 @@ int manager_mdns_ipv6_fd(Manager *m) { if (r < 0) return log_error_errno(r, "mDNS-IPv6: Failed to create event source: %m"); + (void) sd_event_source_set_description(m->mdns_ipv6_event_source, "mdns-ipv6"); + return m->mdns_ipv6_fd = TAKE_FD(s); } diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index e9785ab96..5749aa1dc 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -41,8 +41,7 @@ int manager_check_resolv_conf(const Manager *m) { /* Is it symlinked to our own uplink file? */ if (stat(PRIVATE_STATIC_RESOLV_CONF, &own) >= 0 && - st.st_dev == own.st_dev && - st.st_ino == own.st_ino) + stat_inode_same(&st, &own)) return log_warning_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "DNSStubListener= is disabled, but /etc/resolv.conf is a symlink to " PRIVATE_STATIC_RESOLV_CONF " which expects DNSStubListener= to be enabled."); @@ -51,8 +50,6 @@ int manager_check_resolv_conf(const Manager *m) { } static bool file_is_our_own(const struct stat *st) { - const char *path; - assert(st); FOREACH_STRING(path, @@ -64,8 +61,7 @@ static bool file_is_our_own(const struct stat *st) { /* Is it symlinked to our own uplink file? */ if (stat(path, &own) >= 0 && - st->st_dev == own.st_dev && - st->st_ino == own.st_ino) + stat_inode_same(st, &own)) return true; } @@ -143,8 +139,7 @@ int manager_read_resolv_conf(Manager *m) { a = first_word(l, "nameserver"); if (a) { - r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_SYSTEM, a, - true /* don't warn about loops to our own stub listeners */); + r = manager_parse_dns_server_string_and_warn(m, DNS_SERVER_SYSTEM, a); if (r < 0) log_warning_errno(r, "Failed to parse DNS server address '%s', ignoring.", a); @@ -418,8 +413,7 @@ int resolv_conf_mode(void) { continue; } - if (system_st.st_dev == our_st.st_dev && - system_st.st_ino == our_st.st_ino) + if (stat_inode_same(&system_st, &our_st)) return m; } diff --git a/src/resolve/resolved-socket-graveyard.c b/src/resolve/resolved-socket-graveyard.c index e659ae918..6d4105bc0 100644 --- a/src/resolve/resolved-socket-graveyard.c +++ b/src/resolve/resolved-socket-graveyard.c @@ -53,7 +53,7 @@ void manager_socket_graveyard_process(Manager *m) { SocketGraveyard *g = m->socket_graveyard_oldest; if (n == USEC_INFINITY) - assert_se(sd_event_now(m->event, clock_boottime_or_monotonic(), &n) >= 0); + assert_se(sd_event_now(m->event, CLOCK_BOOTTIME, &n) >= 0); if (g->deadline > n) break; @@ -113,7 +113,7 @@ int manager_add_socket_to_graveyard(Manager *m, int fd) { m->n_socket_graveyard++; - assert_se(sd_event_now(m->event, clock_boottime_or_monotonic(), &g->deadline) >= 0); + assert_se(sd_event_now(m->event, CLOCK_BOOTTIME, &g->deadline) >= 0); g->deadline += SOCKET_GRAVEYARD_USEC; r = sd_event_add_io(m->event, &g->io_event_source, fd, EPOLLIN, on_io_event, g); diff --git a/src/resolve/test-dnssec.c b/src/resolve/test-dnssec.c index 326309536..d325b533e 100644 --- a/src/resolve/test-dnssec.c +++ b/src/resolve/test-dnssec.c @@ -9,13 +9,13 @@ #endif #include "alloc-util.h" +#include "hexdecoct.h" #include "resolved-dns-dnssec.h" #include "resolved-dns-rr.h" #include "string-util.h" -#include "hexdecoct.h" - -static void test_dnssec_verify_dns_key(void) { +#include "tests.h" +TEST(dnssec_verify_dns_key) { static const uint8_t ds1_fprint[] = { 0x46, 0x8B, 0xC8, 0xDD, 0xC7, 0xE8, 0x27, 0x03, 0x40, 0xBB, 0x8A, 0x1F, 0x3B, 0x2E, 0x45, 0x9D, 0x80, 0x67, 0x14, 0x01, @@ -88,7 +88,7 @@ static void test_dnssec_verify_dns_key(void) { assert_se(dnssec_verify_dnskey_by_ds(dnskey, ds2, false) > 0); } -static void test_dnssec_verify_rfc8080_ed25519_example1(void) { +TEST(dnssec_verify_rfc8080_ed25519_example1) { static const uint8_t dnskey_blob[] = { 0x97, 0x4d, 0x96, 0xa2, 0x2d, 0x22, 0x4b, 0xc0, 0x1a, 0xdb, 0x91, 0x50, 0x91, 0x47, 0x7d, 0x44, 0xcc, 0xd9, 0x1c, 0x9a, 0x41, 0xa1, 0x14, 0x30, 0x01, 0x01, 0x17, 0xd5, 0x2c, 0x59, @@ -180,7 +180,7 @@ static void test_dnssec_verify_rfc8080_ed25519_example1(void) { #endif } -static void test_dnssec_verify_rfc8080_ed25519_example2(void) { +TEST(dnssec_verify_rfc8080_ed25519_example2) { static const uint8_t dnskey_blob[] = { 0xcc, 0xf9, 0xd9, 0xfd, 0x0c, 0x04, 0x7b, 0xb4, 0xbc, 0x0b, 0x94, 0x8f, 0xcf, 0x63, 0x9f, 0x4b, 0x94, 0x51, 0xe3, 0x40, 0x13, 0x93, 0x6f, 0xeb, 0x62, 0x71, 0x3d, 0xc4, 0x72, 0x4, @@ -272,7 +272,7 @@ static void test_dnssec_verify_rfc8080_ed25519_example2(void) { #endif } -static void test_dnssec_verify_rfc6605_example1(void) { +TEST(dnssec_verify_rfc6605_example1) { static const uint8_t signature_blob[] = { 0xab, 0x1e, 0xb0, 0x2d, 0x8a, 0xa6, 0x87, 0xe9, 0x7d, 0xa0, 0x22, 0x93, 0x37, 0xaa, 0x88, 0x73, 0xe6, 0xf0, 0xeb, 0x26, 0xbe, 0x28, 0x9f, 0x28, 0x33, 0x3d, 0x18, 0x3f, 0x5d, 0x3b, 0x7a, 0x95, @@ -359,7 +359,7 @@ static void test_dnssec_verify_rfc6605_example1(void) { assert_se(result == DNSSEC_VALIDATED); } -static void test_dnssec_verify_rfc6605_example2(void) { +TEST(dnssec_verify_rfc6605_example2) { static const uint8_t signature_blob[] = { 0xfc, 0xbe, 0x61, 0x0c, 0xa2, 0x2f, 0x18, 0x3c, 0x88, 0xd5, 0xf7, 0x00, 0x45, 0x7d, 0xf3, 0xeb, 0x9a, 0xab, 0x98, 0xfb, 0x15, 0xcf, 0xbd, 0xd0, 0x0f, 0x53, 0x2b, 0xe4, 0x21, 0x2a, 0x3a, 0x22, @@ -454,8 +454,7 @@ static void test_dnssec_verify_rfc6605_example2(void) { assert_se(result == DNSSEC_VALIDATED); } -static void test_dnssec_verify_rrset(void) { - +TEST(dnssec_verify_rrset) { static const uint8_t signature_blob[] = { 0x7f, 0x79, 0xdd, 0x5e, 0x89, 0x79, 0x18, 0xd0, 0x34, 0x86, 0x8c, 0x72, 0x77, 0x75, 0x48, 0x4d, 0xc3, 0x7d, 0x38, 0x04, 0xab, 0xcd, 0x9e, 0x4c, 0x82, 0xb0, 0x92, 0xca, 0xe9, 0x66, 0xe9, 0x6e, @@ -533,8 +532,7 @@ static void test_dnssec_verify_rrset(void) { assert_se(result == DNSSEC_VALIDATED); } -static void test_dnssec_verify_rrset2(void) { - +TEST(dnssec_verify_rrset2) { static const uint8_t signature_blob[] = { 0x48, 0x45, 0xc8, 0x8b, 0xc0, 0x14, 0x92, 0xf5, 0x15, 0xc6, 0x84, 0x9d, 0x2f, 0xe3, 0x32, 0x11, 0x7d, 0xf1, 0xe6, 0x87, 0xb9, 0x42, 0xd3, 0x8b, 0x9e, 0xaf, 0x92, 0x31, 0x0a, 0x53, 0xad, 0x8b, @@ -625,8 +623,7 @@ static void test_dnssec_verify_rrset2(void) { assert_se(result == DNSSEC_VALIDATED); } -static void test_dnssec_verify_rrset3(void) { - +TEST(dnssec_verify_rrset3) { static const uint8_t signature_blob[] = { 0x41, 0x09, 0x08, 0x67, 0x51, 0x6d, 0x02, 0xf2, 0x17, 0x1e, 0x61, 0x03, 0xc6, 0x80, 0x7a, 0x82, 0x8f, 0x6c, 0x8c, 0x4c, 0x68, 0x6f, 0x1c, 0xaa, 0x4a, 0xe0, 0x9b, 0x72, 0xdf, 0x7f, 0x15, 0xfa, @@ -755,7 +752,7 @@ static void test_dnssec_verify_rrset3(void) { assert_se(result == DNSSEC_VALIDATED); } -static void test_dnssec_nsec3_hash(void) { +TEST(dnssec_nsec3_hash) { static const uint8_t salt[] = { 0xB0, 0x1D, 0xFA, 0xCE }; static const uint8_t next_hashed_name[] = { 0x84, 0x10, 0x26, 0x53, 0xc9, 0xfa, 0x4d, 0x85, 0x6c, 0x97, 0x82, 0xe2, 0x8f, 0xdf, 0x2d, 0x5e, 0x87, 0x69, 0xc4, 0x52 }; _cleanup_(dns_resource_record_unrefp) DnsResourceRecord *rr = NULL; @@ -787,16 +784,4 @@ static void test_dnssec_nsec3_hash(void) { assert_se(strcasecmp(b, "PJ8S08RR45VIQDAQGE7EN3VHKNROTBMM") == 0); } -int main(int argc, char *argv[]) { - test_dnssec_verify_dns_key(); - test_dnssec_verify_rfc8080_ed25519_example1(); - test_dnssec_verify_rfc8080_ed25519_example2(); - test_dnssec_verify_rfc6605_example1(); - test_dnssec_verify_rfc6605_example2(); - test_dnssec_verify_rrset(); - test_dnssec_verify_rrset2(); - test_dnssec_verify_rrset3(); - test_dnssec_nsec3_hash(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/resolve/test-resolved-etc-hosts.c b/src/resolve/test-resolved-etc-hosts.c index cc55a980a..d6cd184b5 100644 --- a/src/resolve/test-resolved-etc-hosts.c +++ b/src/resolve/test-resolved-etc-hosts.c @@ -14,11 +14,9 @@ #include "tests.h" #include "tmpfile-util.h" -static void test_parse_etc_hosts_system(void) { +TEST(parse_etc_hosts_system) { _cleanup_fclose_ FILE *f = NULL; - log_info("/* %s */", __func__); - f = fopen("/etc/hosts", "re"); if (!f) { assert_se(errno == ENOENT); @@ -37,15 +35,12 @@ static void test_parse_etc_hosts_system(void) { ((_addr)->family == AF_INET6 && \ !memcmp(&(_addr)->address.in6, &(struct in6_addr) { .s6_addr = __VA_ARGS__}, 16) ) -static void test_parse_etc_hosts(void) { +TEST(parse_etc_hosts) { _cleanup_(unlink_tempfilep) char t[] = "/tmp/test-resolved-etc-hosts.XXXXXX"; - log_info("/* %s */", __func__); - int fd; _cleanup_fclose_ FILE *f; - const char *s; fd = mkostemp_safe(t); assert_se(fd >= 0); @@ -128,7 +123,7 @@ static void test_parse_etc_hosts(void) { assert_se(!set_contains(hosts.no_address, "foobar.foo.foo")); } -static void test_parse_file(const char *fname) { +static void test_parse_file_one(const char *fname) { _cleanup_(etc_hosts_free) EtcHosts hosts = {}; _cleanup_fclose_ FILE *f; @@ -138,14 +133,9 @@ static void test_parse_file(const char *fname) { assert_se(etc_hosts_parse(&hosts, f) == 0); } -int main(int argc, char **argv) { - test_setup_logging(LOG_DEBUG); - - if (argc == 1) { - test_parse_etc_hosts_system(); - test_parse_etc_hosts(); - } else - test_parse_file(argv[1]); - - return 0; +TEST(parse_file) { + for (int i = 1; i < saved_argc; i++) + test_parse_file_one(saved_argv[i]); } + +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/resolve/test-resolved-packet.c b/src/resolve/test-resolved-packet.c index cd93b1c23..dd8c969b1 100644 --- a/src/resolve/test-resolved-packet.c +++ b/src/resolve/test-resolved-packet.c @@ -4,7 +4,7 @@ #include "resolved-dns-packet.h" #include "tests.h" -static void test_dns_packet_new(void) { +TEST(dns_packet_new) { size_t i; _cleanup_(dns_packet_unrefp) DnsPacket *p2 = NULL; @@ -23,10 +23,4 @@ static void test_dns_packet_new(void) { assert_se(dns_packet_new(&p2, DNS_PROTOCOL_DNS, DNS_PACKET_SIZE_MAX + 1, DNS_PACKET_SIZE_MAX) == -EFBIG); } -int main(int argc, char **argv) { - test_setup_logging(LOG_DEBUG); - - test_dns_packet_new(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/resolve/test-resolved-stream.c b/src/resolve/test-resolved-stream.c new file mode 100644 index 000000000..2f6245f40 --- /dev/null +++ b/src/resolve/test-resolved-stream.c @@ -0,0 +1,368 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fd-util.h" +#include "log.h" +#include "macro.h" +#include "process-util.h" +#include "resolved-dns-packet.h" +#include "resolved-dns-question.h" +#include "resolved-dns-rr.h" +#if ENABLE_DNS_OVER_TLS +#include "resolved-dnstls.h" +#endif +#include "resolved-dns-server.h" +#include "resolved-dns-stream.h" +#include "resolved-manager.h" +#include "sd-event.h" +#include "sparse-endian.h" +#include "tests.h" + +static struct sockaddr_in SERVER_ADDRESS; + +/* Bytes of the questions & answers used in the test, including TCP DNS 2-byte length prefix */ +static const uint8_t QUESTION_A[] = { + 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 'e', + 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x01, 0x00, 0x01 +}; +static const uint8_t QUESTION_AAAA[] = { + 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 'e', + 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x1C, 0x00, 0x01 +}; +static const uint8_t ANSWER_A[] = { + 0x00, 0x2D, 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 'e', + 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x01, 0x00, 0x01, 0xC0, + 0x0C, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x52, 0x8D, 0x00, 0x04, 0x5D, 0xB8, 0xD8, 0x22, +}; +static const uint8_t ANSWER_AAAA[] = { + 0x00, 0x39, 0x00, 0x00, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 'e', + 'x' , 'a' , 'm' , 'p' , 'l' , 'e' , 0x03, 'c' , 'o' , 'm' , 0x00, 0x00, 0x1C, 0x00, 0x01, 0xC0, + 0x0C, 0x00, 0x1C, 0x00, 0x01, 0x00, 0x00, 0x54, 0x4B, 0x00, 0x10, 0x26, 0x06, 0x28, 0x00, 0x02, + 0x20, 0x00, 0x01, 0x02, 0x48, 0x18, 0x93, 0x25, 0xC8, 0x19, 0x46, +}; + +/** + * A mock TCP DNS server that asserts certain questions are received + * and replies with the same answer every time. + */ +static void receive_and_check_question(int fd, const uint8_t *expected_question, + size_t question_size) { + uint8_t *actual_question; + size_t n_read = 0; + + actual_question = newa(uint8_t, question_size); + while (n_read < question_size) { + ssize_t r = read(fd, actual_question + n_read, question_size - n_read); + assert_se(r >= 0); + n_read += (size_t)r; + } + assert_se(n_read == question_size); + + assert_se(memcmp(expected_question, actual_question, question_size) == 0); +} + +static void send_answer(int fd, const uint8_t *answer, size_t answer_size) { + assert_se(write(fd, answer, answer_size) == (ssize_t)answer_size); +} + +/* Sends two answers together in a single write operation, + * so they hopefully end up in a single TCP packet / TLS record */ +static void send_answers_together(int fd, + const uint8_t *answer1, size_t answer1_size, + const uint8_t *answer2, size_t answer2_size) { + uint8_t *answer; + size_t answer_size = answer1_size + answer2_size; + + answer = newa(uint8_t, answer_size); + memcpy(answer, answer1, answer1_size); + memcpy(answer + answer1_size, answer2, answer2_size); + assert_se(write(fd, answer, answer_size) == (ssize_t)answer_size); +} + +static void server_handle(int fd) { + receive_and_check_question(fd, QUESTION_A, sizeof(QUESTION_A)); + send_answer(fd, ANSWER_A, sizeof(ANSWER_A)); + + receive_and_check_question(fd, QUESTION_AAAA, sizeof(QUESTION_AAAA)); + send_answer(fd, ANSWER_AAAA, sizeof(ANSWER_AAAA)); + + receive_and_check_question(fd, QUESTION_A, sizeof(QUESTION_A)); + receive_and_check_question(fd, QUESTION_AAAA, sizeof(QUESTION_AAAA)); + send_answers_together(fd, ANSWER_A, sizeof(ANSWER_A), + ANSWER_AAAA, sizeof(ANSWER_AAAA)); +} + +static void *tcp_dns_server(void *p) { + _cleanup_close_ int bindfd = -1, acceptfd = -1; + + assert_se((bindfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) >= 0); + assert_se(setsockopt(bindfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) >= 0); + assert_se(bind(bindfd, &SERVER_ADDRESS, sizeof(SERVER_ADDRESS)) >= 0); + assert_se(listen(bindfd, 1) >= 0); + assert_se((acceptfd = accept(bindfd, NULL, NULL)) >= 0); + server_handle(acceptfd); + return NULL; +} + +#if ENABLE_DNS_OVER_TLS +/* + * Spawns a DNS TLS server using the command line "openssl s_server" tool. + */ +static void *tls_dns_server(void *p) { + pid_t openssl_pid; + int r; + _cleanup_close_ int fd_server = -1, fd_tls = -1; + _cleanup_free_ char *cert_path = NULL, *key_path = NULL; + _cleanup_free_ char *ip_str = NULL, *bind_str = NULL; + + assert_se(get_testdata_dir("test-resolve/selfsigned.cert", &cert_path) >= 0); + assert_se(get_testdata_dir("test-resolve/selfsigned.key", &key_path) >= 0); + + assert_se(in_addr_to_string(SERVER_ADDRESS.sin_family, + &(union in_addr_union){.in = SERVER_ADDRESS.sin_addr}, + &ip_str) >= 0); + assert_se(asprintf(&bind_str, "%s:%d", ip_str, be16toh(SERVER_ADDRESS.sin_port)) >= 0); + + /* We will hook one of the socketpair ends to OpenSSL's TLS server + * stdin/stdout, so we will be able to read and write plaintext + * from the other end's file descriptor like an usual TCP server */ + { + int fd[2]; + assert_se(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) >= 0); + fd_server = fd[0]; + fd_tls = fd[1]; + } + + r = safe_fork_full("(test-resolved-stream-tls-openssl)", (int[]) { fd_server, fd_tls }, 2, + FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_LOG|FORK_REOPEN_LOG, &openssl_pid); + assert_se(r >= 0); + if (r == 0) { + /* Child */ + assert_se(dup2(fd_tls, STDIN_FILENO) >= 0); + assert_se(dup2(fd_tls, STDOUT_FILENO) >= 0); + close(TAKE_FD(fd_server)); + close(TAKE_FD(fd_tls)); + + execlp("openssl", "openssl", "s_server", "-accept", bind_str, + "-key", key_path, "-cert", cert_path, + "-quiet", "-naccept", "1", NULL); + log_error("exec failed, is something wrong with the 'openssl' command?"); + _exit(EXIT_FAILURE); + } else { + pthread_mutex_t *server_lock = (pthread_mutex_t *)p; + + server_handle(fd_server); + + /* Once the test is done kill the TLS server to release the port */ + assert_se(pthread_mutex_lock(server_lock) == 0); + assert_se(kill(openssl_pid, SIGTERM) >= 0); + assert_se(waitpid(openssl_pid, NULL, 0) >= 0); + assert_se(pthread_mutex_unlock(server_lock) == 0); + } + + return NULL; +} +#endif + +static const char *TEST_DOMAIN = "example.com"; +static const uint64_t EVENT_TIMEOUT_USEC = 5 * 1000 * 1000; + +static void send_simple_question(DnsStream *stream, uint16_t type) { + _cleanup_(dns_packet_unrefp) DnsPacket *p = NULL; + _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; + _cleanup_(dns_question_unrefp) DnsQuestion *question = NULL; + + assert_se(dns_packet_new(&p, DNS_PROTOCOL_DNS, 0, DNS_PACKET_SIZE_MAX) >= 0); + assert_se(question = dns_question_new(1)); + assert_se(key = dns_resource_key_new(DNS_CLASS_IN, type, TEST_DOMAIN)); + assert_se(dns_question_add(question, key, 0) >= 0); + assert_se(dns_packet_append_question(p, question) >= 0); + DNS_PACKET_HEADER(p)->qdcount = htobe16(dns_question_size(question)); + assert_se(dns_stream_write_packet(stream, p) >= 0); +} + +static const size_t MAX_RECEIVED_PACKETS = 2; +static DnsPacket *received_packets[2] = {}; +static size_t n_received_packets = 0; + +static int on_stream_packet(DnsStream *stream, DnsPacket *p) { + assert_se(n_received_packets < MAX_RECEIVED_PACKETS); + assert_se(received_packets[n_received_packets++] = dns_packet_ref(p)); + return 0; +} + +static int on_stream_complete_do_nothing(DnsStream *s, int error) { + return 0; +} + +static void test_dns_stream(bool tls) { + Manager manager = {}; + _cleanup_(dns_stream_unrefp) DnsStream *stream = NULL; + _cleanup_(sd_event_unrefp) sd_event *event = NULL; + _cleanup_close_ int clientfd = -1; + int r; + + void *(*server_entrypoint)(void *); + pthread_t server_thread; + pthread_mutex_t server_lock; + + log_info("test-resolved-stream: Started %s test", tls ? "TLS" : "TCP"); + +#if ENABLE_DNS_OVER_TLS + if (tls) + /* For TLS mode, use DNS_OVER_TLS_OPPORTUNISTIC instead of DNS_OVER_TLS_YES, just to make + * certificate validation more lenient, allowing us to use self-signed certificates. We + * never downgrade, everything we test always goes over TLS */ + manager.dns_over_tls_mode = DNS_OVER_TLS_OPPORTUNISTIC; +#endif + + assert_se(sd_event_new(&event) >= 0); + manager.event = event; + + /* Set up a mock DNS (over TCP or TLS) server */ + server_entrypoint = tcp_dns_server; +#if ENABLE_DNS_OVER_TLS + if (tls) + server_entrypoint = tls_dns_server; +#endif + assert_se(pthread_mutex_init(&server_lock, NULL) == 0); + assert_se(pthread_mutex_lock(&server_lock) == 0); + assert_se(pthread_create(&server_thread, NULL, server_entrypoint, &server_lock) == 0); + + /* Create a socket client and connect to the TCP or TLS server + * The server may not be up immediately, so try to connect a few times before failing */ + assert_se((clientfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) >= 0); + + for (int i = 0; i < 100; i++) { + r = connect(clientfd, &SERVER_ADDRESS, sizeof(SERVER_ADDRESS)); + if (r >= 0) + break; + usleep(EVENT_TIMEOUT_USEC / 100); + } + assert_se(r >= 0); + + /* systemd-resolved uses (and requires) the socket to be in nonblocking mode */ + assert_se(fcntl(clientfd, F_SETFL, O_NONBLOCK) >= 0); + + /* Initialize DNS stream (disabling the default self-destruction + behaviour when no complete callback is set) */ + assert_se(dns_stream_new(&manager, &stream, DNS_STREAM_LOOKUP, DNS_PROTOCOL_DNS, + TAKE_FD(clientfd), NULL, on_stream_packet, on_stream_complete_do_nothing, + DNS_STREAM_DEFAULT_TIMEOUT_USEC) >= 0); +#if ENABLE_DNS_OVER_TLS + if (tls) { + DnsServer server = { + .manager = &manager, + .family = SERVER_ADDRESS.sin_family, + .address.in = SERVER_ADDRESS.sin_addr + }; + + assert_se(dnstls_manager_init(&manager) >= 0); + assert_se(dnstls_stream_connect_tls(stream, &server) >= 0); + } +#endif + + /* Test: Question of type A and associated answer */ + log_info("test-resolved-stream: A record"); + send_simple_question(stream, DNS_TYPE_A); + while (n_received_packets != 1) + assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1); + assert_se(DNS_PACKET_DATA(received_packets[0])); + assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]), + ANSWER_A + 2, sizeof(ANSWER_A) - 2) == 0); + dns_packet_unref(TAKE_PTR(received_packets[0])); + n_received_packets = 0; + + /* Test: Question of type AAAA and associated answer */ + log_info("test-resolved-stream: AAAA record"); + send_simple_question(stream, DNS_TYPE_AAAA); + while (n_received_packets != 1) + assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1); + assert_se(DNS_PACKET_DATA(received_packets[0])); + assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]), + ANSWER_AAAA + 2, sizeof(ANSWER_AAAA) - 2) == 0); + dns_packet_unref(TAKE_PTR(received_packets[0])); + n_received_packets = 0; + + /* Test: Question of type A and AAAA and associated answers + * Both answers are sent back in a single packet or TLS record + * (tests the fix of PR #22132: "Fix DoT timeout on multiple answer records") */ + log_info("test-resolved-stream: A + AAAA record"); + send_simple_question(stream, DNS_TYPE_A); + send_simple_question(stream, DNS_TYPE_AAAA); + + while (n_received_packets != 2) + assert_se(sd_event_run(event, EVENT_TIMEOUT_USEC) >= 1); + assert_se(DNS_PACKET_DATA(received_packets[0])); + assert_se(DNS_PACKET_DATA(received_packets[1])); + assert_se(memcmp(DNS_PACKET_DATA(received_packets[0]), + ANSWER_A + 2, sizeof(ANSWER_A) - 2) == 0); + assert_se(memcmp(DNS_PACKET_DATA(received_packets[1]), + ANSWER_AAAA + 2, sizeof(ANSWER_AAAA) - 2) == 0); + dns_packet_unref(TAKE_PTR(received_packets[0])); + dns_packet_unref(TAKE_PTR(received_packets[1])); + n_received_packets = 0; + +#if ENABLE_DNS_OVER_TLS + if (tls) + dnstls_manager_free(&manager); +#endif + + /* Stop the DNS server */ + assert_se(pthread_mutex_unlock(&server_lock) == 0); + assert_se(pthread_join(server_thread, NULL) == 0); + assert_se(pthread_mutex_destroy(&server_lock) == 0); + + log_info("test-resolved-stream: Finished %s test", tls ? "TLS" : "TCP"); +} + +static void try_isolate_network(void) { + _cleanup_close_ int socket_fd = -1; + + if (unshare(CLONE_NEWUSER | CLONE_NEWNET) < 0) { + log_warning("test-resolved-stream: Can't create user and network ns, running on host"); + return; + } + + /* Bring up the loopback interfaceon the newly created network namespace */ + struct ifreq req = { .ifr_ifindex = 1 }; + assert_se((socket_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0)) >= 0); + assert_se(ioctl(socket_fd,SIOCGIFNAME,&req) >= 0); + assert_se(ioctl(socket_fd, SIOCGIFFLAGS, &req) >= 0); + assert_se(FLAGS_SET(req.ifr_flags, IFF_LOOPBACK)); + req.ifr_flags |= IFF_UP; + assert_se(ioctl(socket_fd, SIOCSIFFLAGS, &req) >= 0); +} + +int main(int argc, char **argv) { + SERVER_ADDRESS = (struct sockaddr_in) { + .sin_family = AF_INET, + .sin_port = htobe16(12345), + .sin_addr.s_addr = htobe32(INADDR_LOOPBACK) + }; + + test_setup_logging(LOG_DEBUG); + + try_isolate_network(); + + test_dns_stream(false); +#if ENABLE_DNS_OVER_TLS + if (system("openssl version >/dev/null 2>&1") != 0) + return log_tests_skipped("Skipping TLS test since the 'openssl' command does not seem to be available"); + test_dns_stream(true); +#endif + + return 0; +} diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c index bca2f3b81..a833771d9 100644 --- a/src/rfkill/rfkill.c +++ b/src/rfkill/rfkill.c @@ -80,7 +80,7 @@ static int find_device( r = sd_device_new_from_subsystem_sysname(&device, "rfkill", sysname); if (r < 0) - return log_full_errno(IN_SET(r, -ENOENT, -ENXIO, -ENODEV) ? LOG_DEBUG : LOG_ERR, r, + return log_full_errno(ERRNO_IS_DEVICE_ABSENT(r) ? LOG_DEBUG : LOG_ERR, r, "Failed to open device '%s': %m", sysname); r = sd_device_get_sysattr_value(device, "name", &name); @@ -188,17 +188,14 @@ static int load_state(Context *c, const struct rfkill_event *event) { } static void save_state_queue_remove(Context *c, int idx, const char *state_file) { - struct write_queue_item *item, *tmp; - assert(c); - LIST_FOREACH_SAFE(queue, item, tmp, c->write_queue) { + LIST_FOREACH(queue, item, c->write_queue) if ((state_file && streq(item->file, state_file)) || idx == item->rfkill_idx) { log_debug("Canceled previous save state of '%s' to %s.", one_zero(item->state), item->file); LIST_REMOVE(queue, c->write_queue, item); write_queue_item_free(item); } - } } static int save_state_queue(Context *c, const struct rfkill_event *event) { diff --git a/src/run-generator/run-generator.c b/src/run-generator/run-generator.c index 1cf14e71f..fb6220970 100644 --- a/src/run-generator/run-generator.c +++ b/src/run-generator/run-generator.c @@ -55,7 +55,6 @@ static int parse(const char *key, const char *value, void *data) { static int generate(void) { _cleanup_fclose_ FILE *f = NULL; const char *p; - char **c; int r; if (strv_isempty(arg_commands) && !arg_success_action) diff --git a/src/run/run.c b/src/run/run.c index ff2437384..2ae629f59 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -794,9 +794,12 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p e = getenv("TERM"); if (e) { - char *n; + _cleanup_free_ char *n = NULL; + + n = strjoin("TERM=", e); + if (!n) + return log_oom(); - n = strjoina("TERM=", e); r = sd_bus_message_append(m, "(sv)", "Environment", "as", 1, n); @@ -1699,8 +1702,6 @@ static int start_transient_trigger( } static bool shall_make_executable_absolute(void) { - const char *f; - if (strv_isempty(arg_cmdline)) return false; if (arg_transport != BUS_TRANSPORT_LOCAL) diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c index 3f286a888..2bdc529b8 100644 --- a/src/shared/acl-util.c +++ b/src/shared/acl-util.c @@ -212,7 +212,6 @@ int acl_search_groups(const char *path, char ***ret_groups) { int parse_acl(const char *text, acl_t *acl_access, acl_t *acl_default, bool want_mask) { _cleanup_free_ char **a = NULL, **d = NULL; /* strings are not freed */ _cleanup_strv_free_ char **split = NULL; - char **entry; int r = -EINVAL; _cleanup_(acl_freep) acl_t a_acl = NULL, d_acl = NULL; diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c index 5f5328c8c..6dacc1d20 100644 --- a/src/shared/base-filesystem.c +++ b/src/shared/base-filesystem.c @@ -63,6 +63,20 @@ static const BaseFilesystem table[] = { "usr/lib64\0", "ld-linux-x86-64.so.2" }, # define KNOW_LIB64_DIRS 1 #elif defined(__ia64__) +#elif defined(__loongarch64) +# define KNOW_LIB64_DIRS 1 +# if defined(__loongarch_double_float) + { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" + "usr/lib64\0", "ld-linux-loongarch-lp64d.so.1" }, +# elif defined(__loongarch_single_float) + { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" + "usr/lib64\0", "ld-linux-loongarch-lp64f.so.1" }, +# elif defined(__loongarch_soft_float) + { "lib64", 0, "usr/lib/"LIB_ARCH_TUPLE"\0" + "usr/lib64\0", "ld-linux-loongarch-lp64s.so.1" }, +# else +# error "Unknown LoongArch ABI" +# endif #elif defined(__m68k__) /* No link needed. */ # define KNOW_LIB64_DIRS 1 @@ -176,7 +190,7 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { return -errno; } - if (uid != UID_INVALID || gid != UID_INVALID) + if (uid_is_valid(uid) || gid_is_valid(gid)) if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0) return log_error_errno(errno, "Failed to chown directory at %s/%s: %m", root, table[i].dir); } diff --git a/src/shared/boot-timestamps.c b/src/shared/boot-timestamps.c index e00b37aa3..8786e89c0 100644 --- a/src/shared/boot-timestamps.c +++ b/src/shared/boot-timestamps.c @@ -5,13 +5,11 @@ #include "efi-loader.h" #include "macro.h" #include "time-util.h" -#include "virt.h" int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_timestamp *loader) { usec_t x = 0, y = 0, a; int r; dual_timestamp _n; - bool use_firmware = true; assert(firmware); assert(loader); @@ -26,10 +24,6 @@ int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_time r = efi_loader_get_boot_usec(&x, &y); if (r < 0) return r; - - /* If we are running in a VM, the init timestamp would - * be equivalent to the host uptime. */ - use_firmware = detect_vm() <= 0; } /* Let's convert this to timestamps where the firmware @@ -39,14 +33,12 @@ int boot_timestamps(const dual_timestamp *n, dual_timestamp *firmware, dual_time * the monotonic timestamps here as negative of the actual * value. */ - if (use_firmware) { - firmware->monotonic = y; - a = n->monotonic + firmware->monotonic; - firmware->realtime = n->realtime > a ? n->realtime - a : 0; - } else - firmware->monotonic = firmware->realtime = 0; - + firmware->monotonic = y; loader->monotonic = y - x; + + a = n->monotonic + firmware->monotonic; + firmware->realtime = n->realtime > a ? n->realtime - a : 0; + a = n->monotonic + loader->monotonic; loader->realtime = n->realtime > a ? n->realtime - a : 0; diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 0076092c2..7016f3840 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -1,38 +1,23 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include -#include #include -#include "sd-device.h" -#include "sd-id128.h" - -#include "alloc-util.h" -#include "blkid-util.h" -#include "bootspec-fundamental.h" #include "bootspec.h" +#include "bootspec-fundamental.h" #include "conf-files.h" -#include "def.h" -#include "device-nodes.h" #include "dirent-util.h" -#include "efivars.h" #include "efi-loader.h" #include "env-file.h" -#include "env-util.h" -#include "errno-util.h" #include "fd-util.h" #include "fileio.h" -#include "parse-util.h" +#include "find-esp.h" #include "path-util.h" #include "pe-header.h" +#include "recurse-dir.h" #include "sort-util.h" #include "stat-util.h" -#include "string-table.h" -#include "string-util.h" #include "strv.h" #include "unaligned.h" -#include "util.h" -#include "virt.h" static void boot_entry_free(BootEntry *entry) { assert(entry); @@ -43,6 +28,7 @@ static void boot_entry_free(BootEntry *entry) { free(entry->root); free(entry->title); free(entry->show_title); + free(entry->sort_key); free(entry->version); free(entry->machine_id); free(entry->architecture); @@ -51,40 +37,48 @@ static void boot_entry_free(BootEntry *entry) { free(entry->efi); strv_free(entry->initrd); free(entry->device_tree); + strv_free(entry->device_tree_overlay); } -static int boot_entry_load( +static int boot_entry_load_type1( + FILE *f, const char *root, - const char *path, + const char *dir, + const char *id, BootEntry *entry) { _cleanup_(boot_entry_free) BootEntry tmp = { .type = BOOT_ENTRY_CONF, }; - _cleanup_fclose_ FILE *f = NULL; unsigned line = 1; - char *b, *c; + char *c; int r; + assert(f); assert(root); - assert(path); + assert(dir); + assert(id); assert(entry); - c = endswith_no_case(path, ".conf"); - if (!c) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", path); + /* Loads a Type #1 boot menu entry from the specified FILE* object */ - b = basename(path); - tmp.id = strdup(b); - tmp.id_old = strndup(b, c - b); - if (!tmp.id || !tmp.id_old) + if (!efi_loader_entry_name_valid(id)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry name: %s", id); + + c = endswith_no_case(id, ".conf"); + if (!c) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", id); + + tmp.id = strdup(id); + if (!tmp.id) return log_oom(); - if (!efi_loader_entry_name_valid(tmp.id)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry name: %s", tmp.id); + tmp.id_old = strndup(id, c - id); /* Without .conf suffix */ + if (!tmp.id_old) + return log_oom(); - tmp.path = strdup(path); + tmp.path = path_join(dir, id); if (!tmp.path) return log_oom(); @@ -92,10 +86,6 @@ static int boot_entry_load( if (!tmp.root) return log_oom(); - f = fopen(path, "re"); - if (!f) - return log_error_errno(errno, "Failed to open \"%s\": %m", path); - for (;;) { _cleanup_free_ char *buf = NULL, *field = NULL; const char *p; @@ -104,9 +94,9 @@ static int boot_entry_load( if (r == 0) break; if (r == -ENOBUFS) - return log_error_errno(r, "%s:%u: Line too long", path, line); + return log_error_errno(r, "%s:%u: Line too long", tmp.path, line); if (r < 0) - return log_error_errno(r, "%s:%u: Error while reading: %m", path, line); + return log_error_errno(r, "%s:%u: Error while reading: %m", tmp.path, line); line++; @@ -116,16 +106,18 @@ static int boot_entry_load( p = buf; r = extract_first_word(&p, &field, " \t", 0); if (r < 0) { - log_error_errno(r, "Failed to parse config file %s line %u: %m", path, line); + log_error_errno(r, "Failed to parse config file %s line %u: %m", tmp.path, line); continue; } if (r == 0) { - log_warning("%s:%u: Bad syntax", path, line); + log_warning("%s:%u: Bad syntax", tmp.path, line); continue; } if (streq(field, "title")) r = free_and_strdup(&tmp.title, p); + else if (streq(field, "sort-key")) + r = free_and_strdup(&tmp.sort_key, p); else if (streq(field, "version")) r = free_and_strdup(&tmp.version, p); else if (streq(field, "machine-id")) @@ -142,12 +134,20 @@ static int boot_entry_load( r = strv_extend(&tmp.initrd, p); else if (streq(field, "devicetree")) r = free_and_strdup(&tmp.device_tree, p); - else { - log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field); + else if (streq(field, "devicetree-overlay")) { + _cleanup_strv_free_ char **l = NULL; + + l = strv_split(p, NULL); + if (!l) + return log_oom(); + + r = strv_extend_strv(&tmp.device_tree_overlay, l, false); + } else { + log_notice("%s:%u: Unknown line \"%s\", ignoring.", tmp.path, line, field); continue; } if (r < 0) - return log_error_errno(r, "%s:%u: Error while reading: %m", path, line); + return log_error_errno(r, "%s:%u: Error while reading: %m", tmp.path, line); } *entry = tmp; @@ -156,8 +156,6 @@ static int boot_entry_load( } void boot_config_free(BootConfig *config) { - size_t i; - assert(config); free(config->default_pattern); @@ -167,13 +165,17 @@ void boot_config_free(BootConfig *config) { free(config->auto_firmware); free(config->console_mode); free(config->random_seed_mode); + free(config->beep); free(config->entry_oneshot); free(config->entry_default); + free(config->entry_selected); - for (i = 0; i < config->n_entries; i++) + for (size_t i = 0; i < config->n_entries; i++) boot_entry_free(config->entries + i); free(config->entries); + + set_free(config->inodes_seen); } static int boot_loader_read_conf(const char *path, BootConfig *config) { @@ -234,6 +236,8 @@ static int boot_loader_read_conf(const char *path, BootConfig *config) { r = free_and_strdup(&config->console_mode, p); else if (streq(field, "random-seed-mode")) r = free_and_strdup(&config->random_seed_mode, p); + else if (streq(field, "beep")) + r = free_and_strdup(&config->beep, p); else { log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field); continue; @@ -246,37 +250,139 @@ static int boot_loader_read_conf(const char *path, BootConfig *config) { } static int boot_entry_compare(const BootEntry *a, const BootEntry *b) { - return strverscmp_improved(a->id, b->id); -} - -static int boot_entries_find( - const char *root, - const char *dir, - BootEntry **entries, - size_t *n_entries) { - - _cleanup_strv_free_ char **files = NULL; - char **f; int r; + assert(a); + assert(b); + + r = CMP(!a->sort_key, !b->sort_key); + if (r != 0) + return r; + + if (a->sort_key && b->sort_key) { + r = strcmp(a->sort_key, b->sort_key); + if (r != 0) + return r; + + r = strcmp_ptr(a->machine_id, b->machine_id); + if (r != 0) + return r; + + r = -strverscmp_improved(a->version, b->version); + if (r != 0) + return r; + } + + return -strverscmp_improved(a->id, b->id); +} + +static void inode_hash_func(const struct stat *q, struct siphash *state) { + siphash24_compress(&q->st_dev, sizeof(q->st_dev), state); + siphash24_compress(&q->st_ino, sizeof(q->st_ino), state); +} + +static int inode_compare_func(const struct stat *a, const struct stat *b) { + int r; + + r = CMP(a->st_dev, b->st_dev); + if (r != 0) + return r; + + return CMP(a->st_ino, b->st_ino); +} + +DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(inode_hash_ops, struct stat, inode_hash_func, inode_compare_func, free); + +static int config_check_inode_relevant_and_unseen(BootConfig *config, int fd, const char *fname) { + _cleanup_free_ char *d = NULL; + struct stat st; + + assert(config); + assert(fd >= 0); + assert(fname); + + /* So, here's the thing: because of the mess around /efi/ vs. /boot/ vs. /boot/efi/ it might be that + * people have these dirs, or subdirs of them symlinked or bind mounted, and we might end up + * iterating though some dirs multiple times. Let's thus rather be safe than sorry, and track the + * inodes we already processed: let's ignore inodes we have seen already. This should be robust + * against any form of symlinking or bind mounting, and effectively suppress any such duplicates. */ + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to stat('%s'): %m", fname); + if (!S_ISREG(st.st_mode)) { + log_debug("File '%s' is not a reguar file, ignoring.", fname); + return false; + } + + if (set_contains(config->inodes_seen, &st)) { + log_debug("Inode '%s' already seen before, ignoring.", fname); + return false; + } + d = memdup(&st, sizeof(st)); + if (!d) + return log_oom(); + if (set_ensure_put(&config->inodes_seen, &inode_hash_ops, d) < 0) + return log_oom(); + + TAKE_PTR(d); + return true; +} + +static int boot_entries_find_type1( + BootConfig *config, + const char *root, + const char *dir) { + + _cleanup_free_ DirectoryEntries *dentries = NULL; + _cleanup_close_ int dir_fd = -1; + int r; + + assert(config); assert(root); assert(dir); - assert(entries); - assert(n_entries); - r = conf_files_list(&files, ".conf", NULL, 0, dir); + dir_fd = open(dir, O_DIRECTORY|O_CLOEXEC); + if (dir_fd < 0) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open '%s': %m", dir); + } + + r = readdir_all(dir_fd, RECURSE_DIR_IGNORE_DOT, &dentries); if (r < 0) - return log_error_errno(r, "Failed to list files in \"%s\": %m", dir); + return log_error_errno(r, "Failed to read directory '%s': %m", dir); - STRV_FOREACH(f, files) { - if (!GREEDY_REALLOC0(*entries, *n_entries + 1)) + for (size_t i = 0; i < dentries->n_entries; i++) { + const struct dirent *de = dentries->entries[i]; + _cleanup_fclose_ FILE *f = NULL; + + if (!dirent_is_file(de)) + continue; + + if (!endswith_no_case(de->d_name, ".conf")) + continue; + + r = xfopenat(dir_fd, de->d_name, "re", 0, &f); + if (r < 0) { + log_warning_errno(r, "Failed to open %s/%s, ignoring: %m", dir, de->d_name); + continue; + } + + r = config_check_inode_relevant_and_unseen(config, fileno(f), de->d_name); + if (r < 0) + return r; + if (r == 0) /* inode already seen or otherwise not relevant */ + continue; + + if (!GREEDY_REALLOC0(config->entries, config->n_entries + 1)) return log_oom(); - r = boot_entry_load(root, *f, *entries + *n_entries); + r = boot_entry_load_type1(f, root, dir, de->d_name, config->entries + config->n_entries); if (r < 0) continue; - (*n_entries) ++; + config->n_entries++; } return 0; @@ -294,7 +400,7 @@ static int boot_entry_load_unified( _cleanup_(boot_entry_free) BootEntry tmp = { .type = BOOT_ENTRY_UNIFIED, }; - const char *k, *good_name, *good_version; + const char *k, *good_name, *good_version, *good_sort_key; _cleanup_fclose_ FILE *f = NULL; int r; @@ -322,7 +428,7 @@ static int boot_entry_load_unified( if (r < 0) return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path); - if (!bootspec_pick_name_version( + if (!bootspec_pick_name_version_sort_key( os_pretty_name, os_image_id, os_name, @@ -332,7 +438,8 @@ static int boot_entry_load_unified( os_version_id, os_build_id, &good_name, - &good_version)) + &good_version, + &good_sort_key)) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path); r = path_extract_filename(path, &tmp.id); @@ -370,6 +477,10 @@ static int boot_entry_load_unified( if (!tmp.title) return log_oom(); + tmp.sort_key = strdup(good_sort_key); + if (!tmp.sort_key) + return log_oom(); + tmp.version = strdup(good_version); if (!tmp.version) return log_oom(); @@ -390,12 +501,9 @@ static int find_sections( _cleanup_free_ struct PeSectionHeader *sections = NULL; _cleanup_free_ char *osrelease = NULL, *cmdline = NULL; - size_t i, n_sections; - struct DosFileHeader dos; - struct PeHeader pe; - uint64_t start; ssize_t n; + struct DosFileHeader dos; n = pread(fd, &dos, sizeof(dos), 0); if (n < 0) return log_error_errno(errno, "Failed read DOS header: %m"); @@ -405,7 +513,9 @@ static int find_sections( if (dos.Magic[0] != 'M' || dos.Magic[1] != 'Z') return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DOS executable magic missing, refusing."); - start = unaligned_read_le32(&dos.ExeHeader); + uint64_t start = unaligned_read_le32(&dos.ExeHeader); + + struct PeHeader pe; n = pread(fd, &pe, sizeof(pe), start); if (n < 0) return log_error_errno(errno, "Failed to read PE header: %m"); @@ -415,7 +525,7 @@ static int find_sections( if (pe.Magic[0] != 'P' || pe.Magic[1] != 'E' || pe.Magic[2] != 0 || pe.Magic[3] != 0) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE executable magic missing, refusing."); - n_sections = unaligned_read_le16(&pe.FileHeader.NumberOfSections); + size_t n_sections = unaligned_read_le16(&pe.FileHeader.NumberOfSections); if (n_sections > 96) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE header has too many sections, refusing."); @@ -431,7 +541,7 @@ static int find_sections( if ((size_t) n != n_sections * sizeof(struct PeSectionHeader)) return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading sections, refusing."); - for (i = 0; i < n_sections; i++) { + for (size_t i = 0; i < n_sections; i++) { _cleanup_free_ char *k = NULL; uint32_t offset, size; char **b; @@ -482,18 +592,15 @@ static int find_sections( } static int boot_entries_find_unified( + BootConfig *config, const char *root, - const char *dir, - BootEntry **entries, - size_t *n_entries) { + const char *dir) { _cleanup_(closedirp) DIR *d = NULL; int r; - assert(root); + assert(config); assert(dir); - assert(entries); - assert(n_entries); d = opendir(dir); if (!d) { @@ -513,7 +620,7 @@ static int boot_entries_find_unified( if (!endswith_no_case(de->d_name, ".efi")) continue; - if (!GREEDY_REALLOC0(*entries, *n_entries + 1)) + if (!GREEDY_REALLOC0(config->entries, config->n_entries + 1)) return log_oom(); fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK); @@ -522,42 +629,40 @@ static int boot_entries_find_unified( continue; } - r = fd_verify_regular(fd); - if (r < 0) { - log_warning_errno(r, "File %s/%s is not regular, ignoring: %m", dir, de->d_name); - continue; - } - - r = find_sections(fd, &osrelease, &cmdline); + r = config_check_inode_relevant_and_unseen(config, fd, de->d_name); if (r < 0) + return r; + if (r == 0) /* inode already seen or otherwise not relevant */ + continue; + + if (find_sections(fd, &osrelease, &cmdline) < 0) continue; j = path_join(dir, de->d_name); if (!j) return log_oom(); - r = boot_entry_load_unified(root, j, osrelease, cmdline, *entries + *n_entries); + r = boot_entry_load_unified(root, j, osrelease, cmdline, config->entries + config->n_entries); if (r < 0) continue; - (*n_entries) ++; + config->n_entries++; } return 0; } -static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) { - size_t i, j; +static bool find_nonunique(const BootEntry *entries, size_t n_entries, bool arr[]) { bool non_unique = false; assert(entries || n_entries == 0); assert(arr || n_entries == 0); - for (i = 0; i < n_entries; i++) + for (size_t i = 0; i < n_entries; i++) arr[i] = false; - for (i = 0; i < n_entries; i++) - for (j = 0; j < n_entries; j++) + for (size_t i = 0; i < n_entries; i++) + for (size_t j = 0; j < n_entries; j++) if (i != j && streq(boot_entry_title(entries + i), boot_entry_title(entries + j))) non_unique = arr[i] = arr[j] = true; @@ -566,22 +671,26 @@ static bool find_nonunique(BootEntry *entries, size_t n_entries, bool *arr) { } static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) { + _cleanup_free_ bool *arr = NULL; char *s; - size_t i; - int r; - bool arr[n_entries]; assert(entries || n_entries == 0); + if (n_entries == 0) + return 0; + + arr = new(bool, n_entries); + if (!arr) + return -ENOMEM; + /* Find _all_ non-unique titles */ if (!find_nonunique(entries, n_entries, arr)) return 0; /* Add version to non-unique titles */ - for (i = 0; i < n_entries; i++) + for (size_t i = 0; i < n_entries; i++) if (arr[i] && entries[i].version) { - r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].version); - if (r < 0) + if (asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].version) < 0) return -ENOMEM; free_and_replace(entries[i].show_title, s); @@ -591,10 +700,9 @@ static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) { return 0; /* Add machine-id to non-unique titles */ - for (i = 0; i < n_entries; i++) + for (size_t i = 0; i < n_entries; i++) if (arr[i] && entries[i].machine_id) { - r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].machine_id); - if (r < 0) + if (asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].machine_id) < 0) return -ENOMEM; free_and_replace(entries[i].show_title, s); @@ -604,10 +712,9 @@ static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) { return 0; /* Add file name to non-unique titles */ - for (i = 0; i < n_entries; i++) + for (size_t i = 0; i < n_entries; i++) if (arr[i]) { - r = asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id); - if (r < 0) + if (asprintf(&s, "%s (%s)", boot_entry_title(entries + i), entries[i].id) < 0) return -ENOMEM; free_and_replace(entries[i].show_title, s); @@ -616,6 +723,19 @@ static int boot_entries_uniquify(BootEntry *entries, size_t n_entries) { return 0; } +static int boot_config_find(const BootConfig *config, const char *id) { + assert(config); + + if (!id) + return -1; + + for (size_t i = 0; i < config->n_entries; i++) + if (fnmatch(id, config->entries[i].id, FNM_CASEFOLD) == 0) + return i; + + return -1; +} + static int boot_entries_select_default(const BootConfig *config) { int i; @@ -627,38 +747,97 @@ static int boot_entries_select_default(const BootConfig *config) { return -1; /* -1 means "no default" */ } - if (config->entry_oneshot) - for (i = config->n_entries - 1; i >= 0; i--) - if (streq(config->entry_oneshot, config->entries[i].id)) { - log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot", - config->entries[i].id); - return i; - } + if (config->entry_oneshot) { + i = boot_config_find(config, config->entry_oneshot); + if (i >= 0) { + log_debug("Found default: id \"%s\" is matched by LoaderEntryOneShot", + config->entries[i].id); + return i; + } + } - if (config->entry_default) - for (i = config->n_entries - 1; i >= 0; i--) - if (streq(config->entry_default, config->entries[i].id)) { - log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault", - config->entries[i].id); - return i; - } + if (config->entry_default) { + i = boot_config_find(config, config->entry_default); + if (i >= 0) { + log_debug("Found default: id \"%s\" is matched by LoaderEntryDefault", + config->entries[i].id); + return i; + } + } - if (config->default_pattern) - for (i = config->n_entries - 1; i >= 0; i--) - if (fnmatch(config->default_pattern, config->entries[i].id, FNM_CASEFOLD) == 0) { - log_debug("Found default: id \"%s\" is matched by pattern \"%s\"", - config->entries[i].id, config->default_pattern); - return i; - } + if (config->default_pattern) { + i = boot_config_find(config, config->default_pattern); + if (i >= 0) { + log_debug("Found default: id \"%s\" is matched by pattern \"%s\"", + config->entries[i].id, config->default_pattern); + return i; + } + } - log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id); - return config->n_entries - 1; + log_debug("Found default: first entry \"%s\"", config->entries[0].id); + return 0; } -int boot_entries_load_config( +static int boot_entries_select_selected(const BootConfig *config) { + assert(config); + assert(config->entries || config->n_entries == 0); + + if (!config->entry_selected || config->n_entries == 0) + return -1; + + return boot_config_find(config, config->entry_selected); +} + +static int boot_load_efi_entry_pointers(BootConfig *config) { + int r; + + assert(config); + + if (!is_efi_boot()) + return 0; + + /* Loads the three "pointers" to boot loader entries from their EFI variables */ + + r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &config->entry_oneshot); + if (r == -ENOMEM) + return log_oom(); + if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) + log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryOneShot\", ignoring: %m"); + + r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryDefault), &config->entry_default); + if (r == -ENOMEM) + return log_oom(); + if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) + log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryDefault\", ignoring: %m"); + + r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntrySelected), &config->entry_selected); + if (r == -ENOMEM) + return log_oom(); + if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) + log_warning_errno(r, "Failed to read EFI variable \"LoaderEntrySelected\", ignoring: %m"); + + return 1; +} + +int boot_config_select_special_entries(BootConfig *config) { + int r; + + assert(config); + + r = boot_load_efi_entry_pointers(config); + if (r < 0) + return r; + + config->default_entry = boot_entries_select_default(config); + config->selected_entry = boot_entries_select_selected(config); + + return 0; +} + +int boot_config_load( + BootConfig *config, const char *esp_path, - const char *xbootldr_path, - BootConfig *config) { + const char *xbootldr_path) { const char *p; int r; @@ -672,24 +851,24 @@ int boot_entries_load_config( return r; p = strjoina(esp_path, "/loader/entries"); - r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries); + r = boot_entries_find_type1(config, esp_path, p); if (r < 0) return r; p = strjoina(esp_path, "/EFI/Linux/"); - r = boot_entries_find_unified(esp_path, p, &config->entries, &config->n_entries); + r = boot_entries_find_unified(config, esp_path, p); if (r < 0) return r; } if (xbootldr_path) { p = strjoina(xbootldr_path, "/loader/entries"); - r = boot_entries_find(xbootldr_path, p, &config->entries, &config->n_entries); + r = boot_entries_find_type1(config, xbootldr_path, p); if (r < 0) return r; p = strjoina(xbootldr_path, "/EFI/Linux/"); - r = boot_entries_find_unified(xbootldr_path, p, &config->entries, &config->n_entries); + r = boot_entries_find_unified(config, xbootldr_path, p); if (r < 0) return r; } @@ -700,32 +879,16 @@ int boot_entries_load_config( if (r < 0) return log_error_errno(r, "Failed to uniquify boot entries: %m"); - if (is_efi_boot()) { - r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryOneShot), &config->entry_oneshot); - if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) { - log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryOneShot\": %m"); - if (r == -ENOMEM) - return r; - } - - r = efi_get_variable_string(EFI_LOADER_VARIABLE(LoaderEntryDefault), &config->entry_default); - if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) { - log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryDefault\": %m"); - if (r == -ENOMEM) - return r; - } - } - - config->default_entry = boot_entries_select_default(config); return 0; } -int boot_entries_load_config_auto( +int boot_config_load_auto( + BootConfig *config, const char *override_esp_path, - const char *override_xbootldr_path, - BootConfig *config) { + const char *override_xbootldr_path) { _cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL; + dev_t esp_devid = 0, xbootldr_devid = 0; int r; assert(config); @@ -739,25 +902,29 @@ int boot_entries_load_config_auto( if (!override_esp_path && !override_xbootldr_path) { if (access("/run/boot-loader-entries/", F_OK) >= 0) - return boot_entries_load_config("/run/boot-loader-entries/", NULL, config); + return boot_config_load(config, "/run/boot-loader-entries/", NULL); if (errno != ENOENT) return log_error_errno(errno, "Failed to determine whether /run/boot-loader-entries/ exists: %m"); } - r = find_esp_and_warn(override_esp_path, false, &esp_where, NULL, NULL, NULL, NULL); + r = find_esp_and_warn(override_esp_path, /* unprivileged_mode= */ false, &esp_where, NULL, NULL, NULL, NULL, &esp_devid); if (r < 0) /* we don't log about ENOKEY here, but propagate it, leaving it to the caller to log */ return r; - r = find_xbootldr_and_warn(override_xbootldr_path, false, &xbootldr_where, NULL); + r = find_xbootldr_and_warn(override_xbootldr_path, /* unprivileged_mode= */ false, &xbootldr_where, NULL, &xbootldr_devid); if (r < 0 && r != -ENOKEY) return r; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */ - return boot_entries_load_config(esp_where, xbootldr_where, config); + /* If both paths actually refer to the same inode, suppress the xbootldr path */ + if (esp_where && xbootldr_where && devid_set_and_equal(esp_devid, xbootldr_devid)) + xbootldr_where = mfree(xbootldr_where); + + return boot_config_load(config, esp_where, xbootldr_where); } -int boot_entries_augment_from_loader( +int boot_config_augment_from_loader( BootConfig *config, char **found_by_loader, bool only_auto) { @@ -771,19 +938,20 @@ int boot_entries_augment_from_loader( "auto-reboot-to-firmware-setup", "Reboot Into Firmware Interface", }; - char **i; - assert(config); /* Let's add the entries discovered by the boot loader to the end of our list, unless they are * already included there. */ STRV_FOREACH(i, found_by_loader) { + BootEntry *existing; _cleanup_free_ char *c = NULL, *t = NULL, *p = NULL; - char **a, **b; - if (boot_config_has_entry(config, *i)) + existing = boot_config_find_entry(config, *i); + if (existing) { + existing->reported_by_loader = true; continue; + } if (only_auto && !startswith(*i, "auto-")) continue; @@ -808,651 +976,25 @@ int boot_entries_augment_from_loader( return log_oom(); config->entries[config->n_entries++] = (BootEntry) { - .type = BOOT_ENTRY_LOADER, + .type = startswith(*i, "auto-") ? BOOT_ENTRY_LOADER_AUTO : BOOT_ENTRY_LOADER, .id = TAKE_PTR(c), .title = TAKE_PTR(t), .path = TAKE_PTR(p), + .reported_by_loader = true, }; } return 0; } -/********************************************************************************/ +BootEntry* boot_config_find_entry(BootConfig *config, const char *id) { + assert(config); + assert(id); -static int verify_esp_blkid( - dev_t devid, - bool searching, - uint32_t *ret_part, - uint64_t *ret_pstart, - uint64_t *ret_psize, - sd_id128_t *ret_uuid) { + for (size_t j = 0; j < config->n_entries; j++) + if (streq_ptr(config->entries[j].id, id) || + streq_ptr(config->entries[j].id_old, id)) + return config->entries + j; - sd_id128_t uuid = SD_ID128_NULL; - uint64_t pstart = 0, psize = 0; - uint32_t part = 0; - -#if HAVE_BLKID - _cleanup_(blkid_free_probep) blkid_probe b = NULL; - _cleanup_free_ char *node = NULL; - const char *v; - int r; - - r = device_path_make_major_minor(S_IFBLK, devid, &node); - if (r < 0) - return log_error_errno(r, "Failed to format major/minor device path: %m"); - - errno = 0; - b = blkid_new_probe_from_filename(node); - if (!b) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node); - - blkid_probe_enable_superblocks(b, 1); - blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2) - return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node); - else if (r == 1) - return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node); - else if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node); - - r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); - if (r != 0) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "No filesystem found on \"%s\": %m", node); - if (!streq(v, "vfat")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" is not FAT.", node); - - r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); - if (r != 0) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" is not located on a partitioned block device.", node); - if (!streq(v, "gpt")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" is not on a GPT partition table.", node); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node); - if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node); - r = sd_id128_from_string(v, &uuid); - if (r < 0) - return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": %m", node); - r = safe_atou32(v, &part); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node); - r = safe_atou64(v, &pstart); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node); - r = safe_atou64(v, &psize); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); -#endif - - if (ret_part) - *ret_part = part; - if (ret_pstart) - *ret_pstart = pstart; - if (ret_psize) - *ret_psize = psize; - if (ret_uuid) - *ret_uuid = uuid; - - return 0; -} - -static int verify_esp_udev( - dev_t devid, - bool searching, - uint32_t *ret_part, - uint64_t *ret_pstart, - uint64_t *ret_psize, - sd_id128_t *ret_uuid) { - - _cleanup_(sd_device_unrefp) sd_device *d = NULL; - _cleanup_free_ char *node = NULL; - sd_id128_t uuid = SD_ID128_NULL; - uint64_t pstart = 0, psize = 0; - uint32_t part = 0; - const char *v; - int r; - - r = device_path_make_major_minor(S_IFBLK, devid, &node); - if (r < 0) - return log_error_errno(r, "Failed to format major/minor device path: %m"); - - r = sd_device_new_from_devnum(&d, 'b', devid); - if (r < 0) - return log_error_errno(r, "Failed to get device from device number: %m"); - - r = sd_device_get_property_value(d, "ID_FS_TYPE", &v); - if (r < 0) - return log_error_errno(r, "Failed to get device property: %m"); - if (!streq(v, "vfat")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" is not FAT.", node ); - - r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v); - if (r < 0) - return log_error_errno(r, "Failed to get device property: %m"); - if (!streq(v, "gpt")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" is not on a GPT partition table.", node); - - r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); - if (r < 0) - return log_error_errno(r, "Failed to get device property: %m"); - if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node); - - r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v); - if (r < 0) - return log_error_errno(r, "Failed to get device property: %m"); - r = sd_id128_from_string(v, &uuid); - if (r < 0) - return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); - - r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v); - if (r < 0) - return log_error_errno(r, "Failed to get device property: %m"); - r = safe_atou32(v, &part); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); - - r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v); - if (r < 0) - return log_error_errno(r, "Failed to get device property: %m"); - r = safe_atou64(v, &pstart); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); - - r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v); - if (r < 0) - return log_error_errno(r, "Failed to get device property: %m"); - r = safe_atou64(v, &psize); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); - - if (ret_part) - *ret_part = part; - if (ret_pstart) - *ret_pstart = pstart; - if (ret_psize) - *ret_psize = psize; - if (ret_uuid) - *ret_uuid = uuid; - - return 0; -} - -static int verify_fsroot_dir( - const char *path, - bool searching, - bool unprivileged_mode, - dev_t *ret_dev) { - - struct stat st, st2; - const char *t2, *trigger; - int r; - - assert(path); - assert(ret_dev); - - /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the - * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here, - * before stat()ing */ - trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */ - (void) access(trigger, F_OK); - - if (stat(path, &st) < 0) - return log_full_errno((searching && errno == ENOENT) || - (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, - "Failed to determine block device node of \"%s\": %m", path); - - if (major(st.st_dev) == 0) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "Block device node of \"%s\" is invalid.", path); - - if (path_equal(path, "/")) { - /* Let's assume that the root directory of the OS is always the root of its file system - * (which technically doesn't have to be the case, but it's close enough, and it's not easy - * to be fully correct for it, since we can't look further up than the root dir easily.) */ - if (ret_dev) - *ret_dev = st.st_dev; - - return 0; - } - - t2 = strjoina(path, "/.."); - if (stat(t2, &st2) < 0) { - if (errno != EACCES) - r = -errno; - else { - _cleanup_free_ char *parent = NULL; - - /* If going via ".." didn't work due to EACCESS, then let's determine the parent path - * directly instead. It's not as good, due to symlinks and such, but we can't do - * anything better here. */ - - parent = dirname_malloc(path); - if (!parent) - return log_oom(); - - r = RET_NERRNO(stat(parent, &st2)); - } - - if (r < 0) - return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r, - "Failed to determine block device node of parent of \"%s\": %m", path); - } - - if (st.st_dev == st2.st_dev) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "Directory \"%s\" is not the root of the file system.", path); - - if (ret_dev) - *ret_dev = st.st_dev; - - return 0; -} - -static int verify_esp( - const char *p, - bool searching, - bool unprivileged_mode, - uint32_t *ret_part, - uint64_t *ret_pstart, - uint64_t *ret_psize, - sd_id128_t *ret_uuid) { - - bool relax_checks; - dev_t devid; - int r; - - assert(p); - - /* This logs about all errors, except: - * - * -ENOENT → if 'searching' is set, and the dir doesn't exist - * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP - * -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing - */ - - relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0; - - /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any - * issues. Let's also, silence the error messages. */ - - if (!relax_checks) { - struct statfs sfs; - - if (statfs(p, &sfs) < 0) - /* If we are searching for the mount point, don't generate a log message if we can't find the path */ - return log_full_errno((searching && errno == ENOENT) || - (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, - "Failed to check file system type of \"%s\": %m", p); - - if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), - "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); - } - - r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); - if (r < 0) - return r; - - /* In a container we don't have access to block devices, skip this part of the verification, we trust - * the container manager set everything up correctly on its own. */ - if (detect_container() > 0 || relax_checks) - goto finish; - - /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we - * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an - * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell), - * however blkid can't work if we have no privileges to access block devices directly, which is why - * we use udev in that case. */ - if (unprivileged_mode) - return verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); - else - return verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); - -finish: - if (ret_part) - *ret_part = 0; - if (ret_pstart) - *ret_pstart = 0; - if (ret_psize) - *ret_psize = 0; - if (ret_uuid) - *ret_uuid = SD_ID128_NULL; - - return 0; -} - -int find_esp_and_warn( - const char *path, - bool unprivileged_mode, - char **ret_path, - uint32_t *ret_part, - uint64_t *ret_pstart, - uint64_t *ret_psize, - sd_id128_t *ret_uuid) { - - int r; - - /* This logs about all errors except: - * - * -ENOKEY → when we can't find the partition - * -EACCESS → when unprivileged_mode is true, and we can't access something - */ - - if (path) { - r = verify_esp(path, false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid); - if (r < 0) - return r; - - goto found; - } - - path = getenv("SYSTEMD_ESP_PATH"); - if (path) { - if (!path_is_valid(path) || !path_is_absolute(path)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s", - path); - - /* Note: when the user explicitly configured things with an env var we won't validate the mount - * point. After all we want this to be useful for testing. */ - goto found; - } - - FOREACH_STRING(path, "/efi", "/boot", "/boot/efi") { - - r = verify_esp(path, true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid); - if (r >= 0) - goto found; - if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */ - return r; - } - - /* No logging here */ - return -ENOKEY; - -found: - if (ret_path) { - char *c; - - c = strdup(path); - if (!c) - return log_oom(); - - *ret_path = c; - } - - return 0; -} - -static int verify_xbootldr_blkid( - dev_t devid, - bool searching, - sd_id128_t *ret_uuid) { - - sd_id128_t uuid = SD_ID128_NULL; - -#if HAVE_BLKID - _cleanup_(blkid_free_probep) blkid_probe b = NULL; - _cleanup_free_ char *node = NULL; - const char *v; - int r; - - r = device_path_make_major_minor(S_IFBLK, devid, &node); - if (r < 0) - return log_error_errno(r, "Failed to format major/minor device path: %m"); - errno = 0; - b = blkid_new_probe_from_filename(node); - if (!b) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node); - - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2) - return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node); - else if (r == 1) - return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node); - else if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node); - if (streq(v, "gpt")) { - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node); - if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), - "File system \"%s\" has wrong type for extended boot loader partition.", node); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node); - r = sd_id128_from_string(v, &uuid); - if (r < 0) - return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); - - } else if (streq(v, "dos")) { - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node); - if (!streq(v, "0xea")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), - "File system \"%s\" has wrong type for extended boot loader partition.", node); - - } else - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), - "File system \"%s\" is not on a GPT or DOS partition table.", node); -#endif - - if (ret_uuid) - *ret_uuid = uuid; - - return 0; -} - -static int verify_xbootldr_udev( - dev_t devid, - bool searching, - sd_id128_t *ret_uuid) { - - _cleanup_(sd_device_unrefp) sd_device *d = NULL; - _cleanup_free_ char *node = NULL; - sd_id128_t uuid = SD_ID128_NULL; - const char *v; - int r; - - r = device_path_make_major_minor(S_IFBLK, devid, &node); - if (r < 0) - return log_error_errno(r, "Failed to format major/minor device path: %m"); - - r = sd_device_new_from_devnum(&d, 'b', devid); - if (r < 0) - return log_error_errno(r, "Failed to get device from device number: %m"); - - r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v); - if (r < 0) - return log_error_errno(r, "Failed to get device property: %m"); - - if (streq(v, "gpt")) { - - r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); - if (r < 0) - return log_error_errno(r, "Failed to get device property: %m"); - if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), - "File system \"%s\" has wrong type for extended boot loader partition.", node); - - r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v); - if (r < 0) - return log_error_errno(r, "Failed to get device property: %m"); - r = sd_id128_from_string(v, &uuid); - if (r < 0) - return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); - - } else if (streq(v, "dos")) { - - r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); - if (r < 0) - return log_error_errno(r, "Failed to get device property: %m"); - if (!streq(v, "0xea")) - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), - "File system \"%s\" has wrong type for extended boot loader partition.", node); - } else - return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, - searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), - "File system \"%s\" is not on a GPT or DOS partition table.", node); - - if (ret_uuid) - *ret_uuid = uuid; - - return 0; -} - -static int verify_xbootldr( - const char *p, - bool searching, - bool unprivileged_mode, - sd_id128_t *ret_uuid) { - - bool relax_checks; - dev_t devid; - int r; - - assert(p); - - relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0; - - r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); - if (r < 0) - return r; - - if (detect_container() > 0 || relax_checks) - goto finish; - - if (unprivileged_mode) - return verify_xbootldr_udev(devid, searching, ret_uuid); - else - return verify_xbootldr_blkid(devid, searching, ret_uuid); - -finish: - if (ret_uuid) - *ret_uuid = SD_ID128_NULL; - - return 0; -} - -int find_xbootldr_and_warn( - const char *path, - bool unprivileged_mode, - char **ret_path, - sd_id128_t *ret_uuid) { - - int r; - - /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */ - - if (path) { - r = verify_xbootldr(path, false, unprivileged_mode, ret_uuid); - if (r < 0) - return r; - - goto found; - } - - path = getenv("SYSTEMD_XBOOTLDR_PATH"); - if (path) { - if (!path_is_valid(path) || !path_is_absolute(path)) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s", - path); - - goto found; - } - - r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid); - if (r >= 0) { - path = "/boot"; - goto found; - } - if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */ - return r; - - return -ENOKEY; - -found: - if (ret_path) { - char *c; - - c = strdup(path); - if (!c) - return log_oom(); - - *ret_path = c; - } - - return 0; + return NULL; } diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index 81845f47e..99bc5f69a 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -2,30 +2,33 @@ #pragma once +#include #include #include #include -#include "sd-id128.h" - +#include "set.h" #include "string-util.h" typedef enum BootEntryType { - BOOT_ENTRY_CONF, /* Type #1 entries: *.conf files */ - BOOT_ENTRY_UNIFIED, /* Type #2 entries: *.efi files */ - BOOT_ENTRY_LOADER, /* Additional entries augmented from LoaderEntries EFI var */ - _BOOT_ENTRY_MAX, - _BOOT_ENTRY_INVALID = -EINVAL, + BOOT_ENTRY_CONF, /* Boot Loader Specification Type #1 entries: *.conf files */ + BOOT_ENTRY_UNIFIED, /* Boot Loader Specification Type #2 entries: *.efi files */ + BOOT_ENTRY_LOADER, /* Additional entries augmented from LoaderEntries EFI variable (regular entries) */ + BOOT_ENTRY_LOADER_AUTO, /* Additional entries augmented from LoaderEntries EFI variable (special "automatic" entries) */ + _BOOT_ENTRY_TYPE_MAX, + _BOOT_ENTRY_TYPE_INVALID = -EINVAL, } BootEntryType; typedef struct BootEntry { BootEntryType type; + bool reported_by_loader; char *id; /* This is the file basename (including extension!) */ char *id_old; /* Old-style ID, for deduplication purposes. */ char *path; /* This is the full path to the drop-in file */ char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */ char *title; char *show_title; + char *sort_key; char *version; char *machine_id; char *architecture; @@ -34,6 +37,7 @@ typedef struct BootEntry { char *efi; char **initrd; char *device_tree; + char **device_tree_overlay; } BootEntry; typedef struct BootConfig { @@ -44,43 +48,49 @@ typedef struct BootConfig { char *auto_firmware; char *console_mode; char *random_seed_mode; + char *beep; char *entry_oneshot; char *entry_default; + char *entry_selected; BootEntry *entries; size_t n_entries; + ssize_t default_entry; + ssize_t selected_entry; + + Set *inodes_seen; } BootConfig; -static inline bool boot_config_has_entry(BootConfig *config, const char *id) { - size_t j; - - for (j = 0; j < config->n_entries; j++) { - const char* entry_id_old = config->entries[j].id_old; - if (streq(config->entries[j].id, id) || - (entry_id_old && streq(entry_id_old, id))) - return true; +#define BOOT_CONFIG_NULL \ + { \ + .default_entry = -1, \ + .selected_entry = -1, \ } - return false; -} +BootEntry* boot_config_find_entry(BootConfig *config, const char *id); static inline BootEntry* boot_config_default_entry(BootConfig *config) { + assert(config); + if (config->default_entry < 0) return NULL; + assert((size_t) config->default_entry < config->n_entries); return config->entries + config->default_entry; } void boot_config_free(BootConfig *config); -int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config); -int boot_entries_load_config_auto(const char *override_esp_path, const char *override_xbootldr_path, BootConfig *config); -int boot_entries_augment_from_loader(BootConfig *config, char **list, bool only_auto); + +int boot_config_load(BootConfig *config, const char *esp_path, const char *xbootldr_path); +int boot_config_load_auto(BootConfig *config, const char *override_esp_path, const char *override_xbootldr_path); +int boot_config_augment_from_loader(BootConfig *config, char **list, bool only_auto); + +int boot_config_select_special_entries(BootConfig *config); static inline const char* boot_entry_title(const BootEntry *entry) { + assert(entry); + return entry->show_title ?: entry->title ?: entry->id; } - -int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid); -int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid); diff --git a/src/shared/bpf-link.c b/src/shared/bpf-link.c index 85eab0903..fea49b2ec 100644 --- a/src/shared/bpf-link.c +++ b/src/shared/bpf-link.c @@ -32,12 +32,12 @@ int bpf_serialize_link(FILE *f, FDSet *fds, const char *key, struct bpf_link *li } struct bpf_link *bpf_link_free(struct bpf_link *link) { - - /* Avoid a useless dlopen() if link == NULL */ - if (!link) - return NULL; - - (void) sym_bpf_link__destroy(link); + /* If libbpf wasn't dlopen()ed, sym_bpf_link__destroy might be unresolved (NULL), so let's not try to + * call it if link is NULL. link might also be a non-null "error pointer", but such a value can only + * originate from a call to libbpf, but that means that libbpf is available, and we can let + * bpf_link__destroy() handle it. */ + if (link) + (void) sym_bpf_link__destroy(link); return NULL; } diff --git a/src/shared/bpf-program.c b/src/shared/bpf-program.c index b8ca32a1f..31fa4448b 100644 --- a/src/shared/bpf-program.c +++ b/src/shared/bpf-program.c @@ -55,6 +55,7 @@ BPFProgram *bpf_program_free(BPFProgram *p) { (void) bpf_program_cgroup_detach(p); safe_close(p->kernel_fd); + free(p->prog_name); free(p->instructions); free(p->attached_path); @@ -78,8 +79,18 @@ static int bpf_program_get_info_by_fd(int prog_fd, struct bpf_prog_info *info, u return RET_NERRNO(bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr))); } -int bpf_program_new(uint32_t prog_type, BPFProgram **ret) { +int bpf_program_new(uint32_t prog_type, const char *prog_name, BPFProgram **ret) { _cleanup_(bpf_program_freep) BPFProgram *p = NULL; + _cleanup_free_ char *name = NULL; + + if (prog_name) { + if (strlen(prog_name) >= BPF_OBJ_NAME_LEN) + return -ENAMETOOLONG; + + name = strdup(prog_name); + if (!name) + return -ENOMEM; + } p = new(BPFProgram, 1); if (!p) @@ -88,6 +99,7 @@ int bpf_program_new(uint32_t prog_type, BPFProgram **ret) { *p = (BPFProgram) { .prog_type = prog_type, .kernel_fd = -1, + .prog_name = TAKE_PTR(name), }; *ret = TAKE_PTR(p); @@ -165,6 +177,8 @@ int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size) { attr.log_buf = PTR_TO_UINT64(log_buf); attr.log_level = !!log_buf; attr.log_size = log_size; + if (p->prog_name) + strncpy(attr.prog_name, p->prog_name, BPF_OBJ_NAME_LEN - 1); p->kernel_fd = bpf(BPF_PROG_LOAD, &attr, sizeof(attr)); if (p->kernel_fd < 0) diff --git a/src/shared/bpf-program.h b/src/shared/bpf-program.h index e54900fa2..b640fb9d9 100644 --- a/src/shared/bpf-program.h +++ b/src/shared/bpf-program.h @@ -20,6 +20,7 @@ struct BPFProgram { /* The loaded BPF program, if loaded */ int kernel_fd; uint32_t prog_type; + char *prog_name; /* The code of it BPF program, if known */ size_t n_instructions; @@ -32,7 +33,7 @@ struct BPFProgram { uint32_t attached_flags; }; -int bpf_program_new(uint32_t prog_type, BPFProgram **ret); +int bpf_program_new(uint32_t prog_type, const char *prog_name, BPFProgram **ret); int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret); BPFProgram *bpf_program_free(BPFProgram *p); diff --git a/src/shared/bus-object.c b/src/shared/bus-object.c index f2e53913f..4ed5215e3 100644 --- a/src/shared/bus-object.c +++ b/src/shared/bus-object.c @@ -156,10 +156,10 @@ int bus_introspect_implementations( if (impl != main_impl) bus_introspect_implementation(&intro, impl); - _cleanup_set_free_ Set *nodes = NULL; + _cleanup_ordered_set_free_ OrderedSet *nodes = NULL; for (size_t i = 0; impl->children && impl->children[i]; i++) { - r = set_put_strdup(&nodes, impl->children[i]->path); + r = ordered_set_put_strdup(&nodes, impl->children[i]->path); if (r < 0) return log_oom(); } diff --git a/src/shared/bus-polkit.c b/src/shared/bus-polkit.c index bbe04bea3..4d733a817 100644 --- a/src/shared/bus-polkit.c +++ b/src/shared/bus-polkit.c @@ -36,7 +36,6 @@ static int bus_message_append_strv_key_value( sd_bus_message *m, const char **l) { - const char **k, **v; int r; assert(m); diff --git a/src/shared/bus-print-properties.c b/src/shared/bus-print-properties.c index 4e401180e..737bbd3d3 100644 --- a/src/shared/bus-print-properties.c +++ b/src/shared/bus-print-properties.c @@ -355,7 +355,7 @@ int bus_message_print_all_properties( if (!name_with_equal) return log_oom(); - if (!filter || strv_find(filter, name) || + if (!filter || strv_contains(filter, name) || (expected_value = strv_find_startswith(filter, name_with_equal))) { r = sd_bus_message_peek_type(m, NULL, &contents); if (r < 0) diff --git a/src/shared/bus-unit-procs.c b/src/shared/bus-unit-procs.c index 87c0334fe..3693fd61d 100644 --- a/src/shared/bus-unit-procs.c +++ b/src/shared/bus-unit-procs.c @@ -199,7 +199,7 @@ static int dump_processes( } if (cg->children) { - struct CGroupInfo **children, *child; + struct CGroupInfo **children; size_t n = 0, i; /* Order subcgroups by their name */ @@ -217,9 +217,7 @@ static int dump_processes( const char *name, *special; bool more; - child = children[i]; - - name = strrchr(child->cgroup_path, '/'); + name = strrchr(children[i]->cgroup_path, '/'); if (!name) return -EINVAL; name++; @@ -238,7 +236,7 @@ static int dump_processes( if (!pp) return -ENOMEM; - r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags); + r = dump_processes(cgroups, children[i]->cgroup_path, pp, n_columns, flags); if (r < 0) return r; } diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index dcce530c9..95ef3230b 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -973,6 +973,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con "ExecPaths", "NoExecPaths", "ExecSearchPath", + "ExtensionDirectories", "ConfigurationDirectory", "SupplementaryGroups", "SystemCallArchitectures")) @@ -1674,7 +1675,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con if (streq(field, "RootImageOptions")) { _cleanup_strv_free_ char **l = NULL; - char **first = NULL, **second = NULL; const char *p = eq; r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv"); @@ -2023,7 +2023,6 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con if (r < 0) return bus_log_create_error(r); - char **source, **destination; STRV_FOREACH_PAIR(source, destination, symlinks) { r = sd_bus_message_append(m, "(sst)", *source, *destination, 0); if (r < 0) @@ -2647,7 +2646,6 @@ int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const cha } int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l) { - char **i; int r; assert(m); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 4a2b7684b..a907b67a7 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -490,7 +490,6 @@ int bus_path_decode_unique(const char *path, const char *prefix, char **ret_send int bus_track_add_name_many(sd_bus_track *t, char **l) { int r = 0; - char **i; assert(t); @@ -562,7 +561,6 @@ int bus_open_system_watch_bind_with_description(sd_bus **ret, const char *descri int bus_reply_pair_array(sd_bus_message *m, char **l) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - char **k, **v; int r; assert(m); diff --git a/src/shared/cgroup-setup.c b/src/shared/cgroup-setup.c index 8bda66ca3..a1fabc73c 100644 --- a/src/shared/cgroup-setup.c +++ b/src/shared/cgroup-setup.c @@ -22,7 +22,6 @@ static int cg_any_controller_used_for_v1(void) { _cleanup_free_ char *buf = NULL; _cleanup_strv_free_ char **lines = NULL; - char **line; int r; r = read_full_virtual_file("/proc/cgroups", &buf, NULL); @@ -346,6 +345,9 @@ int cg_attach(const char *controller, const char *path, pid_t pid) { xsprintf(c, PID_FMT "\n", pid); r = write_string_file(fs, c, WRITE_STRING_FILE_DISABLE_BUFFER); + if (r == -EOPNOTSUPP && cg_is_threaded(controller, path) > 0) + /* When the threaded mode is used, we cannot read/write the file. Let's return recognizable error. */ + return -EUCLEAN; if (r < 0) return r; diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 48dd4d800..fc1e63146 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -128,6 +128,31 @@ static int show_cgroup_one_by_path( return 0; } +static int is_delegated(int cgfd, const char *path) { + _cleanup_free_ char *b = NULL; + int r; + + assert(cgfd >= 0 || path); + + r = getxattr_malloc(cgfd < 0 ? path : FORMAT_PROC_FD_PATH(cgfd), "trusted.delegate", &b); + if (r == -ENODATA) { + /* If the trusted xattr isn't set (preferred), then check the untrusted one. Under the + * assumption that whoever is trusted enough to own the cgroup, is also trusted enough to + * decide if it is delegated or not this should be safe. */ + r = getxattr_malloc(cgfd < 0 ? path : FORMAT_PROC_FD_PATH(cgfd), "user.delegate", &b); + if (r == -ENODATA) + return false; + } + if (r < 0) + return log_debug_errno(r, "Failed to read delegate xattr, ignoring: %m"); + + r = parse_boolean(b); + if (r < 0) + return log_debug_errno(r, "Failed to parse delegate xattr boolean value, ignoring: %m"); + + return r; +} + static int show_cgroup_name( const char *path, const char *prefix, @@ -137,7 +162,7 @@ static int show_cgroup_name( uint64_t cgroupid = UINT64_MAX; _cleanup_free_ char *b = NULL; _cleanup_close_ int fd = -1; - bool delegate = false; + bool delegate; int r; if (FLAGS_SET(flags, OUTPUT_CGROUP_XATTRS) || FLAGS_SET(flags, OUTPUT_CGROUP_ID)) { @@ -146,19 +171,7 @@ static int show_cgroup_name( log_debug_errno(errno, "Failed to open cgroup '%s', ignoring: %m", path); } - r = getxattr_malloc(fd < 0 ? path : FORMAT_PROC_FD_PATH(fd), "trusted.delegate", &b); - if (r < 0) { - if (r != -ENODATA) - log_debug_errno(r, "Failed to read trusted.delegate extended attribute, ignoring: %m"); - } else { - r = parse_boolean(b); - if (r < 0) - log_debug_errno(r, "Failed to parse trusted.delegate extended attribute boolean value, ignoring: %m"); - else - delegate = r > 0; - - b = mfree(b); - } + delegate = is_delegated(fd, path) > 0; if (FLAGS_SET(flags, OUTPUT_CGROUP_ID)) { cg_file_handle fh = CG_FILE_HANDLE_INIT; @@ -228,7 +241,7 @@ static int show_cgroup_name( printf("%s%s%s %s%s%s: %s\n", prefix, glyph == SPECIAL_GLYPH_TREE_BRANCH ? special_glyph(SPECIAL_GLYPH_TREE_VERTICAL) : " ", - special_glyph(SPECIAL_GLYPH_ARROW), + special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), ansi_blue(), x, ansi_normal(), y); } diff --git a/src/shared/chown-recursive.c b/src/shared/chown-recursive.c index 7c9a3050b..05a7a10ce 100644 --- a/src/shared/chown-recursive.c +++ b/src/shared/chown-recursive.c @@ -21,7 +21,6 @@ static int chown_one( gid_t gid, mode_t mask) { - const char *n; int r; assert(fd >= 0); diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c index febea8ea8..eb6f12a25 100644 --- a/src/shared/clock-util.c +++ b/src/shared/clock-util.c @@ -134,9 +134,8 @@ int clock_reset_timewarp(void) { #define EPOCH_FILE "/usr/lib/clock-epoch" int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) { - struct stat st; - struct timespec ts; usec_t epoch_usec, now_usec; + struct stat st; /* NB: we update *ret_attempted_change in *all* cases, both * on success and failure, to indicate what we intended to do! */ @@ -161,7 +160,7 @@ int clock_apply_epoch(ClockChangeDirection *ret_attempted_change) { return 0; } - if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, epoch_usec)) < 0) + if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(epoch_usec)) < 0) return -errno; return 1; diff --git a/src/shared/condition.c b/src/shared/condition.c index 68fbbf643..8165513cc 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -22,7 +22,7 @@ #include "cgroup-util.h" #include "condition.h" #include "cpu-set-util.h" -#include "efi-loader.h" +#include "efi-api.h" #include "env-file.h" #include "env-util.h" #include "extract-word.h" @@ -89,9 +89,7 @@ Condition* condition_free(Condition *c) { } Condition* condition_free_list_type(Condition *head, ConditionType type) { - Condition *c, *n; - - LIST_FOREACH_SAFE(conditions, c, n, head) + LIST_FOREACH(conditions, c, head) if (type < 0 || c->type == type) { LIST_REMOVE(conditions, head, c); condition_free(c); @@ -787,7 +785,8 @@ static int condition_test_needs_update(Condition *c, char **env) { if (r < 0) { log_debug_errno(r, "Failed to parse timestamp file '%s', using mtime: %m", p); return true; - } else if (r == 0) { + } + if (isempty(timestamp_str)) { log_debug("No data in timestamp file '%s', using mtime.", p); return true; } @@ -829,7 +828,6 @@ static int condition_test_first_boot(Condition *c, char **env) { static int condition_test_environment(Condition *c, char **env) { bool equal; - char **i; assert(c); assert(c->parameter); @@ -1171,7 +1169,6 @@ bool condition_test_list( condition_test_logger_t logger, void *userdata) { - Condition *c; int triggered = -1; assert(!!logger == !!to_string); @@ -1234,8 +1231,6 @@ void condition_dump(Condition *c, FILE *f, const char *prefix, condition_to_stri } void condition_dump_list(Condition *first, FILE *f, const char *prefix, condition_to_string_t to_string) { - Condition *c; - LIST_FOREACH(conditions, c, first) condition_dump(c, f, prefix, to_string); } diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index 1e1967d7e..23aed96b4 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -11,11 +11,14 @@ #include "conf-files.h" #include "conf-parser.h" #include "def.h" +#include "dns-domain.h" +#include "escape.h" #include "ether-addr-util.h" #include "extract-word.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "hostname-util.h" #include "in-addr-util.h" #include "log.h" #include "macro.h" @@ -40,18 +43,18 @@ int config_item_table_lookup( const void *table, const char *section, const char *lvalue, - ConfigParserCallback *func, - int *ltype, - void **data, + ConfigParserCallback *ret_func, + int *ret_ltype, + void **ret_data, void *userdata) { const ConfigTableItem *t; assert(table); assert(lvalue); - assert(func); - assert(ltype); - assert(data); + assert(ret_func); + assert(ret_ltype); + assert(ret_data); for (t = table; t->lvalue; t++) { @@ -61,12 +64,15 @@ int config_item_table_lookup( if (!streq_ptr(section, t->section)) continue; - *func = t->parse; - *ltype = t->ltype; - *data = t->data; + *ret_func = t->parse; + *ret_ltype = t->ltype; + *ret_data = t->data; return 1; } + *ret_func = NULL; + *ret_ltype = 0; + *ret_data = NULL; return 0; } @@ -74,9 +80,9 @@ int config_item_perf_lookup( const void *table, const char *section, const char *lvalue, - ConfigParserCallback *func, - int *ltype, - void **data, + ConfigParserCallback *ret_func, + int *ret_ltype, + void **ret_data, void *userdata) { ConfigPerfItemLookup lookup = (ConfigPerfItemLookup) table; @@ -84,9 +90,9 @@ int config_item_perf_lookup( assert(table); assert(lvalue); - assert(func); - assert(ltype); - assert(data); + assert(ret_func); + assert(ret_ltype); + assert(ret_data); if (section) { const char *key; @@ -95,12 +101,16 @@ int config_item_perf_lookup( p = lookup(key, strlen(key)); } else p = lookup(lvalue, strlen(lvalue)); - if (!p) + if (!p) { + *ret_func = NULL; + *ret_ltype = 0; + *ret_data = NULL; return 0; + } - *func = p->parse; - *ltype = p->ltype; - *data = (uint8_t*) userdata + p->offset; + *ret_func = p->parse; + *ret_ltype = p->ltype; + *ret_data = (uint8_t*) userdata + p->offset; return 1; } @@ -133,11 +143,11 @@ static int next_assignment( if (r < 0) return r; if (r > 0) { - if (func) - return func(unit, filename, line, section, section_line, - lvalue, ltype, rvalue, data, userdata); + if (!func) + return 0; - return 0; + return func(unit, filename, line, section, section_line, + lvalue, ltype, rvalue, data, userdata); } /* Warn about unknown non-extension fields. */ @@ -160,7 +170,7 @@ static int parse_line( char **section, unsigned *section_line, bool *section_ignored, - char *l, + char *l, /* is modified */ void *userdata) { char *e; @@ -171,18 +181,18 @@ static int parse_line( assert(l); l = strstrip(l); - if (!*l) + if (isempty(l)) return 0; - if (*l == '\n') + if (l[0] == '\n') return 0; if (!utf8_is_valid(l)) return log_syntax_invalid_utf8(unit, LOG_WARNING, filename, line, l); - if (*l == '[') { + if (l[0] == '[') { + _cleanup_free_ char *n = NULL; size_t k; - char *n; k = strlen(l); assert(k > 0); @@ -194,15 +204,18 @@ static int parse_line( if (!n) return log_oom(); + if (!string_is_safe(n)) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EBADMSG), "Bad characters in section header '%s'", l); + if (sections && !nulstr_contains(sections, n)) { - bool ignore = flags & CONFIG_PARSE_RELAXED; + bool ignore; const char *t; - ignore = ignore || startswith(n, "X-"); + ignore = (flags & CONFIG_PARSE_RELAXED) || startswith(n, "X-"); if (!ignore) NULSTR_FOREACH(t, sections) - if (streq_ptr(n, startswith(t, "-"))) { + if (streq_ptr(n, startswith(t, "-"))) { /* Ignore sections prefixed with "-" in valid section list */ ignore = true; break; } @@ -210,7 +223,6 @@ static int parse_line( if (!ignore) log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown section '%s'. Ignoring.", n); - free(n); *section = mfree(*section); *section_line = 0; *section_ignored = true; @@ -474,7 +486,6 @@ static int config_parse_many_files( _cleanup_hashmap_free_ Hashmap *stats_by_path = NULL; struct stat st; - char **fn; int r; if (ret_stats_by_path) { @@ -573,6 +584,50 @@ int config_parse_many( return config_parse_many_files(conf_files, files, sections, lookup, table, flags, userdata, ret_stats_by_path); } +static void config_section_hash_func(const ConfigSection *c, struct siphash *state) { + siphash24_compress_string(c->filename, state); + siphash24_compress(&c->line, sizeof(c->line), state); +} + +static int config_section_compare_func(const ConfigSection *x, const ConfigSection *y) { + int r; + + r = strcmp(x->filename, y->filename); + if (r != 0) + return r; + + return CMP(x->line, y->line); +} + +DEFINE_HASH_OPS(config_section_hash_ops, ConfigSection, config_section_hash_func, config_section_compare_func); + +int config_section_new(const char *filename, unsigned line, ConfigSection **s) { + ConfigSection *cs; + + cs = malloc0(offsetof(ConfigSection, filename) + strlen(filename) + 1); + if (!cs) + return -ENOMEM; + + strcpy(cs->filename, filename); + cs->line = line; + + *s = TAKE_PTR(cs); + + return 0; +} + +unsigned hashmap_find_free_section_line(Hashmap *hashmap) { + ConfigSection *cs; + unsigned n = 0; + void *entry; + + HASHMAP_FOREACH_KEY(entry, cs, hashmap) + if (n < cs->line) + n = cs->line; + + return n + 1; +} + #define DEFINE_PARSER(type, vartype, conv_func) \ DEFINE_CONFIG_PARSE_PTR(config_parse_##type, conv_func, vartype, "Failed to parse " #type " value") @@ -589,6 +644,7 @@ DEFINE_PARSER(nsec, nsec_t, parse_nsec); DEFINE_PARSER(sec, usec_t, parse_sec); DEFINE_PARSER(sec_def_infinity, usec_t, parse_sec_def_infinity); DEFINE_PARSER(mode, mode_t, parse_mode); +DEFINE_PARSER(pid, pid_t, parse_pid); int config_parse_iec_size( const char* unit, @@ -819,16 +875,110 @@ int config_parse_string( void *data, void *userdata) { - char **s = data; + char **s = ASSERT_PTR(data); assert(filename); assert(lvalue); assert(rvalue); - assert(data); + + if (isempty(rvalue)) { + *s = mfree(*s); + return 0; + } + + if (FLAGS_SET(ltype, CONFIG_PARSE_STRING_SAFE) && !string_is_safe(rvalue)) { + _cleanup_free_ char *escaped = NULL; + + escaped = cescape(rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Specified string contains unsafe characters, ignoring: %s", strna(escaped)); + return 0; + } + + if (FLAGS_SET(ltype, CONFIG_PARSE_STRING_ASCII) && !ascii_is_valid(rvalue)) { + _cleanup_free_ char *escaped = NULL; + + escaped = cescape(rvalue); + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Specified string contains invalid ASCII characters, ignoring: %s", strna(escaped)); + return 0; + } return free_and_strdup_warn(s, empty_to_null(rvalue)); } +int config_parse_dns_name( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char **hostname = ASSERT_PTR(data); + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + *hostname = mfree(*hostname); + return 0; + } + + r = dns_name_is_valid(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to check validity of DNS domain name '%s', ignoring assignment: %m", rvalue); + return 0; + } + if (r == 0) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Specified invalid DNS domain name, ignoring assignment: %s", rvalue); + return 0; + } + + return free_and_strdup_warn(hostname, rvalue); +} + +int config_parse_hostname( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char **hostname = ASSERT_PTR(data); + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + *hostname = mfree(*hostname); + return 0; + } + + if (!hostname_is_valid(rvalue, 0)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Specified invalid hostname, ignoring assignment: %s", rvalue); + return 0; + } + + return config_parse_dns_name(unit, filename, line, section, section_line, + lvalue, ltype, rvalue, data, userdata); +} + int config_parse_path( const char *unit, const char *filename, diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index d68666553..94778af45 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -69,18 +69,18 @@ typedef int (*ConfigItemLookup)( const void *table, const char *section, const char *lvalue, - ConfigParserCallback *func, - int *ltype, - void **data, + ConfigParserCallback *ret_func, + int *ret_ltype, + void **ret_data, void *userdata); /* Linear table search implementation of ConfigItemLookup, based on * ConfigTableItem arrays */ -int config_item_table_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); +int config_item_table_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *ret_func, int *ret_ltype, void **ret_data, void *userdata); /* gperf implementation of ConfigItemLookup, based on gperf * ConfigPerfItem tables */ -int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *func, int *ltype, void **data, void *userdata); +int config_item_perf_lookup(const void *table, const char *section, const char *lvalue, ConfigParserCallback *ret_func, int *ret_ltype, void **ret_data, void *userdata); int config_parse( const char *unit, @@ -114,6 +114,43 @@ int config_parse_many( void *userdata, Hashmap **ret_stats_by_path); /* possibly NULL */ +typedef struct ConfigSection { + unsigned line; + bool invalid; + char filename[]; +} ConfigSection; + +static inline ConfigSection* config_section_free(ConfigSection *cs) { + return mfree(cs); +} +DEFINE_TRIVIAL_CLEANUP_FUNC(ConfigSection*, config_section_free); + +int config_section_new(const char *filename, unsigned line, ConfigSection **s); +extern const struct hash_ops config_section_hash_ops; +unsigned hashmap_find_free_section_line(Hashmap *hashmap); + +static inline bool section_is_invalid(ConfigSection *section) { + /* If this returns false, then it does _not_ mean the section is valid. */ + + if (!section) + return false; + + return section->invalid; +} + +#define DEFINE_SECTION_CLEANUP_FUNCTIONS(type, free_func) \ + static inline type* free_func##_or_set_invalid(type *p) { \ + assert(p); \ + \ + if (p->section) \ + p->section->invalid = true; \ + else \ + free_func(p); \ + return NULL; \ + } \ + DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \ + DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid); + CONFIG_PARSER_PROTOTYPE(config_parse_int); CONFIG_PARSER_PROTOTYPE(config_parse_unsigned); CONFIG_PARSER_PROTOTYPE(config_parse_long); @@ -131,6 +168,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_bool); CONFIG_PARSER_PROTOTYPE(config_parse_id128); CONFIG_PARSER_PROTOTYPE(config_parse_tristate); CONFIG_PARSER_PROTOTYPE(config_parse_string); +CONFIG_PARSER_PROTOTYPE(config_parse_dns_name); +CONFIG_PARSER_PROTOTYPE(config_parse_hostname); CONFIG_PARSER_PROTOTYPE(config_parse_path); CONFIG_PARSER_PROTOTYPE(config_parse_strv); CONFIG_PARSER_PROTOTYPE(config_parse_sec); @@ -157,6 +196,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ether_addrs); CONFIG_PARSER_PROTOTYPE(config_parse_in_addr_non_null); CONFIG_PARSER_PROTOTYPE(config_parse_percent); CONFIG_PARSER_PROTOTYPE(config_parse_permyriad); +CONFIG_PARSER_PROTOTYPE(config_parse_pid); typedef enum Disabled { DISABLED_CONFIGURATION, @@ -164,6 +204,13 @@ typedef enum Disabled { DISABLED_EXPERIMENTAL, } Disabled; +typedef enum ConfigParseStringFlags { + CONFIG_PARSE_STRING_SAFE = 1 << 0, + CONFIG_PARSE_STRING_ASCII = 1 << 1, + + CONFIG_PARSE_STRING_SAFE_AND_ASCII = CONFIG_PARSE_STRING_SAFE | CONFIG_PARSE_STRING_ASCII, +} ConfigParseStringFlags; + #define DEFINE_CONFIG_PARSE(function, parser, msg) \ CONFIG_PARSER_PROTOTYPE(function) { \ int *i = data, r; \ diff --git a/src/shared/copy.c b/src/shared/copy.c index 1ace40424..0043c4ff7 100644 --- a/src/shared/copy.c +++ b/src/shared/copy.c @@ -107,6 +107,47 @@ static int look_for_signals(CopyFlags copy_flags) { return 0; } +static int create_hole(int fd, off_t size) { + off_t offset; + off_t end; + + offset = lseek(fd, 0, SEEK_CUR); + if (offset < 0) + return -errno; + + end = lseek(fd, 0, SEEK_END); + if (end < 0) + return -errno; + + /* If we're not at the end of the target file, try to punch a hole in the existing space using fallocate(). */ + + if (offset < end && + fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, offset, MIN(size, end - offset)) < 0 && + !ERRNO_IS_NOT_SUPPORTED(errno)) + return -errno; + + if (end - offset >= size) { + /* If we've created the full hole, set the file pointer to the end of the hole we created and exit. */ + if (lseek(fd, offset + size, SEEK_SET) < 0) + return -errno; + + return 0; + } + + /* If we haven't created the full hole, use ftruncate() to grow the file (and the hole) to the + * required size and move the file pointer to the end of the file. */ + + size -= end - offset; + + if (ftruncate(fd, end + size) < 0) + return -errno; + + if (lseek(fd, 0, SEEK_END) < 0) + return -errno; + + return 0; +} + int copy_bytes_full( int fdf, int fdt, uint64_t max_bytes, @@ -202,6 +243,49 @@ int copy_bytes_full( if (max_bytes != UINT64_MAX && m > max_bytes) m = max_bytes; + if (copy_flags & COPY_HOLES) { + off_t c, e; + + c = lseek(fdf, 0, SEEK_CUR); + if (c < 0) + return -errno; + + /* To see if we're in a hole, we search for the next data offset. */ + e = lseek(fdf, c, SEEK_DATA); + if (e < 0 && errno == ENXIO) + /* If errno == ENXIO, that means we've reached the final hole of the file and + * that hole isn't followed by more data. */ + e = lseek(fdf, 0, SEEK_END); + if (e < 0) + return -errno; + + /* If we're in a hole (current offset is not a data offset), create a hole of the + * same size in the target file. */ + if (e > c) { + r = create_hole(fdt, e - c); + if (r < 0) + return r; + } + + c = e; /* Set c to the start of the data segment. */ + + /* After copying a potential hole, find the end of the data segment by looking for + * the next hole. If we get ENXIO, we're at EOF. */ + e = lseek(fdf, c, SEEK_HOLE); + if (e < 0) { + if (errno == ENXIO) + break; + return -errno; + } + + /* SEEK_HOLE modifies the file offset so we need to move back to the initial offset. */ + if (lseek(fdf, c, SEEK_SET) < 0) + return -errno; + + /* Make sure we're not copying more than the current data segment. */ + m = MIN(m, (size_t) e - c); + } + /* First try copy_file_range(), unless we already tried */ if (try_cfr) { n = try_copy_file_range(fdf, NULL, fdt, NULL, m, 0u); @@ -1130,7 +1214,10 @@ int copy_file_fd_full( if (r < 0) return r; - if (S_ISREG(fdt)) { + /* Make sure to copy file attributes only over if target is a regular + * file (so that copying a file to /dev/null won't alter the access + * mode/ownership of that device node...) */ + if (S_ISREG(st.st_mode)) { (void) copy_times(fdf, fdt, copy_flags); (void) copy_xattr(fdf, fdt, copy_flags); } @@ -1401,7 +1488,7 @@ int copy_xattr(int fdf, int fdt, CopyFlags copy_flags) { NULSTR_FOREACH(p, names) { _cleanup_free_ char *value = NULL; - if (!(copy_flags & COPY_ALL_XATTRS) && !startswith(p, "user.")) + if (!FLAGS_SET(copy_flags, COPY_ALL_XATTRS) && !startswith(p, "user.")) continue; r = fgetxattr_malloc(fdf, p, &value); diff --git a/src/shared/copy.h b/src/shared/copy.h index a7b45b4fb..d755916bd 100644 --- a/src/shared/copy.h +++ b/src/shared/copy.h @@ -24,6 +24,7 @@ typedef enum CopyFlags { COPY_FSYNC_FULL = 1 << 11, /* fsync_full() after we are done */ COPY_SYNCFS = 1 << 12, /* syncfs() the *top-level* dir after we are done */ COPY_ALL_XATTRS = 1 << 13, /* Preserve all xattrs when copying, not just those in the user namespace */ + COPY_HOLES = 1 << 14, /* Copy holes */ } CopyFlags; typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata); diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index 4d0681bc1..c4dcc396a 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -534,6 +534,7 @@ int encrypt_credential_and_warn( r = tpm2_seal(tpm2_device, tpm2_pcr_mask, + NULL, &tpm2_key, &tpm2_key_size, &tpm2_blob, @@ -803,6 +804,7 @@ int decrypt_credential_and_warn( le32toh(t->blob_size), t->policy_hash_and_blob + le32toh(t->blob_size), le32toh(t->policy_hash_size), + NULL, &tpm2_key, &tpm2_key_size); if (r < 0) diff --git a/src/shared/discover-image.c b/src/shared/discover-image.c index 268d91021..3f1644ea8 100644 --- a/src/shared/discover-image.c +++ b/src/shared/discover-image.c @@ -85,7 +85,7 @@ DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(image_hash_ops, char, string_hash_func, st static char **image_settings_path(Image *image) { _cleanup_strv_free_ char **l = NULL; - const char *fn, *s; + const char *fn; unsigned i = 0; assert(image); @@ -632,7 +632,6 @@ int image_remove(Image *i) { _cleanup_(release_lock_file) LockFile global_lock = LOCK_FILE_INIT, local_lock = LOCK_FILE_INIT; _cleanup_strv_free_ char **settings = NULL; _cleanup_free_ char *roothash = NULL; - char **j; int r; assert(i); @@ -695,10 +694,9 @@ int image_remove(Image *i) { return -EOPNOTSUPP; } - STRV_FOREACH(j, settings) { + STRV_FOREACH(j, settings) if (unlink(*j) < 0 && errno != ENOENT) log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", *j); - } if (unlink(roothash) < 0 && errno != ENOENT) log_debug_errno(errno, "Failed to unlink %s, ignoring: %m", roothash); @@ -724,7 +722,6 @@ int image_rename(Image *i, const char *new_name) { _cleanup_free_ char *new_path = NULL, *nn = NULL, *roothash = NULL; _cleanup_strv_free_ char **settings = NULL; unsigned file_attr = 0; - char **j; int r; assert(i); @@ -845,7 +842,6 @@ int image_clone(Image *i, const char *new_name, bool read_only) { _cleanup_strv_free_ char **settings = NULL; _cleanup_free_ char *roothash = NULL; const char *new_path; - char **j; int r; assert(i); diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index 14519ead7..f4756bf8c 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -1807,7 +1807,7 @@ static int mount_partition( (void) fs_grow(node, p); if (remap_uid_gid) { - r = remount_idmap(p, uid_shift, uid_range); + r = remount_idmap(p, uid_shift, uid_range, REMOUNT_IDMAP_HOST_ROOT); if (r < 0) return r; } @@ -2208,7 +2208,6 @@ static int validate_signature_userspace(const VeritySettings *verity) { _cleanup_(BIO_freep) BIO *bio = NULL; /* 'bio' must be freed first, 's' second, hence keep this order * of declaration in place, please */ const unsigned char *d; - char **i; int r; assert(verity); @@ -3042,7 +3041,6 @@ int dissected_image_acquire_metadata(DissectedImage *m, DissectImageFlags extra_ case META_HAS_INIT_SYSTEM: { bool found = false; - const char *init; FOREACH_STRING(init, "/usr/lib/systemd/systemd", /* systemd on /usr merged system */ @@ -3333,8 +3331,6 @@ MountOptions* mount_options_free_all(MountOptions *options) { } const char* mount_options_from_designator(const MountOptions *options, PartitionDesignator designator) { - const MountOptions *m; - LIST_FOREACH(mount_options, m, options) if (designator == m->partition_designator && !isempty(m->options)) return m->options; diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c index f54b187a1..0ae1de1c5 100644 --- a/src/shared/dns-domain.c +++ b/src/shared/dns-domain.c @@ -168,24 +168,18 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha /* Skip current terminal character (and accept domain names ending it ".") */ if (*terminal == 0) - terminal--; + terminal = PTR_SUB1(terminal, name); if (terminal >= name && *terminal == '.') - terminal--; + terminal = PTR_SUB1(terminal, name); /* Point name to the last label, and terminal to the preceding terminal symbol (or make it a NULL pointer) */ - for (;;) { - if (terminal < name) { - /* Reached the first label, so indicate that there are no more */ - terminal = NULL; - break; - } - + while (terminal) { /* Find the start of the last label */ if (*terminal == '.') { const char *y; unsigned slashes = 0; - for (y = terminal - 1; y >= name && *y == '\\'; y--) + for (y = PTR_SUB1(terminal, name); y && *y == '\\'; y = PTR_SUB1(y, name)) slashes++; if (slashes % 2 == 0) { @@ -198,7 +192,7 @@ int dns_label_unescape_suffix(const char *name, const char **label_terminal, cha } } - terminal--; + terminal = PTR_SUB1(terminal, name); } r = dns_label_unescape(&name, dest, sz, 0); @@ -1415,3 +1409,18 @@ int dns_name_dot_suffixed(const char *name) { return false; } } + +bool dns_name_dont_resolve(const char *name) { + + /* Never respond to some of the domains listed in RFC6303 */ + if (dns_name_endswith(name, "0.in-addr.arpa") > 0 || + dns_name_equal(name, "255.255.255.255.in-addr.arpa") > 0 || + dns_name_equal(name, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa") > 0) + return true; + + /* Never respond to some of the domains listed in RFC6761 */ + if (dns_name_endswith(name, "invalid") > 0) + return true; + + return false; +} diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h index 24bf00bd5..e5f3d4d9e 100644 --- a/src/shared/dns-domain.h +++ b/src/shared/dns-domain.h @@ -103,3 +103,5 @@ int dns_name_apply_idna(const char *name, char **ret); int dns_name_is_valid_or_address(const char *name); int dns_name_dot_suffixed(const char *name); + +bool dns_name_dont_resolve(const char *name); diff --git a/src/shared/dropin.c b/src/shared/dropin.c index eb016eb11..375a3ca60 100644 --- a/src/shared/dropin.c +++ b/src/shared/dropin.c @@ -232,7 +232,6 @@ int unit_file_find_dropin_paths( _cleanup_strv_free_ char **dirs = NULL; const char *n; - char **p; int r; assert(ret); diff --git a/src/shared/efi-api.c b/src/shared/efi-api.c new file mode 100644 index 000000000..b2c6af875 --- /dev/null +++ b/src/shared/efi-api.c @@ -0,0 +1,556 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "alloc-util.h" +#include "dirent-util.h" +#include "efi-api.h" +#include "efivars.h" +#include "fd-util.h" +#include "sort-util.h" +#include "stat-util.h" +#include "stdio-util.h" +#include "utf8.h" + +#if ENABLE_EFI + +#define LOAD_OPTION_ACTIVE 0x00000001 +#define MEDIA_DEVICE_PATH 0x04 +#define MEDIA_HARDDRIVE_DP 0x01 +#define MEDIA_FILEPATH_DP 0x04 +#define SIGNATURE_TYPE_GUID 0x02 +#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02 +#define END_DEVICE_PATH_TYPE 0x7f +#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff + +#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI UINT64_C(0x0000000000000001) + +#define boot_option__contents \ + { \ + uint32_t attr; \ + uint16_t path_len; \ + uint16_t title[]; \ + } + +struct boot_option boot_option__contents; +struct boot_option__packed boot_option__contents _packed_; +assert_cc(offsetof(struct boot_option, title) == offsetof(struct boot_option__packed, title)); +/* sizeof(struct boot_option) != sizeof(struct boot_option__packed), so + * the *size* of the structure should not be used anywhere below. */ + +struct drive_path { + uint32_t part_nr; + uint64_t part_start; + uint64_t part_size; + char signature[16]; + uint8_t mbr_type; + uint8_t signature_type; +} _packed_; + +#define device_path__contents \ + { \ + uint8_t type; \ + uint8_t sub_type; \ + uint16_t length; \ + union { \ + uint16_t path[0]; \ + struct drive_path drive; \ + }; \ + } + +struct device_path device_path__contents; +struct device_path__packed device_path__contents _packed_; +assert_cc(sizeof(struct device_path) == sizeof(struct device_path__packed)); + +int efi_reboot_to_firmware_supported(void) { + _cleanup_free_ void *v = NULL; + static int cache = -1; + uint64_t b; + size_t s; + int r; + + if (cache > 0) + return 0; + if (cache == 0) + return -EOPNOTSUPP; + + if (!is_efi_boot()) + goto not_supported; + + r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndicationsSupported), NULL, &v, &s); + if (r == -ENOENT) + goto not_supported; /* variable doesn't exist? it's not supported then */ + if (r < 0) + return r; + if (s != sizeof(uint64_t)) + return -EINVAL; + + b = *(uint64_t*) v; + if (!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) + goto not_supported; /* bit unset? it's not supported then */ + + cache = 1; + return 0; + +not_supported: + cache = 0; + return -EOPNOTSUPP; +} + +static int get_os_indications(uint64_t *ret) { + static struct stat cache_stat = {}; + _cleanup_free_ void *v = NULL; + static uint64_t cache; + struct stat new_stat; + size_t s; + int r; + + assert(ret); + + /* Let's verify general support first */ + r = efi_reboot_to_firmware_supported(); + if (r < 0) + return r; + + /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */ + if (stat(EFIVAR_PATH(EFI_GLOBAL_VARIABLE(OsIndications)), &new_stat) < 0) { + if (errno != ENOENT) + return -errno; + + /* Doesn't exist? Then we can exit early (also see below) */ + *ret = 0; + return 0; + + } else if (stat_inode_unmodified(&new_stat, &cache_stat)) { + /* inode didn't change, we can return the cached value */ + *ret = cache; + return 0; + } + + r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndications), NULL, &v, &s); + if (r == -ENOENT) { + /* Some firmware implementations that do support OsIndications and report that with + * OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's + * pretend it's 0 then, to hide this implementation detail. Note that this call will return + * -ENOENT then only if the support for OsIndications is missing entirely, as determined by + * efi_reboot_to_firmware_supported() above. */ + *ret = 0; + return 0; + } + if (r < 0) + return r; + if (s != sizeof(uint64_t)) + return -EINVAL; + + cache_stat = new_stat; + *ret = cache = *(uint64_t *)v; + return 0; +} + +int efi_get_reboot_to_firmware(void) { + int r; + uint64_t b; + + r = get_os_indications(&b); + if (r < 0) + return r; + + return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI); +} + +int efi_set_reboot_to_firmware(bool value) { + int r; + uint64_t b, b_new; + + r = get_os_indications(&b); + if (r < 0) + return r; + + b_new = UPDATE_FLAG(b, EFI_OS_INDICATIONS_BOOT_TO_FW_UI, value); + + /* Avoid writing to efi vars store if we can due to firmware bugs. */ + if (b != b_new) + return efi_set_variable(EFI_GLOBAL_VARIABLE(OsIndications), &b_new, sizeof(uint64_t)); + + return 0; +} + +static ssize_t utf16_size(const uint16_t *s, size_t buf_len_bytes) { + size_t l = 0; + + /* Returns the size of the string in bytes without the terminating two zero bytes */ + + if (buf_len_bytes % sizeof(uint16_t) != 0) + return -EINVAL; + + while (l < buf_len_bytes / sizeof(uint16_t)) { + if (s[l] == 0) + return (l + 1) * sizeof(uint16_t); + l++; + } + + return -EINVAL; /* The terminator was not found */ +} + +struct guid { + uint32_t u1; + uint16_t u2; + uint16_t u3; + uint8_t u4[8]; +} _packed_; + +static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) { + uint32_t u1; + uint16_t u2, u3; + const struct guid *uuid = guid; + + memcpy(&u1, &uuid->u1, sizeof(uint32_t)); + id128->bytes[0] = (u1 >> 24) & 0xff; + id128->bytes[1] = (u1 >> 16) & 0xff; + id128->bytes[2] = (u1 >> 8) & 0xff; + id128->bytes[3] = u1 & 0xff; + memcpy(&u2, &uuid->u2, sizeof(uint16_t)); + id128->bytes[4] = (u2 >> 8) & 0xff; + id128->bytes[5] = u2 & 0xff; + memcpy(&u3, &uuid->u3, sizeof(uint16_t)); + id128->bytes[6] = (u3 >> 8) & 0xff; + id128->bytes[7] = u3 & 0xff; + memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4)); +} + +int efi_get_boot_option( + uint16_t id, + char **ret_title, + sd_id128_t *ret_part_uuid, + char **ret_path, + bool *ret_active) { + + char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1]; + _cleanup_free_ uint8_t *buf = NULL; + size_t l; + struct boot_option *header; + ssize_t title_size; + _cleanup_free_ char *s = NULL, *p = NULL; + sd_id128_t p_uuid = SD_ID128_NULL; + int r; + + if (!is_efi_boot()) + return -EOPNOTSUPP; + + xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id); + r = efi_get_variable(variable, NULL, (void **)&buf, &l); + if (r < 0) + return r; + if (l < offsetof(struct boot_option, title)) + return -ENOENT; + + header = (struct boot_option *)buf; + title_size = utf16_size(header->title, l - offsetof(struct boot_option, title)); + if (title_size < 0) + return title_size; + + if (ret_title) { + s = utf16_to_utf8(header->title, title_size); + if (!s) + return -ENOMEM; + } + + if (header->path_len > 0) { + uint8_t *dbuf; + size_t dnext, doff; + + doff = offsetof(struct boot_option, title) + title_size; + dbuf = buf + doff; + if (header->path_len > l - doff) + return -EINVAL; + + dnext = 0; + while (dnext < header->path_len) { + struct device_path *dpath; + + dpath = (struct device_path *)(dbuf + dnext); + if (dpath->length < 4) + break; + + /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */ + if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE) + break; + + dnext += dpath->length; + + /* Type 0x04 – Media Device Path */ + if (dpath->type != MEDIA_DEVICE_PATH) + continue; + + /* Sub-Type 1 – Hard Drive */ + if (dpath->sub_type == MEDIA_HARDDRIVE_DP) { + /* 0x02 – GUID Partition Table */ + if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER) + continue; + + /* 0x02 – GUID signature */ + if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID) + continue; + + if (ret_part_uuid) + efi_guid_to_id128(dpath->drive.signature, &p_uuid); + continue; + } + + /* Sub-Type 4 – File Path */ + if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && ret_path) { + p = utf16_to_utf8(dpath->path, dpath->length-4); + if (!p) + return -ENOMEM; + + efi_tilt_backslashes(p); + continue; + } + } + } + + if (ret_title) + *ret_title = TAKE_PTR(s); + if (ret_part_uuid) + *ret_part_uuid = p_uuid; + if (ret_path) + *ret_path = TAKE_PTR(p); + if (ret_active) + *ret_active = header->attr & LOAD_OPTION_ACTIVE; + + return 0; +} + +static void to_utf16(uint16_t *dest, const char *src) { + int i; + + for (i = 0; src[i] != '\0'; i++) + dest[i] = src[i]; + dest[i] = '\0'; +} + +static void id128_to_efi_guid(sd_id128_t id, void *guid) { + struct guid uuid = { + .u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3], + .u2 = id.bytes[4] << 8 | id.bytes[5], + .u3 = id.bytes[6] << 8 | id.bytes[7], + }; + memcpy(uuid.u4, id.bytes+8, sizeof(uuid.u4)); + memcpy(guid, &uuid, sizeof(uuid)); +} + +static uint16_t *tilt_slashes(uint16_t *s) { + for (uint16_t *p = s; *p; p++) + if (*p == '/') + *p = '\\'; + + return s; +} + +int efi_add_boot_option( + uint16_t id, + const char *title, + uint32_t part, + uint64_t pstart, + uint64_t psize, + sd_id128_t part_uuid, + const char *path) { + + size_t size, title_len, path_len; + _cleanup_free_ char *buf = NULL; + struct boot_option *option; + struct device_path *devicep; + char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1]; + + if (!is_efi_boot()) + return -EOPNOTSUPP; + + title_len = (strlen(title)+1) * 2; + path_len = (strlen(path)+1) * 2; + + buf = malloc0(offsetof(struct boot_option, title) + title_len + + sizeof(struct drive_path) + + sizeof(struct device_path) + path_len); + if (!buf) + return -ENOMEM; + + /* header */ + option = (struct boot_option *)buf; + option->attr = LOAD_OPTION_ACTIVE; + option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) + + offsetof(struct device_path, path) + path_len + + offsetof(struct device_path, path); + to_utf16(option->title, title); + size = offsetof(struct boot_option, title) + title_len; + + /* partition info */ + devicep = (struct device_path *)(buf + size); + devicep->type = MEDIA_DEVICE_PATH; + devicep->sub_type = MEDIA_HARDDRIVE_DP; + devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path); + memcpy(&devicep->drive.part_nr, &part, sizeof(uint32_t)); + memcpy(&devicep->drive.part_start, &pstart, sizeof(uint64_t)); + memcpy(&devicep->drive.part_size, &psize, sizeof(uint64_t)); + id128_to_efi_guid(part_uuid, devicep->drive.signature); + devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; + devicep->drive.signature_type = SIGNATURE_TYPE_GUID; + size += devicep->length; + + /* path to loader */ + devicep = (struct device_path *)(buf + size); + devicep->type = MEDIA_DEVICE_PATH; + devicep->sub_type = MEDIA_FILEPATH_DP; + devicep->length = offsetof(struct device_path, path) + path_len; + to_utf16(devicep->path, path); + tilt_slashes(devicep->path); + size += devicep->length; + + /* end of path */ + devicep = (struct device_path *)(buf + size); + devicep->type = END_DEVICE_PATH_TYPE; + devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE; + devicep->length = offsetof(struct device_path, path); + size += devicep->length; + + xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id); + return efi_set_variable(variable, buf, size); +} + +int efi_remove_boot_option(uint16_t id) { + char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1]; + + if (!is_efi_boot()) + return -EOPNOTSUPP; + + xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id); + return efi_set_variable(variable, NULL, 0); +} + +int efi_get_boot_order(uint16_t **ret_order) { + _cleanup_free_ void *buf = NULL; + size_t l; + int r; + + assert(ret_order); + + if (!is_efi_boot()) + return -EOPNOTSUPP; + + r = efi_get_variable(EFI_GLOBAL_VARIABLE(BootOrder), NULL, &buf, &l); + if (r < 0) + return r; + + if (l <= 0) + return -ENOENT; + + if (l % sizeof(uint16_t) > 0 || + l / sizeof(uint16_t) > INT_MAX) + return -EINVAL; + + *ret_order = TAKE_PTR(buf); + return (int) (l / sizeof(uint16_t)); +} + +int efi_set_boot_order(const uint16_t *order, size_t n) { + + if (!is_efi_boot()) + return -EOPNOTSUPP; + + return efi_set_variable(EFI_GLOBAL_VARIABLE(BootOrder), order, n * sizeof(uint16_t)); +} + +static int boot_id_hex(const char s[static 4]) { + int id = 0; + + assert(s); + + for (int i = 0; i < 4; i++) + if (s[i] >= '0' && s[i] <= '9') + id |= (s[i] - '0') << (3 - i) * 4; + else if (s[i] >= 'A' && s[i] <= 'F') + id |= (s[i] - 'A' + 10) << (3 - i) * 4; + else + return -EINVAL; + + return id; +} + +static int cmp_uint16(const uint16_t *a, const uint16_t *b) { + return CMP(*a, *b); +} + +int efi_get_boot_options(uint16_t **ret_options) { + _cleanup_closedir_ DIR *dir = NULL; + _cleanup_free_ uint16_t *list = NULL; + int count = 0; + + assert(ret_options); + + if (!is_efi_boot()) + return -EOPNOTSUPP; + + dir = opendir(EFIVAR_PATH(".")); + if (!dir) + return -errno; + + FOREACH_DIRENT(de, dir, return -errno) { + int id; + + if (strncmp(de->d_name, "Boot", 4) != 0) + continue; + + if (strlen(de->d_name) != 45) + continue; + + if (strcmp(de->d_name + 8, EFI_GLOBAL_VARIABLE_STR("")) != 0) /* generate variable suffix using macro */ + continue; + + id = boot_id_hex(de->d_name + 4); + if (id < 0) + continue; + + if (!GREEDY_REALLOC(list, count + 1)) + return -ENOMEM; + + list[count++] = id; + } + + typesafe_qsort(list, count, cmp_uint16); + + *ret_options = TAKE_PTR(list); + + return count; +} + +bool efi_has_tpm2(void) { + static int cache = -1; + + /* Returns whether the system has a TPM2 chip which is known to the EFI firmware. */ + + if (cache < 0) { + + /* First, check if we are on an EFI boot at all. */ + if (!is_efi_boot()) + cache = false; + else { + /* Then, check if the ACPI table "TPM2" exists, which is the TPM2 event log table, see: + * https://trustedcomputinggroup.org/wp-content/uploads/TCG_ACPIGeneralSpecification_v1.20_r8.pdf + * This table exists whenever the firmware is hooked up to TPM2. */ + cache = access("/sys/firmware/acpi/tables/TPM2", F_OK) >= 0; + if (!cache && errno != ENOENT) + log_debug_errno(errno, "Unable to test whether /sys/firmware/acpi/tables/TPM2 exists, assuming it doesn't: %m"); + } + } + + return cache; +} + +#endif + +char *efi_tilt_backslashes(char *s) { + for (char *p = s; *p; p++) + if (*p == '\\') + *p = '/'; + + return s; +} diff --git a/src/shared/efi-api.h b/src/shared/efi-api.h new file mode 100644 index 000000000..5acc9e83c --- /dev/null +++ b/src/shared/efi-api.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "efivars-fundamental.h" +#include "efivars.h" + +/* Various calls for interfacing with EFI variables from the official UEFI specs. */ + +#if ENABLE_EFI + +int efi_reboot_to_firmware_supported(void); +int efi_get_reboot_to_firmware(void); +int efi_set_reboot_to_firmware(bool value); + +int efi_get_boot_option(uint16_t nr, char **ret_title, sd_id128_t *ret_part_uuid, char **ret_path, bool *ret_active); +int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path); +int efi_remove_boot_option(uint16_t id); +int efi_get_boot_order(uint16_t **ret_order); +int efi_set_boot_order(const uint16_t *order, size_t n); +int efi_get_boot_options(uint16_t **ret_options); + +bool efi_has_tpm2(void); + +#else + +static inline int efi_reboot_to_firmware_supported(void) { + return -EOPNOTSUPP; +} + +static inline int efi_get_reboot_to_firmware(void) { + return -EOPNOTSUPP; +} + +static inline int efi_set_reboot_to_firmware(bool value) { + return -EOPNOTSUPP; +} + +static inline int efi_get_boot_option(uint16_t nr, char **ret_title, sd_id128_t *ret_part_uuid, char **ret_path, bool *ret_active) { + return -EOPNOTSUPP; +} + +static inline int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path) { + return -EOPNOTSUPP; +} + +static inline int efi_remove_boot_option(uint16_t id) { + return -EOPNOTSUPP; +} + +static inline int efi_get_boot_order(uint16_t **ret_order) { + return -EOPNOTSUPP; +} + +static inline int efi_set_boot_order(const uint16_t *order, size_t n) { + return -EOPNOTSUPP; +} + +static inline int efi_get_boot_options(uint16_t **ret_options) { + return -EOPNOTSUPP; +} + +static inline bool efi_has_tpm2(void) { + return false; +} + +#endif + +char *efi_tilt_backslashes(char *s); diff --git a/src/shared/efi-loader.c b/src/shared/efi-loader.c index b14a2c32a..e114d5ea2 100644 --- a/src/shared/efi-loader.c +++ b/src/shared/efi-loader.c @@ -1,537 +1,22 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -#include -#include -#include - #include "alloc-util.h" -#include "dirent-util.h" #include "efi-loader.h" -#include "efivars.h" -#include "fd-util.h" -#include "io-util.h" #include "parse-util.h" -#include "sort-util.h" +#include "path-util.h" #include "stat-util.h" -#include "stdio-util.h" -#include "string-util.h" +#include "strv.h" #include "utf8.h" -#include "virt.h" #if ENABLE_EFI -#define LOAD_OPTION_ACTIVE 0x00000001 -#define MEDIA_DEVICE_PATH 0x04 -#define MEDIA_HARDDRIVE_DP 0x01 -#define MEDIA_FILEPATH_DP 0x04 -#define SIGNATURE_TYPE_GUID 0x02 -#define MBR_TYPE_EFI_PARTITION_TABLE_HEADER 0x02 -#define END_DEVICE_PATH_TYPE 0x7f -#define END_ENTIRE_DEVICE_PATH_SUBTYPE 0xff -#define EFI_OS_INDICATIONS_BOOT_TO_FW_UI 0x0000000000000001 - -#define boot_option__contents \ - { \ - uint32_t attr; \ - uint16_t path_len; \ - uint16_t title[]; \ - } - -struct boot_option boot_option__contents; -struct boot_option__packed boot_option__contents _packed_; -assert_cc(offsetof(struct boot_option, title) == offsetof(struct boot_option__packed, title)); -/* sizeof(struct boot_option) != sizeof(struct boot_option__packed), so - * the *size* of the structure should not be used anywhere below. */ - -struct drive_path { - uint32_t part_nr; - uint64_t part_start; - uint64_t part_size; - char signature[16]; - uint8_t mbr_type; - uint8_t signature_type; -} _packed_; - -#define device_path__contents \ - { \ - uint8_t type; \ - uint8_t sub_type; \ - uint16_t length; \ - union { \ - uint16_t path[0]; \ - struct drive_path drive; \ - }; \ - } - -struct device_path device_path__contents; -struct device_path__packed device_path__contents _packed_; -assert_cc(sizeof(struct device_path) == sizeof(struct device_path__packed)); - -int efi_reboot_to_firmware_supported(void) { - _cleanup_free_ void *v = NULL; - static int cache = -1; - uint64_t b; - size_t s; - int r; - - if (cache > 0) - return 0; - if (cache == 0) - return -EOPNOTSUPP; - - if (!is_efi_boot()) - goto not_supported; - - r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndicationsSupported), NULL, &v, &s); - if (r == -ENOENT) - goto not_supported; /* variable doesn't exist? it's not supported then */ - if (r < 0) - return r; - if (s != sizeof(uint64_t)) - return -EINVAL; - - b = *(uint64_t*) v; - if (!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) - goto not_supported; /* bit unset? it's not supported then */ - - cache = 1; - return 0; - -not_supported: - cache = 0; - return -EOPNOTSUPP; -} - -static int get_os_indications(uint64_t *ret) { - static struct stat cache_stat = {}; - _cleanup_free_ void *v = NULL; - static uint64_t cache; - struct stat new_stat; - size_t s; - int r; - - assert(ret); - - /* Let's verify general support first */ - r = efi_reboot_to_firmware_supported(); - if (r < 0) - return r; - - /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */ - if (stat(EFIVAR_PATH(EFI_GLOBAL_VARIABLE(OsIndications)), &new_stat) < 0) { - if (errno != ENOENT) - return -errno; - - /* Doesn't exist? Then we can exit early (also see below) */ - *ret = 0; - return 0; - - } else if (stat_inode_unmodified(&new_stat, &cache_stat)) { - /* inode didn't change, we can return the cached value */ - *ret = cache; - return 0; - } - - r = efi_get_variable(EFI_GLOBAL_VARIABLE(OsIndications), NULL, &v, &s); - if (r == -ENOENT) { - /* Some firmware implementations that do support OsIndications and report that with - * OsIndicationsSupported will remove the OsIndications variable when it is unset. Let's - * pretend it's 0 then, to hide this implementation detail. Note that this call will return - * -ENOENT then only if the support for OsIndications is missing entirely, as determined by - * efi_reboot_to_firmware_supported() above. */ - *ret = 0; - return 0; - } - if (r < 0) - return r; - if (s != sizeof(uint64_t)) - return -EINVAL; - - cache_stat = new_stat; - *ret = cache = *(uint64_t *)v; - return 0; -} - -int efi_get_reboot_to_firmware(void) { - int r; - uint64_t b; - - r = get_os_indications(&b); - if (r < 0) - return r; - - return !!(b & EFI_OS_INDICATIONS_BOOT_TO_FW_UI); -} - -int efi_set_reboot_to_firmware(bool value) { - int r; - uint64_t b, b_new; - - r = get_os_indications(&b); - if (r < 0) - return r; - - b_new = UPDATE_FLAG(b, EFI_OS_INDICATIONS_BOOT_TO_FW_UI, value); - - /* Avoid writing to efi vars store if we can due to firmware bugs. */ - if (b != b_new) - return efi_set_variable(EFI_GLOBAL_VARIABLE(OsIndications), &b_new, sizeof(uint64_t)); - - return 0; -} - -static ssize_t utf16_size(const uint16_t *s, size_t buf_len_bytes) { - size_t l = 0; - - /* Returns the size of the string in bytes without the terminating two zero bytes */ - - if (buf_len_bytes % sizeof(uint16_t) != 0) - return -EINVAL; - - while (l < buf_len_bytes / sizeof(uint16_t)) { - if (s[l] == 0) - return (l + 1) * sizeof(uint16_t); - l++; - } - - return -EINVAL; /* The terminator was not found */ -} - -struct guid { - uint32_t u1; - uint16_t u2; - uint16_t u3; - uint8_t u4[8]; -} _packed_; - -static void efi_guid_to_id128(const void *guid, sd_id128_t *id128) { - uint32_t u1; - uint16_t u2, u3; - const struct guid *uuid = guid; - - memcpy(&u1, &uuid->u1, sizeof(uint32_t)); - id128->bytes[0] = (u1 >> 24) & 0xff; - id128->bytes[1] = (u1 >> 16) & 0xff; - id128->bytes[2] = (u1 >> 8) & 0xff; - id128->bytes[3] = u1 & 0xff; - memcpy(&u2, &uuid->u2, sizeof(uint16_t)); - id128->bytes[4] = (u2 >> 8) & 0xff; - id128->bytes[5] = u2 & 0xff; - memcpy(&u3, &uuid->u3, sizeof(uint16_t)); - id128->bytes[6] = (u3 >> 8) & 0xff; - id128->bytes[7] = u3 & 0xff; - memcpy(&id128->bytes[8], uuid->u4, sizeof(uuid->u4)); -} - -int efi_get_boot_option( - uint16_t id, - char **title, - sd_id128_t *part_uuid, - char **path, - bool *active) { - - char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1]; - _cleanup_free_ uint8_t *buf = NULL; - size_t l; - struct boot_option *header; - ssize_t title_size; - _cleanup_free_ char *s = NULL, *p = NULL; - sd_id128_t p_uuid = SD_ID128_NULL; - int r; - - if (!is_efi_boot()) - return -EOPNOTSUPP; - - xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id); - r = efi_get_variable(variable, NULL, (void **)&buf, &l); - if (r < 0) - return r; - if (l < offsetof(struct boot_option, title)) - return -ENOENT; - - header = (struct boot_option *)buf; - title_size = utf16_size(header->title, l - offsetof(struct boot_option, title)); - if (title_size < 0) - return title_size; - - if (title) { - s = utf16_to_utf8(header->title, title_size); - if (!s) - return -ENOMEM; - } - - if (header->path_len > 0) { - uint8_t *dbuf; - size_t dnext, doff; - - doff = offsetof(struct boot_option, title) + title_size; - dbuf = buf + doff; - if (header->path_len > l - doff) - return -EINVAL; - - dnext = 0; - while (dnext < header->path_len) { - struct device_path *dpath; - - dpath = (struct device_path *)(dbuf + dnext); - if (dpath->length < 4) - break; - - /* Type 0x7F – End of Hardware Device Path, Sub-Type 0xFF – End Entire Device Path */ - if (dpath->type == END_DEVICE_PATH_TYPE && dpath->sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE) - break; - - dnext += dpath->length; - - /* Type 0x04 – Media Device Path */ - if (dpath->type != MEDIA_DEVICE_PATH) - continue; - - /* Sub-Type 1 – Hard Drive */ - if (dpath->sub_type == MEDIA_HARDDRIVE_DP) { - /* 0x02 – GUID Partition Table */ - if (dpath->drive.mbr_type != MBR_TYPE_EFI_PARTITION_TABLE_HEADER) - continue; - - /* 0x02 – GUID signature */ - if (dpath->drive.signature_type != SIGNATURE_TYPE_GUID) - continue; - - if (part_uuid) - efi_guid_to_id128(dpath->drive.signature, &p_uuid); - continue; - } - - /* Sub-Type 4 – File Path */ - if (dpath->sub_type == MEDIA_FILEPATH_DP && !p && path) { - p = utf16_to_utf8(dpath->path, dpath->length-4); - if (!p) - return -ENOMEM; - - efi_tilt_backslashes(p); - continue; - } - } - } - - if (title) - *title = TAKE_PTR(s); - if (part_uuid) - *part_uuid = p_uuid; - if (path) - *path = TAKE_PTR(p); - if (active) - *active = header->attr & LOAD_OPTION_ACTIVE; - - return 0; -} - -static void to_utf16(uint16_t *dest, const char *src) { - int i; - - for (i = 0; src[i] != '\0'; i++) - dest[i] = src[i]; - dest[i] = '\0'; -} - -static void id128_to_efi_guid(sd_id128_t id, void *guid) { - struct guid uuid = { - .u1 = id.bytes[0] << 24 | id.bytes[1] << 16 | id.bytes[2] << 8 | id.bytes[3], - .u2 = id.bytes[4] << 8 | id.bytes[5], - .u3 = id.bytes[6] << 8 | id.bytes[7], - }; - memcpy(uuid.u4, id.bytes+8, sizeof(uuid.u4)); - memcpy(guid, &uuid, sizeof(uuid)); -} - -static uint16_t *tilt_slashes(uint16_t *s) { - for (uint16_t *p = s; *p; p++) - if (*p == '/') - *p = '\\'; - - return s; -} - -int efi_add_boot_option( - uint16_t id, - const char *title, - uint32_t part, - uint64_t pstart, - uint64_t psize, - sd_id128_t part_uuid, - const char *path) { - - size_t size, title_len, path_len; - _cleanup_free_ char *buf = NULL; - struct boot_option *option; - struct device_path *devicep; - char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1]; - - if (!is_efi_boot()) - return -EOPNOTSUPP; - - title_len = (strlen(title)+1) * 2; - path_len = (strlen(path)+1) * 2; - - buf = malloc0(offsetof(struct boot_option, title) + title_len + - sizeof(struct drive_path) + - sizeof(struct device_path) + path_len); - if (!buf) - return -ENOMEM; - - /* header */ - option = (struct boot_option *)buf; - option->attr = LOAD_OPTION_ACTIVE; - option->path_len = offsetof(struct device_path, drive) + sizeof(struct drive_path) + - offsetof(struct device_path, path) + path_len + - offsetof(struct device_path, path); - to_utf16(option->title, title); - size = offsetof(struct boot_option, title) + title_len; - - /* partition info */ - devicep = (struct device_path *)(buf + size); - devicep->type = MEDIA_DEVICE_PATH; - devicep->sub_type = MEDIA_HARDDRIVE_DP; - devicep->length = offsetof(struct device_path, drive) + sizeof(struct drive_path); - memcpy(&devicep->drive.part_nr, &part, sizeof(uint32_t)); - memcpy(&devicep->drive.part_start, &pstart, sizeof(uint64_t)); - memcpy(&devicep->drive.part_size, &psize, sizeof(uint64_t)); - id128_to_efi_guid(part_uuid, devicep->drive.signature); - devicep->drive.mbr_type = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; - devicep->drive.signature_type = SIGNATURE_TYPE_GUID; - size += devicep->length; - - /* path to loader */ - devicep = (struct device_path *)(buf + size); - devicep->type = MEDIA_DEVICE_PATH; - devicep->sub_type = MEDIA_FILEPATH_DP; - devicep->length = offsetof(struct device_path, path) + path_len; - to_utf16(devicep->path, path); - tilt_slashes(devicep->path); - size += devicep->length; - - /* end of path */ - devicep = (struct device_path *)(buf + size); - devicep->type = END_DEVICE_PATH_TYPE; - devicep->sub_type = END_ENTIRE_DEVICE_PATH_SUBTYPE; - devicep->length = offsetof(struct device_path, path); - size += devicep->length; - - xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id); - return efi_set_variable(variable, buf, size); -} - -int efi_remove_boot_option(uint16_t id) { - char variable[STRLEN(EFI_GLOBAL_VARIABLE_STR("Boot")) + 4 + 1]; - - if (!is_efi_boot()) - return -EOPNOTSUPP; - - xsprintf(variable, EFI_GLOBAL_VARIABLE_STR("Boot%04X"), id); - return efi_set_variable(variable, NULL, 0); -} - -int efi_get_boot_order(uint16_t **order) { - _cleanup_free_ void *buf = NULL; - size_t l; - int r; - - if (!is_efi_boot()) - return -EOPNOTSUPP; - - r = efi_get_variable(EFI_GLOBAL_VARIABLE(BootOrder), NULL, &buf, &l); - if (r < 0) - return r; - - if (l <= 0) - return -ENOENT; - - if (l % sizeof(uint16_t) > 0 || - l / sizeof(uint16_t) > INT_MAX) - return -EINVAL; - - *order = TAKE_PTR(buf); - return (int) (l / sizeof(uint16_t)); -} - -int efi_set_boot_order(uint16_t *order, size_t n) { - - if (!is_efi_boot()) - return -EOPNOTSUPP; - - return efi_set_variable(EFI_GLOBAL_VARIABLE(BootOrder), order, n * sizeof(uint16_t)); -} - -static int boot_id_hex(const char s[static 4]) { - int id = 0; - - assert(s); - - for (int i = 0; i < 4; i++) - if (s[i] >= '0' && s[i] <= '9') - id |= (s[i] - '0') << (3 - i) * 4; - else if (s[i] >= 'A' && s[i] <= 'F') - id |= (s[i] - 'A' + 10) << (3 - i) * 4; - else - return -EINVAL; - - return id; -} - -static int cmp_uint16(const uint16_t *a, const uint16_t *b) { - return CMP(*a, *b); -} - -int efi_get_boot_options(uint16_t **options) { - _cleanup_closedir_ DIR *dir = NULL; - _cleanup_free_ uint16_t *list = NULL; - int count = 0; - - assert(options); - - if (!is_efi_boot()) - return -EOPNOTSUPP; - - dir = opendir(EFIVAR_PATH(".")); - if (!dir) - return -errno; - - FOREACH_DIRENT(de, dir, return -errno) { - int id; - - if (strncmp(de->d_name, "Boot", 4) != 0) - continue; - - if (strlen(de->d_name) != 45) - continue; - - if (strcmp(de->d_name + 8, EFI_GLOBAL_VARIABLE_STR("")) != 0) /* generate variable suffix using macro */ - continue; - - id = boot_id_hex(de->d_name + 4); - if (id < 0) - continue; - - if (!GREEDY_REALLOC(list, count + 1)) - return -ENOMEM; - - list[count++] = id; - } - - typesafe_qsort(list, count, cmp_uint16); - - *options = TAKE_PTR(list); - - return count; -} - -static int read_usec(const char *variable, usec_t *u) { +static int read_usec(const char *variable, usec_t *ret) { _cleanup_free_ char *j = NULL; - int r; uint64_t x = 0; + int r; assert(variable); - assert(u); + assert(ret); r = efi_get_variable_string(variable, &j); if (r < 0) @@ -541,16 +26,16 @@ static int read_usec(const char *variable, usec_t *u) { if (r < 0) return r; - *u = x; + *ret = x; return 0; } -int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) { +int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) { uint64_t x, y; int r; - assert(firmware); - assert(loader); + assert(ret_firmware); + assert(ret_loader); if (!is_efi_boot()) return -EOPNOTSUPP; @@ -568,13 +53,12 @@ int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader) { "Bad LoaderTimeInitUSec=%"PRIu64", LoaderTimeExecUSec=%" PRIu64"; refusing.", x, y); - *firmware = x; - *loader = y; - + *ret_firmware = x; + *ret_loader = y; return 0; } -int efi_loader_get_device_part_uuid(sd_id128_t *u) { +int efi_loader_get_device_part_uuid(sd_id128_t *ret) { _cleanup_free_ char *p = NULL; int r, parsed[16]; @@ -592,9 +76,9 @@ int efi_loader_get_device_part_uuid(sd_id128_t *u) { &parsed[12], &parsed[13], &parsed[14], &parsed[15]) != 16) return -EIO; - if (u) + if (ret) for (unsigned i = 0; i < ELEMENTSOF(parsed); i++) - u->bytes[i] = parsed[i]; + ret->bytes[i] = parsed[i]; return 0; } @@ -657,6 +141,8 @@ int efi_loader_get_features(uint64_t *ret) { size_t s; int r; + assert(ret); + if (!is_efi_boot()) { *ret = 0; return 0; @@ -763,29 +249,6 @@ int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat return 0; } -bool efi_has_tpm2(void) { - static int cache = -1; - - /* Returns whether the system has a TPM2 chip which is known to the EFI firmware. */ - - if (cache < 0) { - - /* First, check if we are on an EFI boot at all. */ - if (!is_efi_boot()) - cache = false; - else { - /* Then, check if the ACPI table "TPM2" exists, which is the TPM2 event log table, see: - * https://trustedcomputinggroup.org/wp-content/uploads/TCG_ACPIGeneralSpecification_v1.20_r8.pdf - * This table exists whenever the firmware is hooked up to TPM2. */ - cache = access("/sys/firmware/acpi/tables/TPM2", F_OK) >= 0; - if (!cache && errno != ENOENT) - log_debug_errno(errno, "Unable to test whether /sys/firmware/acpi/tables/TPM2 exists, assuming it doesn't: %m"); - } - } - - return cache; -} - #endif bool efi_loader_entry_name_valid(const char *s) { @@ -794,11 +257,3 @@ bool efi_loader_entry_name_valid(const char *s) { return in_charset(s, ALPHANUMERICAL "+-_."); } - -char *efi_tilt_backslashes(char *s) { - for (char *p = s; *p; p++) - if (*p == '\\') - *p = '/'; - - return s; -} diff --git a/src/shared/efi-loader.h b/src/shared/efi-loader.h index 7a100e536..507160e50 100644 --- a/src/shared/efi-loader.h +++ b/src/shared/efi-loader.h @@ -6,21 +6,12 @@ #include "efivars-fundamental.h" #include "efivars.h" +/* Various calls that interface with EFI variables implementing https://systemd.io/BOOT_LOADER_INTERFACE */ + #if ENABLE_EFI -int efi_reboot_to_firmware_supported(void); -int efi_get_reboot_to_firmware(void); -int efi_set_reboot_to_firmware(bool value); - -int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active); -int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path); -int efi_remove_boot_option(uint16_t id); -int efi_get_boot_order(uint16_t **order); -int efi_set_boot_order(uint16_t *order, size_t n); -int efi_get_boot_options(uint16_t **options); - -int efi_loader_get_device_part_uuid(sd_id128_t *u); -int efi_loader_get_boot_usec(usec_t *firmware, usec_t *loader); +int efi_loader_get_device_part_uuid(sd_id128_t *ret); +int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader); int efi_loader_get_entries(char ***ret); @@ -29,46 +20,8 @@ int efi_loader_get_features(uint64_t *ret); int efi_loader_get_config_timeout_one_shot(usec_t *ret); int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat); -bool efi_has_tpm2(void); - #else -static inline int efi_reboot_to_firmware_supported(void) { - return -EOPNOTSUPP; -} - -static inline int efi_get_reboot_to_firmware(void) { - return -EOPNOTSUPP; -} - -static inline int efi_set_reboot_to_firmware(bool value) { - return -EOPNOTSUPP; -} - -static inline int efi_get_boot_option(uint16_t nr, char **title, sd_id128_t *part_uuid, char **path, bool *active) { - return -EOPNOTSUPP; -} - -static inline int efi_add_boot_option(uint16_t id, const char *title, uint32_t part, uint64_t pstart, uint64_t psize, sd_id128_t part_uuid, const char *path) { - return -EOPNOTSUPP; -} - -static inline int efi_remove_boot_option(uint16_t id) { - return -EOPNOTSUPP; -} - -static inline int efi_get_boot_order(uint16_t **order) { - return -EOPNOTSUPP; -} - -static inline int efi_set_boot_order(uint16_t *order, size_t n) { - return -EOPNOTSUPP; -} - -static inline int efi_get_boot_options(uint16_t **options) { - return -EOPNOTSUPP; -} - static inline int efi_loader_get_device_part_uuid(sd_id128_t *u) { return -EOPNOTSUPP; } @@ -93,12 +46,6 @@ static inline int efi_loader_update_entry_one_shot_cache(char **cache, struct st return -EOPNOTSUPP; } -static inline bool efi_has_tpm2(void) { - return false; -} - #endif bool efi_loader_entry_name_valid(const char *s); - -char *efi_tilt_backslashes(char *s); diff --git a/src/shared/elf-util.c b/src/shared/elf-util.c index 6d9fcfbbf..cc0fce56d 100644 --- a/src/shared/elf-util.c +++ b/src/shared/elf-util.c @@ -178,8 +178,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(Elf *, sym_elf_end, NULL); static int frame_callback(Dwfl_Frame *frame, void *userdata) { StackContext *c = userdata; - Dwarf_Addr pc, pc_adjusted, bias = 0; - _cleanup_free_ Dwarf_Die *scopes = NULL; + Dwarf_Addr pc, pc_adjusted; const char *fname = NULL, *symbol = NULL; Dwfl_Module *module; bool is_activation; @@ -198,29 +197,32 @@ static int frame_callback(Dwfl_Frame *frame, void *userdata) { module = sym_dwfl_addrmodule(c->dwfl, pc_adjusted); if (module) { - Dwarf_Die *s, *cudie; - int n; - Dwarf_Addr start; + Dwarf_Addr start, bias = 0; + Dwarf_Die *cudie; cudie = sym_dwfl_module_addrdie(module, pc_adjusted, &bias); if (cudie) { + _cleanup_free_ Dwarf_Die *scopes = NULL; + int n; + n = sym_dwarf_getscopes(cudie, pc_adjusted - bias, &scopes); if (n > 0) - for (s = scopes; s && s < scopes + n; s++) { - if (IN_SET(sym_dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) { - Dwarf_Attribute *a, space; + for (Dwarf_Die *s = scopes; s && s < scopes + n; s++) { + Dwarf_Attribute *a, space; - a = sym_dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space); - if (!a) - a = sym_dwarf_attr_integrate(s, DW_AT_linkage_name, &space); - if (a) - symbol = sym_dwarf_formstring(a); - if (!symbol) - symbol = sym_dwarf_diename(s); + if (!IN_SET(sym_dwarf_tag(s), DW_TAG_subprogram, DW_TAG_inlined_subroutine, DW_TAG_entry_point)) + continue; - if (symbol) - break; - } + a = sym_dwarf_attr_integrate(s, DW_AT_MIPS_linkage_name, &space); + if (!a) + a = sym_dwarf_attr_integrate(s, DW_AT_linkage_name, &space); + if (a) + symbol = sym_dwarf_formstring(a); + if (!symbol) + symbol = sym_dwarf_diename(s); + + if (symbol) + break; } } @@ -286,7 +288,6 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e /* Iterate over all program headers in that ELF object. These will have been copied by * the kernel verbatim when the core file is generated. */ for (size_t i = 0; i < n_program_headers; ++i) { - size_t note_offset = 0, name_offset, desc_offset; GElf_Phdr mem, *program_header; GElf_Nhdr note_header; Elf_Data *data; @@ -311,8 +312,11 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e if (!data) continue; - while (note_offset < data->d_size && - (note_offset = sym_gelf_getnote(data, note_offset, ¬e_header, &name_offset, &desc_offset)) > 0) { + for (size_t note_offset = 0, name_offset, desc_offset; + note_offset < data->d_size && + (note_offset = sym_gelf_getnote(data, note_offset, ¬e_header, &name_offset, &desc_offset)) > 0;) { + + _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL; const char *note_name = (const char *)data->d_buf + name_offset; const char *payload = (const char *)data->d_buf + desc_offset; @@ -321,50 +325,50 @@ static int parse_package_metadata(const char *name, JsonVariant *id_json, Elf *e /* Package metadata might have different owners, but the * magic ID is always the same. */ - if (note_header.n_type == ELF_PACKAGE_METADATA_ID) { - _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *w = NULL; + if (note_header.n_type != ELF_PACKAGE_METADATA_ID) + continue; - r = json_parse(payload, 0, &v, NULL, NULL); - if (r < 0) - return log_error_errno(r, "json_parse on %s failed: %m", payload); + r = json_parse(payload, 0, &v, NULL, NULL); + if (r < 0) + return log_error_errno(r, "json_parse on %s failed: %m", payload); - /* First pretty-print to the buffer, so that the metadata goes as - * plaintext in the journal. */ - if (c->f) { - fprintf(c->f, "Metadata for module %s owned by %s found: ", - name, note_name); - json_variant_dump(v, JSON_FORMAT_NEWLINE|JSON_FORMAT_PRETTY, c->f, NULL); - fputc('\n', c->f); - } + /* First pretty-print to the buffer, so that the metadata goes as + * plaintext in the journal. */ + if (c->f) { + fprintf(c->f, "Metadata for module %s owned by %s found: ", + name, note_name); + json_variant_dump(v, JSON_FORMAT_NEWLINE|JSON_FORMAT_PRETTY, c->f, NULL); + fputc('\n', c->f); + } - /* Secondly, if we have a build-id, merge it in the same JSON object - * so that it appears all nicely together in the logs/metadata. */ - if (id_json) { - r = json_variant_merge(&v, id_json); - if (r < 0) - return log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m"); - } - - /* Then we build a new object using the module name as the key, and merge it - * with the previous parses, so that in the end it all fits together in a single - * JSON blob. */ - r = json_build(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(name, JSON_BUILD_VARIANT(v)))); - if (r < 0) - return log_error_errno(r, "Failed to build JSON object: %m"); - r = json_variant_merge(c->package_metadata, w); + /* Secondly, if we have a build-id, merge it in the same JSON object + * so that it appears all nicely together in the logs/metadata. */ + if (id_json) { + r = json_variant_merge(&v, id_json); if (r < 0) return log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m"); - - /* Finally stash the name, so we avoid double visits. */ - r = set_put_strdup(c->modules, name); - if (r < 0) - return log_error_errno(r, "set_put_strdup failed: %m"); - - if (ret_interpreter_found) - *ret_interpreter_found = interpreter_found; - - return 1; } + + /* Then we build a new object using the module name as the key, and merge it + * with the previous parses, so that in the end it all fits together in a single + * JSON blob. */ + r = json_build(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(name, JSON_BUILD_VARIANT(v)))); + if (r < 0) + return log_error_errno(r, "Failed to build JSON object: %m"); + + r = json_variant_merge(c->package_metadata, w); + if (r < 0) + return log_error_errno(r, "json_variant_merge of package meta with buildid failed: %m"); + + /* Finally stash the name, so we avoid double visits. */ + r = set_put_strdup(c->modules, name); + if (r < 0) + return log_error_errno(r, "set_put_strdup failed: %m"); + + if (ret_interpreter_found) + *ret_interpreter_found = interpreter_found; + + return 1; } } @@ -573,7 +577,7 @@ static int parse_elf(int fd, const char *executable, char **ret, JsonVariant **r .package_metadata = &package_metadata, .modules = &modules, }; - const char *elf_architecture = NULL, *elf_type; + const char *elf_type; GElf_Ehdr elf_header; size_t sz = 0; int r; @@ -642,8 +646,7 @@ static int parse_elf(int fd, const char *executable, char **ret, JsonVariant **r return log_warning_errno(r, "Failed to build JSON object: %m"); #if HAVE_DWELF_ELF_E_MACHINE_STRING - elf_architecture = sym_dwelf_elf_e_machine_string(elf_header.e_machine); -#endif + const char *elf_architecture = sym_dwelf_elf_e_machine_string(elf_header.e_machine); if (elf_architecture) { _cleanup_(json_variant_unrefp) JsonVariant *json_architecture = NULL; @@ -659,6 +662,7 @@ static int parse_elf(int fd, const char *executable, char **ret, JsonVariant **r if (ret) fprintf(c.f, "ELF object binary architecture: %s\n", elf_architecture); } +#endif /* We always at least have the ELF type, so merge that (and possibly the arch). */ r = json_variant_merge(&elf_metadata, package_metadata); diff --git a/src/shared/ethtool-util.c b/src/shared/ethtool-util.c index 489a29fa2..6c9aeb1c6 100644 --- a/src/shared/ethtool-util.c +++ b/src/shared/ethtool-util.c @@ -74,6 +74,15 @@ static const char* const port_table[] = { DEFINE_STRING_TABLE_LOOKUP(port, NetDevPort); DEFINE_CONFIG_PARSE_ENUM(config_parse_port, port, NetDevPort, "Failed to parse Port setting"); +static const char* const mdi_table[] = { + [ETH_TP_MDI_INVALID] = "unknown", + [ETH_TP_MDI] = "mdi", + [ETH_TP_MDI_X] = "mdi-x", + [ETH_TP_MDI_AUTO] = "auto", +}; + +DEFINE_STRING_TABLE_LOOKUP_TO_STRING(mdi, int); + static const char* const netdev_feature_table[_NET_DEV_FEAT_MAX] = { [NET_DEV_FEAT_SG] = "tx-scatter-gather", [NET_DEV_FEAT_IP_CSUM] = "tx-checksum-ipv4", @@ -835,6 +844,8 @@ static int get_gset(int fd, struct ifreq *ifr, struct ethtool_link_usettings **r .base.phy_address = ecmd.phy_address, .base.autoneg = ecmd.autoneg, .base.mdio_support = ecmd.mdio_support, + .base.eth_tp_mdix = ecmd.eth_tp_mdix, + .base.eth_tp_mdix_ctrl = ecmd.eth_tp_mdix_ctrl, .link_modes.supported[0] = ecmd.supported, .link_modes.advertising[0] = ecmd.advertising, @@ -914,7 +925,8 @@ int ethtool_set_glinksettings( const uint32_t advertise[static N_ADVERTISE], uint64_t speed, Duplex duplex, - NetDevPort port) { + NetDevPort port, + uint8_t mdi) { _cleanup_free_ struct ethtool_link_usettings *u = NULL; struct ifreq ifr = {}; @@ -926,7 +938,7 @@ int ethtool_set_glinksettings( assert(advertise); if (autonegotiation < 0 && memeqzero(advertise, sizeof(uint32_t) * N_ADVERTISE) && - speed == 0 && duplex < 0 && port < 0) + speed == 0 && duplex < 0 && port < 0 && mdi == ETH_TP_MDI_INVALID) return 0; /* If autonegotiation is disabled, the speed and duplex represent the fixed link mode and are @@ -957,7 +969,7 @@ int ethtool_set_glinksettings( if (r < 0) { r = get_gset(*fd, &ifr, &u); if (r < 0) - return log_debug_errno(r, "ethtool: Cannot get device settings for %s : %m", ifname); + return log_debug_errno(r, "ethtool: Cannot get device settings for %s: %m", ifname); } if (speed > 0) @@ -984,6 +996,13 @@ int ethtool_set_glinksettings( ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES - sizeof(uint32_t) * N_ADVERTISE); } + if (mdi != ETH_TP_MDI_INVALID) { + if (u->base.eth_tp_mdix_ctrl == ETH_TP_MDI_INVALID) + log_debug("ethtool: setting MDI not supported for %s, ignoring.", ifname); + else + UPDATE(u->base.eth_tp_mdix_ctrl, mdi, changed); + } + if (!changed) return 0; @@ -1261,6 +1280,48 @@ int config_parse_advertise( } } +int config_parse_mdi( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint8_t *mdi = ASSERT_PTR(data); + + assert(filename); + assert(rvalue); + + if (isempty(rvalue)) { + *mdi = ETH_TP_MDI_INVALID; + return 0; + } + + if (STR_IN_SET(rvalue, "mdi", "straight")) { + *mdi = ETH_TP_MDI; + return 0; + } + + if (STR_IN_SET(rvalue, "mdi-x", "mdix", "crossover")) { + *mdi = ETH_TP_MDI_X; + return 0; + } + + if (streq(rvalue, "auto")) { + *mdi = ETH_TP_MDI_AUTO; + return 0; + } + + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Failed to parse %s= setting, ignoring assignment: %s", lvalue, rvalue); + return 0; +} + int config_parse_ring_buffer_or_channel( const char *unit, const char *filename, diff --git a/src/shared/ethtool-util.h b/src/shared/ethtool-util.h index 8ca221bc0..d07cfaefb 100644 --- a/src/shared/ethtool-util.h +++ b/src/shared/ethtool-util.h @@ -168,9 +168,15 @@ int ethtool_get_permanent_hw_addr(int *ethtool_fd, const char *ifname, struct hw int ethtool_set_wol(int *ethtool_fd, const char *ifname, uint32_t wolopts, const uint8_t password[SOPASS_MAX]); int ethtool_set_nic_buffer_size(int *ethtool_fd, const char *ifname, const netdev_ring_param *ring); int ethtool_set_features(int *ethtool_fd, const char *ifname, const int features[static _NET_DEV_FEAT_MAX]); -int ethtool_set_glinksettings(int *ethtool_fd, const char *ifname, - int autonegotiation, const uint32_t advertise[static N_ADVERTISE], - uint64_t speed, Duplex duplex, NetDevPort port); +int ethtool_set_glinksettings( + int *fd, + const char *ifname, + int autonegotiation, + const uint32_t advertise[static N_ADVERTISE], + uint64_t speed, + Duplex duplex, + NetDevPort port, + uint8_t mdi); int ethtool_set_channels(int *ethtool_fd, const char *ifname, const netdev_channels *channels); int ethtool_set_flow_control(int *fd, const char *ifname, int rx, int tx, int autoneg); int ethtool_set_nic_coalesce_settings(int *ethtool_fd, const char *ifname, const netdev_coalesce_param *coalesce); @@ -183,12 +189,15 @@ int wol_options_to_string_alloc(uint32_t opts, char **ret); const char *port_to_string(NetDevPort port) _const_; NetDevPort port_from_string(const char *port) _pure_; +const char *mdi_to_string(int mdi) _const_; + const char *ethtool_link_mode_bit_to_string(enum ethtool_link_mode_bit_indices val) _const_; enum ethtool_link_mode_bit_indices ethtool_link_mode_bit_from_string(const char *str) _pure_; CONFIG_PARSER_PROTOTYPE(config_parse_duplex); CONFIG_PARSER_PROTOTYPE(config_parse_wol); CONFIG_PARSER_PROTOTYPE(config_parse_port); +CONFIG_PARSER_PROTOTYPE(config_parse_mdi); CONFIG_PARSER_PROTOTYPE(config_parse_advertise); CONFIG_PARSER_PROTOTYPE(config_parse_ring_buffer_or_channel); CONFIG_PARSER_PROTOTYPE(config_parse_coalesce_u32); diff --git a/src/shared/exec-util.c b/src/shared/exec-util.c index b93de9c92..d1f50249d 100644 --- a/src/shared/exec-util.c +++ b/src/shared/exec-util.c @@ -91,7 +91,6 @@ static int do_execute( _cleanup_hashmap_free_free_ Hashmap *pids = NULL; _cleanup_strv_free_ char **paths = NULL; - char **path, **e; int r; bool parallel_execution; @@ -254,7 +253,7 @@ int execute_directories( } static int gather_environment_generate(int fd, void *arg) { - char ***env = arg, **x, **y; + char ***env = arg; _cleanup_fclose_ FILE *f = NULL; _cleanup_strv_free_ char **new = NULL; int r; @@ -369,7 +368,6 @@ static int gather_environment_consume(int fd, void *arg) { int exec_command_flags_from_strv(char **ex_opts, ExecCommandFlags *flags) { ExecCommandFlags ex_flag, ret_flags = 0; - char **opt; assert(flags); @@ -449,7 +447,16 @@ ExecCommandFlags exec_command_flags_from_string(const char *s) { } int fexecve_or_execve(int executable_fd, const char *executable, char *const argv[], char *const envp[]) { + /* Refuse invalid fds, regardless if fexecve() use is enabled or not */ + if (executable_fd < 0) + return -EBADF; + + /* Block any attempts on exploiting Linux' liberal argv[] handling, i.e. CVE-2021-4034 and suchlike */ + if (isempty(executable) || strv_isempty(argv)) + return -EINVAL; + #if ENABLE_FEXECVE + execveat(executable_fd, "", argv, envp, AT_EMPTY_PATH); if (IN_SET(errno, ENOSYS, ENOENT) || ERRNO_IS_PRIVILEGE(errno)) diff --git a/src/shared/find-esp.c b/src/shared/find-esp.c new file mode 100644 index 000000000..fca24329d --- /dev/null +++ b/src/shared/find-esp.c @@ -0,0 +1,710 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "sd-device.h" + +#include "alloc-util.h" +#include "blkid-util.h" +#include "env-util.h" +#include "errno-util.h" +#include "find-esp.h" +#include "gpt.h" +#include "id128-util.h" +#include "parse-util.h" +#include "path-util.h" +#include "stat-util.h" +#include "string-util.h" +#include "virt.h" + +static int verify_esp_blkid( + dev_t devid, + bool searching, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid) { + + sd_id128_t uuid = SD_ID128_NULL; + uint64_t pstart = 0, psize = 0; + uint32_t part = 0; + +#if HAVE_BLKID + _cleanup_(blkid_free_probep) blkid_probe b = NULL; + _cleanup_free_ char *node = NULL; + const char *v; + int r; + + r = device_path_make_major_minor(S_IFBLK, devid, &node); + if (r < 0) + return log_error_errno(r, "Failed to format major/minor device path: %m"); + + errno = 0; + b = blkid_new_probe_from_filename(node); + if (!b) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node); + + blkid_probe_enable_superblocks(b, 1); + blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node); + else if (r == 1) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node); + else if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node); + + r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); + if (r != 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "No filesystem found on \"%s\": %m", node); + if (!streq(v, "vfat")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not FAT.", node); + + r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); + if (r != 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not located on a partitioned block device.", node); + if (!streq(v, "gpt")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not on a GPT partition table.", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node); + if (id128_equal_string(v, GPT_ESP) <= 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": %m", node); + r = safe_atou32(v, &part); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node); + r = safe_atou64(v, &pstart); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node); + r = safe_atou64(v, &psize); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); +#endif + + if (ret_part) + *ret_part = part; + if (ret_pstart) + *ret_pstart = pstart; + if (ret_psize) + *ret_psize = psize; + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + +static int verify_esp_udev( + dev_t devid, + bool searching, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid) { + + _cleanup_(sd_device_unrefp) sd_device *d = NULL; + _cleanup_free_ char *node = NULL; + sd_id128_t uuid = SD_ID128_NULL; + uint64_t pstart = 0, psize = 0; + uint32_t part = 0; + const char *v; + int r; + + r = device_path_make_major_minor(S_IFBLK, devid, &node); + if (r < 0) + return log_error_errno(r, "Failed to format major/minor device path: %m"); + + r = sd_device_new_from_devnum(&d, 'b', devid); + if (r < 0) + return log_error_errno(r, "Failed to get device from device number: %m"); + + r = sd_device_get_property_value(d, "ID_FS_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "vfat")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not FAT.", node ); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "gpt")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not on a GPT partition table.", node); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (id128_equal_string(v, GPT_ESP) <= 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = safe_atou32(v, &part); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = safe_atou64(v, &pstart); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = safe_atou64(v, &psize); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); + + if (ret_part) + *ret_part = part; + if (ret_pstart) + *ret_pstart = pstart; + if (ret_psize) + *ret_psize = psize; + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + +static int verify_fsroot_dir( + const char *path, + bool searching, + bool unprivileged_mode, + dev_t *ret_dev) { + + struct stat st, st2; + const char *t2, *trigger; + int r; + + assert(path); + assert(ret_dev); + + /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the + * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here, + * before stat()ing */ + trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */ + (void) access(trigger, F_OK); + + if (stat(path, &st) < 0) + return log_full_errno((searching && errno == ENOENT) || + (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, + "Failed to determine block device node of \"%s\": %m", path); + + if (major(st.st_dev) == 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Block device node of \"%s\" is invalid.", path); + + if (path_equal(path, "/")) { + /* Let's assume that the root directory of the OS is always the root of its file system + * (which technically doesn't have to be the case, but it's close enough, and it's not easy + * to be fully correct for it, since we can't look further up than the root dir easily.) */ + if (ret_dev) + *ret_dev = st.st_dev; + + return 0; + } + + t2 = strjoina(path, "/.."); + if (stat(t2, &st2) < 0) { + if (errno != EACCES) + r = -errno; + else { + _cleanup_free_ char *parent = NULL; + + /* If going via ".." didn't work due to EACCESS, then let's determine the parent path + * directly instead. It's not as good, due to symlinks and such, but we can't do + * anything better here. */ + + parent = dirname_malloc(path); + if (!parent) + return log_oom(); + + r = RET_NERRNO(stat(parent, &st2)); + } + + if (r < 0) + return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r, + "Failed to determine block device node of parent of \"%s\": %m", path); + } + + if (st.st_dev == st2.st_dev) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Directory \"%s\" is not the root of the file system.", path); + + if (ret_dev) + *ret_dev = st.st_dev; + + return 0; +} + +static int verify_esp( + const char *p, + bool searching, + bool unprivileged_mode, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid, + dev_t *ret_devid) { + + bool relax_checks; + dev_t devid; + int r; + + assert(p); + + /* This logs about all errors, except: + * + * -ENOENT → if 'searching' is set, and the dir doesn't exist + * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP + * -EACESS → if 'unprivileged_mode' is set, and we have trouble accessing the thing + */ + + relax_checks = getenv_bool("SYSTEMD_RELAX_ESP_CHECKS") > 0; + + /* Non-root user can only check the status, so if an error occurred in the following, it does not cause any + * issues. Let's also, silence the error messages. */ + + if (!relax_checks) { + struct statfs sfs; + + if (statfs(p, &sfs) < 0) + /* If we are searching for the mount point, don't generate a log message if we can't find the path */ + return log_full_errno((searching && errno == ENOENT) || + (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, + "Failed to check file system type of \"%s\": %m", p); + + if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); + } + + r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); + if (r < 0) + return r; + + /* In a container we don't have access to block devices, skip this part of the verification, we trust + * the container manager set everything up correctly on its own. */ + if (detect_container() > 0 || relax_checks) + goto finish; + + /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we + * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an + * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell), + * however blkid can't work if we have no privileges to access block devices directly, which is why + * we use udev in that case. */ + if (unprivileged_mode) + r = verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); + else + r = verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); + if (r < 0) + return r; + + if (ret_devid) + *ret_devid = devid; + + return 0; + +finish: + if (ret_part) + *ret_part = 0; + if (ret_pstart) + *ret_pstart = 0; + if (ret_psize) + *ret_psize = 0; + if (ret_uuid) + *ret_uuid = SD_ID128_NULL; + if (ret_devid) + *ret_devid = 0; + + return 0; +} + +int find_esp_and_warn( + const char *path, + bool unprivileged_mode, + char **ret_path, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid, + dev_t *ret_devid) { + + int r; + + /* This logs about all errors except: + * + * -ENOKEY → when we can't find the partition + * -EACCESS → when unprivileged_mode is true, and we can't access something + */ + + if (path) { + r = verify_esp(path, /* searching= */ false, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid); + if (r < 0) + return r; + + goto found; + } + + path = getenv("SYSTEMD_ESP_PATH"); + if (path) { + struct stat st; + + if (!path_is_valid(path) || !path_is_absolute(path)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "$SYSTEMD_ESP_PATH does not refer to absolute path, refusing to use it: %s", + path); + + /* Note: when the user explicitly configured things with an env var we won't validate the + * path beyond checking it refers to a directory. After all we want this to be useful for + * testing. */ + + if (stat(path, &st) < 0) + return log_error_errno(errno, "Failed to stat '%s': %m", path); + if (!S_ISDIR(st.st_mode)) + return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "ESP path '%s' is not a directory.", path); + + if (ret_part) + *ret_part = 0; + if (ret_pstart) + *ret_pstart = 0; + if (ret_psize) + *ret_psize = 0; + if (ret_uuid) + *ret_uuid = SD_ID128_NULL; + if (ret_devid) + *ret_devid = st.st_dev; + + goto found; + } + + FOREACH_STRING(_path, "/efi", "/boot", "/boot/efi") { + path = _path; + + r = verify_esp(path, /* searching= */ true, unprivileged_mode, ret_part, ret_pstart, ret_psize, ret_uuid, ret_devid); + if (r >= 0) + goto found; + if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */ + return r; + } + + /* No logging here */ + return -ENOKEY; + +found: + if (ret_path) { + char *c; + + c = strdup(path); + if (!c) + return log_oom(); + + *ret_path = c; + } + + return 0; +} + +static int verify_xbootldr_blkid( + dev_t devid, + bool searching, + sd_id128_t *ret_uuid) { + + sd_id128_t uuid = SD_ID128_NULL; + +#if HAVE_BLKID + _cleanup_(blkid_free_probep) blkid_probe b = NULL; + _cleanup_free_ char *node = NULL; + const char *v; + int r; + + r = device_path_make_major_minor(S_IFBLK, devid, &node); + if (r < 0) + return log_error_errno(r, "Failed to format major/minor device path: %m"); + errno = 0; + b = blkid_new_probe_from_filename(node); + if (!b) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node); + + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node); + else if (r == 1) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node); + else if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node); + if (streq(v, "gpt")) { + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node); + if (id128_equal_string(v, GPT_XBOOTLDR) <= 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + } else if (streq(v, "dos")) { + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node); + if (!streq(v, "0xea")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + + } else + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" is not on a GPT or DOS partition table.", node); +#endif + + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + +static int verify_xbootldr_udev( + dev_t devid, + bool searching, + sd_id128_t *ret_uuid) { + + _cleanup_(sd_device_unrefp) sd_device *d = NULL; + _cleanup_free_ char *node = NULL; + sd_id128_t uuid = SD_ID128_NULL; + const char *v; + int r; + + r = device_path_make_major_minor(S_IFBLK, devid, &node); + if (r < 0) + return log_error_errno(r, "Failed to format major/minor device path: %m"); + + r = sd_device_new_from_devnum(&d, 'b', devid); + if (r < 0) + return log_error_errno(r, "Failed to get device from device number: %m"); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + + if (streq(v, "gpt")) { + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (id128_equal_string(v, GPT_XBOOTLDR)) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + } else if (streq(v, "dos")) { + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "0xea")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + } else + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" is not on a GPT or DOS partition table.", node); + + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + +static int verify_xbootldr( + const char *p, + bool searching, + bool unprivileged_mode, + sd_id128_t *ret_uuid, + dev_t *ret_devid) { + + bool relax_checks; + dev_t devid; + int r; + + assert(p); + + relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0; + + r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); + if (r < 0) + return r; + + if (detect_container() > 0 || relax_checks) + goto finish; + + if (unprivileged_mode) + r = verify_xbootldr_udev(devid, searching, ret_uuid); + else + r = verify_xbootldr_blkid(devid, searching, ret_uuid); + if (r < 0) + return r; + + if (ret_devid) + *ret_devid = devid; + + return 0; + +finish: + if (ret_uuid) + *ret_uuid = SD_ID128_NULL; + if (ret_devid) + *ret_devid = 0; + + return 0; +} + +int find_xbootldr_and_warn( + const char *path, + bool unprivileged_mode, + char **ret_path, + sd_id128_t *ret_uuid, + dev_t *ret_devid) { + + int r; + + /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */ + + if (path) { + r = verify_xbootldr(path, /* searching= */ false, unprivileged_mode, ret_uuid, ret_devid); + if (r < 0) + return r; + + goto found; + } + + path = getenv("SYSTEMD_XBOOTLDR_PATH"); + if (path) { + struct stat st; + + if (!path_is_valid(path) || !path_is_absolute(path)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s", + path); + + if (stat(path, &st) < 0) + return log_error_errno(errno, "Failed to stat '%s': %m", path); + if (!S_ISDIR(st.st_mode)) + return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "XBOOTLDR path '%s' is not a directory.", path); + + if (ret_uuid) + *ret_uuid = SD_ID128_NULL; + if (ret_devid) + *ret_devid = st.st_dev; + + goto found; + } + + r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid, ret_devid); + if (r >= 0) { + path = "/boot"; + goto found; + } + if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */ + return r; + + return -ENOKEY; + +found: + if (ret_path) { + char *c; + + c = strdup(path); + if (!c) + return log_oom(); + + *ret_path = c; + } + + return 0; +} diff --git a/src/shared/find-esp.h b/src/shared/find-esp.h new file mode 100644 index 000000000..e4e65ac3e --- /dev/null +++ b/src/shared/find-esp.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#pragma once + +#include +#include +#include + +#include "sd-id128.h" + +int find_esp_and_warn(const char *path, bool unprivileged_mode, char **ret_path, uint32_t *ret_part, uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid, dev_t *ret_devid); +int find_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path, sd_id128_t *ret_uuid, dev_t *ret_devid); diff --git a/src/shared/format-table.c b/src/shared/format-table.c index b95680b36..d4e66cb6a 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -1364,7 +1364,6 @@ static char* format_strv_width(char **strv, size_t column_width) { return NULL; size_t position = 0; - char **p; STRV_FOREACH(p, strv) { size_t our_len = utf8_console_width(*p); /* This returns -1 on invalid utf-8 (which shouldn't happen). * If that happens, we'll just print one item per line. */ @@ -1699,11 +1698,11 @@ static const char *table_data_format(Table *t, TableData *d, bool avoid_uppercas case TABLE_UUID: { char *p; - p = new(char, ID128_UUID_STRING_MAX); + p = new(char, SD_ID128_UUID_STRING_MAX); if (!p) return NULL; - d->formatted = id128_to_uuid_string(d->id128, p); + d->formatted = sd_id128_to_uuid_string(d->id128, p); break; } @@ -2562,7 +2561,7 @@ static int table_data_to_json(TableData *d, JsonVariant **ret) { return json_variant_new_string(ret, SD_ID128_TO_STRING(d->id128)); case TABLE_UUID: - return json_variant_new_string(ret, ID128_TO_UUID_STRING(d->id128)); + return json_variant_new_string(ret, SD_ID128_TO_UUID_STRING(d->id128)); case TABLE_UID: if (!uid_is_valid(d->uid)) diff --git a/src/shared/generator.c b/src/shared/generator.c index 014b34747..ca66673d8 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -616,10 +616,10 @@ int generator_write_cryptsetup_service_section( FILE *f, const char *name, const char *what, - const char *password, + const char *key_file, const char *options) { - _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *password_escaped = NULL, *options_escaped = NULL; + _cleanup_free_ char *name_escaped = NULL, *what_escaped = NULL, *key_file_escaped = NULL, *options_escaped = NULL; assert(f); assert(name); @@ -633,9 +633,9 @@ int generator_write_cryptsetup_service_section( if (!what_escaped) return log_oom(); - if (password) { - password_escaped = specifier_escape(password); - if (!password_escaped) + if (key_file) { + key_file_escaped = specifier_escape(key_file); + if (!key_file_escaped) return log_oom(); } @@ -655,7 +655,7 @@ int generator_write_cryptsetup_service_section( "OOMScoreAdjust=500\n" /* Unlocking can allocate a lot of memory if Argon2 is used */ "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n" "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", - name_escaped, what_escaped, strempty(password_escaped), strempty(options_escaped), + name_escaped, what_escaped, strempty(key_file_escaped), strempty(options_escaped), name_escaped); return 0; diff --git a/src/shared/gpt.c b/src/shared/gpt.c index c01a57a5f..cd57447b8 100644 --- a/src/shared/gpt.c +++ b/src/shared/gpt.c @@ -75,7 +75,7 @@ const char *gpt_partition_type_uuid_to_string(sd_id128_t id) { const char *gpt_partition_type_uuid_to_string_harder( sd_id128_t id, - char buffer[static ID128_UUID_STRING_MAX]) { + char buffer[static SD_ID128_UUID_STRING_MAX]) { const char *s; @@ -85,7 +85,7 @@ const char *gpt_partition_type_uuid_to_string_harder( if (s) return s; - return id128_to_uuid_string(id, buffer); + return sd_id128_to_uuid_string(id, buffer); } int gpt_partition_type_uuid_from_string(const char *s, sd_id128_t *ret) { diff --git a/src/shared/gpt.h b/src/shared/gpt.h index 00c829ca7..64416bb08 100644 --- a/src/shared/gpt.h +++ b/src/shared/gpt.h @@ -122,15 +122,24 @@ #define GPT_USR_X86_64_VERITY_SIG SD_ID128_MAKE(e7,bb,33,fb,06,cf,4e,81,82,73,e5,43,b4,13,e2,e2) #define GPT_USR_X86_VERITY_SIG SD_ID128_MAKE(97,4a,71,c0,de,41,43,c3,be,5d,5c,5c,cd,1a,d2,c0) -#define GPT_ESP SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b) -#define GPT_XBOOTLDR SD_ID128_MAKE(bc,13,c2,ff,59,e6,42,62,a3,52,b2,75,fd,6f,71,72) -#define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f) -#define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15) -#define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8) -#define GPT_VAR SD_ID128_MAKE(4d,21,b0,16,b5,34,45,c2,a9,fb,5c,16,e0,91,fd,2d) -#define GPT_TMP SD_ID128_MAKE(7e,c6,f5,57,3b,c5,4a,ca,b2,93,16,ef,5d,f6,39,d1) -#define GPT_USER_HOME SD_ID128_MAKE(77,3f,91,ef,66,d4,49,b5,bd,83,d6,83,bf,40,ad,16) -#define GPT_LINUX_GENERIC SD_ID128_MAKE(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4) +#define GPT_ESP SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b) +#define GPT_ESP_STR SD_ID128_MAKE_UUID_STR(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b) +#define GPT_XBOOTLDR SD_ID128_MAKE(bc,13,c2,ff,59,e6,42,62,a3,52,b2,75,fd,6f,71,72) +#define GPT_XBOOTLDR_STR SD_ID128_MAKE_UUID_STR(bc,13,c2,ff,59,e6,42,62,a3,52,b2,75,fd,6f,71,72) +#define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f) +#define GPT_SWAP_STR SD_ID128_MAKE_UUID_STR(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f) +#define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15) +#define GPT_HOME_STR SD_ID128_MAKE_UUID_STR(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15) +#define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8) +#define GPT_SRV_STR SD_ID128_MAKE_UUID_STR(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8) +#define GPT_VAR SD_ID128_MAKE(4d,21,b0,16,b5,34,45,c2,a9,fb,5c,16,e0,91,fd,2d) +#define GPT_VAR_STR SD_ID128_MAKE_UUID_STR(4d,21,b0,16,b5,34,45,c2,a9,fb,5c,16,e0,91,fd,2d) +#define GPT_TMP SD_ID128_MAKE(7e,c6,f5,57,3b,c5,4a,ca,b2,93,16,ef,5d,f6,39,d1) +#define GPT_TMP_STR SD_ID128_MAKE_UUID_STR(7e,c6,f5,57,3b,c5,4a,ca,b2,93,16,ef,5d,f6,39,d1) +#define GPT_USER_HOME SD_ID128_MAKE(77,3f,91,ef,66,d4,49,b5,bd,83,d6,83,bf,40,ad,16) +#define GPT_USER_HOME_STR SD_ID128_MAKE_UUID_STR(77,3f,91,ef,66,d4,49,b5,bd,83,d6,83,bf,40,ad,16) +#define GPT_LINUX_GENERIC SD_ID128_MAKE(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4) +#define GPT_LINUX_GENERIC_STR SD_ID128_MAKE_UUID_STR(0f,c6,3d,af,84,83,47,72,8e,79,3d,69,d8,47,7d,e4) /* Maintain same order as above */ #if defined(__alpha__) @@ -286,7 +295,7 @@ const char *gpt_partition_type_uuid_to_string(sd_id128_t id); const char *gpt_partition_type_uuid_to_string_harder( sd_id128_t id, - char buffer[static ID128_UUID_STRING_MAX]); + char buffer[static SD_ID128_UUID_STRING_MAX]); int gpt_partition_type_uuid_from_string(const char *s, sd_id128_t *ret); Architecture gpt_partition_type_uuid_to_arch(sd_id128_t id); diff --git a/src/shared/hwdb-util.c b/src/shared/hwdb-util.c index fe4785f3e..f98d03f76 100644 --- a/src/shared/hwdb-util.c +++ b/src/shared/hwdb-util.c @@ -434,7 +434,7 @@ static int trie_store(struct trie *trie, const char *filename, bool compat) { static int insert_data(struct trie *trie, char **match_list, char *line, const char *filename, uint16_t file_priority, uint32_t line_number, bool compat) { - char *value, **entry; + char *value; assert(line[0] == ' '); @@ -583,7 +583,6 @@ int hwdb_update(const char *root, const char *hwdb_bin_dir, bool strict, bool co _cleanup_free_ char *hwdb_bin = NULL; _cleanup_(trie_freep) struct trie *trie = NULL; _cleanup_strv_free_ char **files = NULL; - char **f; uint16_t file_priority = 1; int r = 0, err; diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c index 403d6013c..5436aa483 100644 --- a/src/shared/install-printf.c +++ b/src/shared/install-printf.c @@ -14,12 +14,10 @@ #include "user-util.h" static int specifier_prefix_and_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const UnitFileInstallInfo *i = userdata; + const UnitFileInstallInfo *i = ASSERT_PTR(userdata); _cleanup_free_ char *prefix = NULL; int r; - assert(i); - r = unit_name_to_prefix_and_instance(i->name, &prefix); if (r < 0) return r; @@ -38,11 +36,9 @@ static int specifier_prefix_and_instance(char specifier, const void *data, const } static int specifier_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const UnitFileInstallInfo *i = userdata; + const UnitFileInstallInfo *i = ASSERT_PTR(userdata); char *ans; - assert(i); - if (unit_name_is_valid(i->name, UNIT_NAME_TEMPLATE) && i->default_instance) return unit_name_replace_instance(i->name, i->default_instance, ret); @@ -54,20 +50,16 @@ static int specifier_name(char specifier, const void *data, const char *root, co } static int specifier_prefix(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const UnitFileInstallInfo *i = userdata; - - assert(i); + const UnitFileInstallInfo *i = ASSERT_PTR(userdata); return unit_name_to_prefix(i->name, ret); } static int specifier_instance(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - const UnitFileInstallInfo *i = userdata; + const UnitFileInstallInfo *i = ASSERT_PTR(userdata); char *instance; int r; - assert(i); - r = unit_name_to_instance(i->name, &instance); if (r < 0) return r; @@ -87,6 +79,8 @@ static int specifier_last_component(char specifier, const void *data, const char char *dash; int r; + assert(ret); + r = specifier_prefix(specifier, data, root, userdata, &prefix); if (r < 0) return r; @@ -103,7 +97,11 @@ static int specifier_last_component(char specifier, const void *data, const char return 0; } -int install_full_printf_internal(const UnitFileInstallInfo *i, const char *format, size_t max_length, const char *root, char **ret) { +int install_name_printf( + LookupScope scope, + const UnitFileInstallInfo *info, + const char *format, + char **ret) { /* This is similar to unit_name_printf() */ const Specifier table[] = { @@ -115,13 +113,13 @@ int install_full_printf_internal(const UnitFileInstallInfo *i, const char *forma COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(scope), {} }; - assert(i); + assert(info); assert(format); assert(ret); - return specifier_printf(format, max_length, table, root, i, ret); + return specifier_printf(format, UNIT_NAME_MAX, table, info->root, info, ret); } diff --git a/src/shared/install-printf.h b/src/shared/install-printf.h index af32acc2c..6a9ab24e1 100644 --- a/src/shared/install-printf.h +++ b/src/shared/install-printf.h @@ -4,11 +4,8 @@ #include "install.h" #include "unit-name.h" -int install_full_printf_internal(const UnitFileInstallInfo *i, const char *format, size_t max_length, const char *root, char **ret); - -static inline int install_name_printf(const UnitFileInstallInfo *i, const char *format, const char *root, char **ret) { - return install_full_printf_internal(i, format, UNIT_NAME_MAX, root, ret); -} -static inline int install_path_printf(const UnitFileInstallInfo *i, const char *format, const char *root, char **ret) { - return install_full_printf_internal(i, format, PATH_MAX-1, root, ret); -} +int install_name_printf( + LookupScope scope, + const UnitFileInstallInfo *info, + const char *format, + char **ret); diff --git a/src/shared/install.c b/src/shared/install.c index 8f1af755f..f807947a3 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -47,6 +47,7 @@ typedef enum SearchFlags { } SearchFlags; typedef struct { + LookupScope scope; OrderedHashmap *will_process; OrderedHashmap *have_processed; } InstallContext; @@ -92,13 +93,14 @@ void unit_file_presets_freep(UnitFilePresets *p) { static const char *const unit_file_type_table[_UNIT_FILE_TYPE_MAX] = { [UNIT_FILE_TYPE_REGULAR] = "regular", - [UNIT_FILE_TYPE_SYMLINK] = "symlink", - [UNIT_FILE_TYPE_MASKED] = "masked", + [UNIT_FILE_TYPE_LINKED] = "linked", + [UNIT_FILE_TYPE_ALIAS] = "alias", + [UNIT_FILE_TYPE_MASKED] = "masked", }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(unit_file_type, UnitFileType); -static int in_search_path(const LookupPaths *p, const char *path) { +static int in_search_path(const LookupPaths *lp, const char *path) { _cleanup_free_ char *parent = NULL; assert(path); @@ -107,19 +109,16 @@ static int in_search_path(const LookupPaths *p, const char *path) { if (!parent) return -ENOMEM; - return path_strv_contains(p->search_path, parent); + return path_strv_contains(lp->search_path, parent); } -static const char* skip_root(const LookupPaths *p, const char *path) { - char *e; - - assert(p); +static const char* skip_root(const char *root_dir, const char *path) { assert(path); - if (!p->root_dir) + if (!root_dir) return path; - e = path_startswith(path, p->root_dir); + const char *e = path_startswith(path, root_dir); if (!e) return NULL; @@ -134,52 +133,52 @@ static const char* skip_root(const LookupPaths *p, const char *path) { return e; } -static int path_is_generator(const LookupPaths *p, const char *path) { +static int path_is_generator(const LookupPaths *lp, const char *path) { _cleanup_free_ char *parent = NULL; - assert(p); + assert(lp); assert(path); parent = dirname_malloc(path); if (!parent) return -ENOMEM; - return path_equal_ptr(parent, p->generator) || - path_equal_ptr(parent, p->generator_early) || - path_equal_ptr(parent, p->generator_late); + return path_equal_ptr(parent, lp->generator) || + path_equal_ptr(parent, lp->generator_early) || + path_equal_ptr(parent, lp->generator_late); } -static int path_is_transient(const LookupPaths *p, const char *path) { +static int path_is_transient(const LookupPaths *lp, const char *path) { _cleanup_free_ char *parent = NULL; - assert(p); + assert(lp); assert(path); parent = dirname_malloc(path); if (!parent) return -ENOMEM; - return path_equal_ptr(parent, p->transient); + return path_equal_ptr(parent, lp->transient); } -static int path_is_control(const LookupPaths *p, const char *path) { +static int path_is_control(const LookupPaths *lp, const char *path) { _cleanup_free_ char *parent = NULL; - assert(p); + assert(lp); assert(path); parent = dirname_malloc(path); if (!parent) return -ENOMEM; - return path_equal_ptr(parent, p->persistent_control) || - path_equal_ptr(parent, p->runtime_control); + return path_equal_ptr(parent, lp->persistent_control) || + path_equal_ptr(parent, lp->runtime_control); } -static int path_is_config(const LookupPaths *p, const char *path, bool check_parent) { +static int path_is_config(const LookupPaths *lp, const char *path, bool check_parent) { _cleanup_free_ char *parent = NULL; - assert(p); + assert(lp); assert(path); /* Note that we do *not* have generic checks for /etc or /run in place, since with @@ -193,21 +192,21 @@ static int path_is_config(const LookupPaths *p, const char *path, bool check_par path = parent; } - return path_equal_ptr(path, p->persistent_config) || - path_equal_ptr(path, p->runtime_config); + return path_equal_ptr(path, lp->persistent_config) || + path_equal_ptr(path, lp->runtime_config); } -static int path_is_runtime(const LookupPaths *p, const char *path, bool check_parent) { +static int path_is_runtime(const LookupPaths *lp, const char *path, bool check_parent) { _cleanup_free_ char *parent = NULL; const char *rpath; - assert(p); + assert(lp); assert(path); /* Everything in /run is considered runtime. On top of that we also add * explicit checks for the various runtime directories, as safety net. */ - rpath = skip_root(p, path); + rpath = skip_root(lp->root_dir, path); if (rpath && path_startswith(rpath, "/run")) return true; @@ -219,21 +218,21 @@ static int path_is_runtime(const LookupPaths *p, const char *path, bool check_pa path = parent; } - return path_equal_ptr(path, p->runtime_config) || - path_equal_ptr(path, p->generator) || - path_equal_ptr(path, p->generator_early) || - path_equal_ptr(path, p->generator_late) || - path_equal_ptr(path, p->transient) || - path_equal_ptr(path, p->runtime_control); + return path_equal_ptr(path, lp->runtime_config) || + path_equal_ptr(path, lp->generator) || + path_equal_ptr(path, lp->generator_early) || + path_equal_ptr(path, lp->generator_late) || + path_equal_ptr(path, lp->transient) || + path_equal_ptr(path, lp->runtime_control); } -static int path_is_vendor_or_generator(const LookupPaths *p, const char *path) { +static int path_is_vendor_or_generator(const LookupPaths *lp, const char *path) { const char *rpath; - assert(p); + assert(lp); assert(path); - rpath = skip_root(p, path); + rpath = skip_root(lp->root_dir, path); if (!rpath) return 0; @@ -245,19 +244,19 @@ static int path_is_vendor_or_generator(const LookupPaths *p, const char *path) { return true; #endif - if (path_is_generator(p, rpath)) + if (path_is_generator(lp, rpath)) return true; return path_equal(rpath, SYSTEM_DATA_UNIT_DIR); } -static const char* config_path_from_flags(const LookupPaths *paths, UnitFileFlags flags) { - assert(paths); +static const char* config_path_from_flags(const LookupPaths *lp, UnitFileFlags flags) { + assert(lp); if (FLAGS_SET(flags, UNIT_FILE_PORTABLE)) - return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? paths->runtime_attached : paths->persistent_attached; + return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? lp->runtime_attached : lp->persistent_attached; else - return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? paths->runtime_config : paths->persistent_config; + return FLAGS_SET(flags, UNIT_FILE_RUNTIME) ? lp->runtime_config : lp->persistent_config; } int unit_file_changes_add( @@ -322,7 +321,7 @@ void unit_file_changes_free(UnitFileChange *changes, size_t n_changes) { } void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet) { - bool logged = false; + int err = 0; assert(changes || n_changes == 0); /* If verb is not specified, errors are not allowed! */ @@ -336,12 +335,12 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang if (!quiet) log_info("Created symlink %s %s %s.", changes[i].path, - special_glyph(SPECIAL_GLYPH_ARROW), + special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), changes[i].source); break; case UNIT_FILE_UNLINK: if (!quiet) - log_info("Removed %s.", changes[i].path); + log_info("Removed \"%s\".", changes[i].path); break; case UNIT_FILE_IS_MASKED: if (!quiet) @@ -363,102 +362,141 @@ void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *chang break; case -EEXIST: if (changes[i].source) - log_error_errno(changes[i].type_or_errno, - "Failed to %s unit, file %s already exists and is a symlink to %s.", - verb, changes[i].path, changes[i].source); + err = log_error_errno(changes[i].type_or_errno, + "Failed to %s unit, file \"%s\" already exists and is a symlink to \"%s\".", + verb, changes[i].path, changes[i].source); else - log_error_errno(changes[i].type_or_errno, - "Failed to %s unit, file %s already exists.", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, + "Failed to %s unit, file \"%s\" already exists.", + verb, changes[i].path); break; case -ERFKILL: - log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s is masked.", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s is masked.", + verb, changes[i].path); break; case -EADDRNOTAVAIL: - log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s is transient or generated.", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s is transient or generated.", + verb, changes[i].path); + break; + case -EBADSLT: + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, invalid specifier in \"%s\".", + verb, changes[i].path); break; case -EIDRM: - log_error_errno(changes[i].type_or_errno, "Failed to %s %s, destination unit %s is a non-template unit.", - verb, changes[i].source, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s %s, destination unit %s is a non-template unit.", + verb, changes[i].source, changes[i].path); break; case -EUCLEAN: - log_error_errno(changes[i].type_or_errno, - "Failed to %s unit, \"%s\" is not a valid unit name.", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, + "Failed to %s unit, \"%s\" is not a valid unit name.", + verb, changes[i].path); break; case -ELOOP: - log_error_errno(changes[i].type_or_errno, "Failed to %s unit, refusing to operate on linked unit file %s", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, refusing to operate on linked unit file %s.", + verb, changes[i].path); + break; + case -EXDEV: + if (changes[i].source) + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, cannot alias %s as %s.", + verb, changes[i].source, changes[i].path); + else + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, invalid unit reference \"%s\".", + verb, changes[i].path); break; - case -ENOENT: - log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s does not exist.", verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, unit %s does not exist.", + verb, changes[i].path); + break; + case -EUNATCH: + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, cannot resolve specifiers in \"%s\".", + verb, changes[i].path); break; - default: assert(changes[i].type_or_errno < 0); - log_error_errno(changes[i].type_or_errno, "Failed to %s unit, file %s: %m.", - verb, changes[i].path); - logged = true; + err = log_error_errno(changes[i].type_or_errno, "Failed to %s unit, file \"%s\": %m", + verb, changes[i].path); } } - if (r < 0 && !logged) + if (r < 0 && err >= 0) log_error_errno(r, "Failed to %s: %m.", verb); } /** - * Checks if two paths or symlinks from wd are the same, when root is the root of the filesystem. - * wc should be the full path in the host file system. + * Checks if two symlink targets (starting from src) are equivalent as far as the unit enablement logic is + * concerned. If the target is in the unit search path, then anything with the same name is equivalent. + * If outside the unit search path, paths must be identical. */ -static bool chroot_symlinks_same(const char *root, const char *wd, const char *a, const char *b) { - assert(path_is_absolute(wd)); +static int chroot_unit_symlinks_equivalent( + const LookupPaths *lp, + const char *src, + const char *target_a, + const char *target_b) { + + assert(lp); + assert(src); + assert(target_a); + assert(target_b); /* This will give incorrect results if the paths are relative and go outside * of the chroot. False negatives are possible. */ - if (!root) - root = "/"; + const char *root = lp->root_dir ?: "/"; + _cleanup_free_ char *dirname = NULL; + int r; - a = strjoina(path_is_absolute(a) ? root : wd, "/", a); - b = strjoina(path_is_absolute(b) ? root : wd, "/", b); - return path_equal_or_files_same(a, b, 0); + if (!path_is_absolute(target_a) || !path_is_absolute(target_b)) { + r = path_extract_directory(src, &dirname); + if (r < 0) + return r; + } + + _cleanup_free_ char *a = path_join(path_is_absolute(target_a) ? root : dirname, target_a); + _cleanup_free_ char *b = path_join(path_is_absolute(target_b) ? root : dirname, target_b); + if (!a || !b) + return log_oom(); + + r = path_equal_or_files_same(a, b, 0); + if (r != 0) + return r; + + _cleanup_free_ char *a_name = NULL, *b_name = NULL; + r = path_extract_filename(a, &a_name); + if (r < 0) + return r; + r = path_extract_filename(b, &b_name); + if (r < 0) + return r; + + return streq(a_name, b_name) && + path_startswith_strv(a, lp->search_path) && + path_startswith_strv(b, lp->search_path); } static int create_symlink( - const LookupPaths *paths, + const LookupPaths *lp, const char *old_path, const char *new_path, bool force, UnitFileChange **changes, size_t *n_changes) { - _cleanup_free_ char *dest = NULL, *dirname = NULL; + _cleanup_free_ char *dest = NULL; const char *rp; int r; assert(old_path); assert(new_path); - rp = skip_root(paths, old_path); + rp = skip_root(lp->root_dir, old_path); if (rp) old_path = rp; - /* Actually create a symlink, and remember that we did. Is - * smart enough to check if there's already a valid symlink in - * place. + /* Actually create a symlink, and remember that we did. This function is + * smart enough to check if there's already a valid symlink in place. * - * Returns 1 if a symlink was created or already exists and points to - * the right place, or negative on error. + * Returns 1 if a symlink was created or already exists and points to the + * right place, or negative on error. */ (void) mkdir_parents_label(new_path, 0755); @@ -483,11 +521,7 @@ static int create_symlink( return r; } - dirname = dirname_malloc(new_path); - if (!dirname) - return -ENOMEM; - - if (chroot_symlinks_same(paths->root_dir, dirname, dest, old_path)) { + if (chroot_unit_symlinks_equivalent(lp, new_path, dest, old_path)) { log_debug("Symlink %s → %s already exists", new_path, dest); return 1; } @@ -566,7 +600,7 @@ static int remove_marked_symlinks_fd( rewinddir(d); - FOREACH_DIRENT(de, d, return -errno) { + FOREACH_DIRENT(de, d, return -errno) if (de->d_type == DT_DIR) { _cleanup_free_ char *p = NULL; @@ -594,8 +628,7 @@ static int remove_marked_symlinks_fd( r = q; } else if (de->d_type == DT_LNK) { - _cleanup_free_ char *p = NULL, *dest = NULL; - const char *rp; + _cleanup_free_ char *p = NULL; bool found; int q; @@ -607,21 +640,42 @@ static int remove_marked_symlinks_fd( return -ENOMEM; path_simplify(p); - q = chase_symlinks(p, NULL, CHASE_NONEXISTENT, &dest, NULL); - if (q == -ENOENT) - continue; - if (q < 0) { - if (r == 0) - r = q; - continue; + /* We remove all links pointing to a file or path that is marked, as well as all + * files sharing the same name as a file that is marked, and files sharing the same + * name after the instance has been removed. Do path chasing only if we don't already + * know that we want to remove the symlink. */ + found = set_contains(remove_symlinks_to, de->d_name); + + if (!found) { + _cleanup_free_ char *template = NULL; + + q = unit_name_template(de->d_name, &template); + if (q < 0 && q != -EINVAL) + return q; + if (q >= 0) + found = set_contains(remove_symlinks_to, template); } - /* We remove all links pointing to a file or path that is marked, as well as all files sharing - * the same name as a file that is marked. */ + if (!found) { + _cleanup_free_ char *dest = NULL; + + q = chase_symlinks(p, lp->root_dir, CHASE_NONEXISTENT, &dest, NULL); + if (q == -ENOENT) + continue; + if (q < 0) { + log_debug_errno(q, "Failed to resolve symlink \"%s\": %m", p); + unit_file_changes_add(changes, n_changes, q, p, NULL); + + if (r == 0) + r = q; + continue; + } + + found = set_contains(remove_symlinks_to, dest) || + set_contains(remove_symlinks_to, basename(dest)); + + } - found = set_contains(remove_symlinks_to, dest) || - set_contains(remove_symlinks_to, basename(dest)) || - set_contains(remove_symlinks_to, de->d_name); if (!found) continue; @@ -642,14 +696,13 @@ static int remove_marked_symlinks_fd( /* Now, remember the full path (but with the root prefix removed) of * the symlink we just removed, and remove any symlinks to it, too. */ - rp = skip_root(lp, p); + const char *rp = skip_root(lp->root_dir, p); q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: p); if (q < 0) return q; if (q > 0 && !dry_run) *restart = true; } - } return r; } @@ -722,8 +775,9 @@ static int find_symlinks_in_directory( DIR *dir, const char *dir_path, const char *root_dir, - const UnitFileInstallInfo *i, - bool match_aliases, + const UnitFileInstallInfo *info, + bool ignore_destination, + bool match_name, bool ignore_same_name, const char *config_path, bool *same_name_link) { @@ -731,53 +785,69 @@ static int find_symlinks_in_directory( int r = 0; FOREACH_DIRENT(de, dir, return -errno) { - _cleanup_free_ char *dest = NULL; - bool found_path = false, found_dest, b = false; + bool found_path = false, found_dest = false, b = false; int q; if (de->d_type != DT_LNK) continue; - /* Acquire symlink destination */ - q = readlinkat_malloc(dirfd(dir), de->d_name, &dest); - if (q == -ENOENT) - continue; - if (q < 0) { - if (r == 0) - r = q; - continue; + if (!ignore_destination) { + _cleanup_free_ char *dest = NULL; + + /* Acquire symlink destination */ + q = readlinkat_malloc(dirfd(dir), de->d_name, &dest); + if (q == -ENOENT) + continue; + if (q < 0) { + if (r == 0) + r = q; + continue; + } + + /* Make absolute */ + if (!path_is_absolute(dest)) { + char *x; + + x = path_join(dir_path, dest); + if (!x) + return -ENOMEM; + + free_and_replace(dest, x); + } + + /* Check if what the symlink points to matches what we are looking for */ + found_dest = streq(basename(dest), info->name); } - /* Make absolute */ - if (!path_is_absolute(dest)) { - char *x; + assert(unit_name_is_valid(info->name, UNIT_NAME_ANY)); - x = path_join(dir_path, dest); - if (!x) - return -ENOMEM; + /* Check if the symlink itself matches what we are looking for. + * + * If ignore_destination is specified, we only look at the source name. + * + * If ignore_same_name is specified, we are in one of the directories which + * have lower priority than the unit file, and even if a file or symlink with + * this name was found, we should ignore it. */ - free_and_replace(dest, x); + if (ignore_destination || !ignore_same_name) + found_path = streq(de->d_name, info->name); + + if (!found_path && ignore_destination) { + _cleanup_free_ char *template = NULL; + + q = unit_name_template(de->d_name, &template); + if (q < 0 && q != -EINVAL) + return q; + if (q >= 0) + found_dest = streq(template, info->name); } - assert(unit_name_is_valid(i->name, UNIT_NAME_ANY)); - if (!ignore_same_name) - /* Check if the symlink itself matches what we are looking for. - * - * If ignore_same_name is specified, we are in one of the directories which - * have lower priority than the unit file, and even if a file or symlink with - * this name was found, we should ignore it. */ - found_path = streq(de->d_name, i->name); - - /* Check if what the symlink points to matches what we are looking for */ - found_dest = streq(basename(dest), i->name); - if (found_path && found_dest) { _cleanup_free_ char *p = NULL, *t = NULL; - /* Filter out same name links in the main - * config path */ + /* Filter out same name links in the main config path */ p = path_make_absolute(de->d_name, dir_path); - t = path_make_absolute(i->name, config_path); + t = path_make_absolute(info->name, config_path); if (!p || !t) return -ENOMEM; @@ -788,11 +858,11 @@ static int find_symlinks_in_directory( if (b) *same_name_link = true; else if (found_path || found_dest) { - if (!match_aliases) + if (!match_name) return 1; /* Check if symlink name is in the set of names used by [Install] */ - q = is_symlink_with_known_name(i, de->d_name); + q = is_symlink_with_known_name(info, de->d_name); if (q < 0) return q; if (q > 0) @@ -843,64 +913,73 @@ static int find_symlinks( d = opendir(path); if (!d) { - log_error_errno(errno, "Failed to open directory '%s' while scanning for symlinks, ignoring: %m", path); + log_error_errno(errno, "Failed to open directory \"%s\" while scanning for symlinks, ignoring: %m", path); continue; } - r = find_symlinks_in_directory(d, path, root_dir, i, match_name, ignore_same_name, config_path, same_name_link); + r = find_symlinks_in_directory(d, path, root_dir, i, + /* ignore_destination= */ true, + /* match_name= */ match_name, + /* ignore_same_name= */ ignore_same_name, + config_path, + same_name_link); if (r > 0) return 1; else if (r < 0) - log_debug_errno(r, "Failed to lookup for symlinks in '%s': %m", path); + log_debug_errno(r, "Failed to look up symlinks in \"%s\": %m", path); } /* We didn't find any suitable symlinks in .wants or .requires directories, let's look for linked unit files in this directory. */ rewinddir(config_dir); - return find_symlinks_in_directory(config_dir, config_path, root_dir, i, match_name, ignore_same_name, config_path, same_name_link); + return find_symlinks_in_directory(config_dir, config_path, root_dir, i, + /* ignore_destination= */ false, + /* match_name= */ match_name, + /* ignore_same_name= */ ignore_same_name, + config_path, + same_name_link); } static int find_symlinks_in_scope( - UnitFileScope scope, - const LookupPaths *paths, - const UnitFileInstallInfo *i, + LookupScope scope, + const LookupPaths *lp, + const UnitFileInstallInfo *info, bool match_name, UnitFileState *state) { bool same_name_link_runtime = false, same_name_link_config = false; bool enabled_in_runtime = false, enabled_at_all = false; bool ignore_same_name = false; - char **p; int r; - assert(paths); - assert(i); + assert(lp); + assert(info); - /* As we iterate over the list of search paths in paths->search_path, we may encounter "same name" + /* As we iterate over the list of search paths in lp->search_path, we may encounter "same name" * symlinks. The ones which are "below" (i.e. have lower priority) than the unit file itself are * effectively masked, so we should ignore them. */ - STRV_FOREACH(p, paths->search_path) { + STRV_FOREACH(p, lp->search_path) { bool same_name_link = false; - r = find_symlinks(paths->root_dir, i, match_name, ignore_same_name, *p, &same_name_link); + r = find_symlinks(lp->root_dir, info, match_name, ignore_same_name, *p, &same_name_link); if (r < 0) return r; if (r > 0) { /* We found symlinks in this dir? Yay! Let's see where precisely it is enabled. */ - if (path_equal_ptr(*p, paths->persistent_config)) { + if (path_equal_ptr(*p, lp->persistent_config)) { /* This is the best outcome, let's return it immediately. */ *state = UNIT_FILE_ENABLED; return 1; } /* look for global enablement of user units */ - if (scope == UNIT_FILE_USER && path_is_user_config_dir(*p)) { + if (scope == LOOKUP_SCOPE_USER && path_is_user_config_dir(*p)) { *state = UNIT_FILE_ENABLED; return 1; } - r = path_is_runtime(paths, *p, false); + r = path_is_runtime(lp, *p, false); if (r < 0) return r; if (r > 0) @@ -909,10 +988,10 @@ static int find_symlinks_in_scope( enabled_at_all = true; } else if (same_name_link) { - if (path_equal_ptr(*p, paths->persistent_config)) + if (path_equal_ptr(*p, lp->persistent_config)) same_name_link_config = true; else { - r = path_is_runtime(paths, *p, false); + r = path_is_runtime(lp, *p, false); if (r < 0) return r; if (r > 0) @@ -922,7 +1001,7 @@ static int find_symlinks_in_scope( /* Check if next iteration will be "below" the unit file (either a regular file * or a symlink), and hence should be ignored */ - if (!ignore_same_name && path_startswith(i->path, *p)) + if (!ignore_same_name && path_startswith(info->path, *p)) ignore_same_name = true; } @@ -935,7 +1014,7 @@ static int find_symlinks_in_scope( * outside of runtime and configuration directory, then we consider it statically enabled. Note we do that only * for instance, not for regular names, as those are merely aliases, while instances explicitly instantiate * something, and hence are a much stronger concept. */ - if (enabled_at_all && unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) { + if (enabled_at_all && unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { *state = UNIT_FILE_STATIC; return 1; } @@ -955,7 +1034,6 @@ static int find_symlinks_in_scope( } static void install_info_free(UnitFileInstallInfo *i) { - if (!i) return; @@ -971,30 +1049,30 @@ static void install_info_free(UnitFileInstallInfo *i) { free(i); } -static void install_context_done(InstallContext *c) { - assert(c); +static void install_context_done(InstallContext *ctx) { + assert(ctx); - c->will_process = ordered_hashmap_free_with_destructor(c->will_process, install_info_free); - c->have_processed = ordered_hashmap_free_with_destructor(c->have_processed, install_info_free); + ctx->will_process = ordered_hashmap_free_with_destructor(ctx->will_process, install_info_free); + ctx->have_processed = ordered_hashmap_free_with_destructor(ctx->have_processed, install_info_free); } -static UnitFileInstallInfo *install_info_find(InstallContext *c, const char *name) { +static UnitFileInstallInfo *install_info_find(InstallContext *ctx, const char *name) { UnitFileInstallInfo *i; - i = ordered_hashmap_get(c->have_processed, name); + i = ordered_hashmap_get(ctx->have_processed, name); if (i) return i; - return ordered_hashmap_get(c->will_process, name); + return ordered_hashmap_get(ctx->will_process, name); } static int install_info_may_process( const UnitFileInstallInfo *i, - const LookupPaths *paths, + const LookupPaths *lp, UnitFileChange **changes, size_t *n_changes) { assert(i); - assert(paths); + assert(lp); /* Checks whether the loaded unit file is one we should process, or is masked, * transient or generated and thus not subject to enable/disable operations. */ @@ -1003,8 +1081,8 @@ static int install_info_may_process( unit_file_changes_add(changes, n_changes, -ERFKILL, i->path, NULL); return -ERFKILL; } - if (path_is_generator(paths, i->path) || - path_is_transient(paths, i->path)) { + if (path_is_generator(lp, i->path) || + path_is_transient(lp, i->path)) { unit_file_changes_add(changes, n_changes, -EADDRNOTAVAIL, i->path, NULL); return -EADDRNOTAVAIL; } @@ -1019,7 +1097,7 @@ static int install_info_may_process( * Returns negative on error, 0 if the unit was already known, 1 otherwise. */ static int install_info_add( - InstallContext *c, + InstallContext *ctx, const char *name, const char *path, const char *root, @@ -1029,7 +1107,7 @@ static int install_info_add( UnitFileInstallInfo *i = NULL; int r; - assert(c); + assert(ctx); if (!name) { /* 'name' and 'path' must not both be null. Check here 'path' using assert_se() to @@ -1042,7 +1120,7 @@ static int install_info_add( if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - i = install_info_find(c, name); + i = install_info_find(ctx, name); if (i) { i->auxiliary = i->auxiliary && auxiliary; @@ -1082,7 +1160,7 @@ static int install_info_add( } } - r = ordered_hashmap_ensure_put(&c->will_process, &string_hash_ops, i->name, i); + r = ordered_hashmap_ensure_put(&ctx->will_process, &string_hash_ops, i->name, i); if (r < 0) goto fail; @@ -1137,8 +1215,8 @@ static int config_parse_also( void *data, void *userdata) { - UnitFileInstallInfo *info = userdata; - InstallContext *c = data; + UnitFileInstallInfo *info = ASSERT_PTR(userdata); + InstallContext *ctx = ASSERT_PTR(data); int r; assert(unit); @@ -1155,11 +1233,12 @@ static int config_parse_also( if (r == 0) break; - r = install_name_printf(info, word, info->root, &printed); + r = install_name_printf(ctx->scope, info, word, &printed); if (r < 0) - return r; + return log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve unit name in Also=\"%s\": %m", word); - r = install_info_add(c, printed, NULL, info->root, /* auxiliary= */ true, NULL); + r = install_info_add(ctx, printed, NULL, info->root, /* auxiliary= */ true, NULL); if (r < 0) return r; @@ -1185,7 +1264,8 @@ static int config_parse_default_instance( void *data, void *userdata) { - UnitFileInstallInfo *i = data; + InstallContext *ctx = ASSERT_PTR(data); + UnitFileInstallInfo *info = ASSERT_PTR(userdata); _cleanup_free_ char *printed = NULL; int r; @@ -1202,24 +1282,23 @@ static int config_parse_default_instance( return log_syntax(unit, LOG_WARNING, filename, line, 0, "DefaultInstance= only makes sense for template units, ignoring."); - r = install_name_printf(i, rvalue, i->root, &printed); + r = install_name_printf(ctx->scope, info, rvalue, &printed); if (r < 0) - return r; + return log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to resolve instance name in DefaultInstance=\"%s\": %m", rvalue); - if (isempty(printed)) { - i->default_instance = mfree(i->default_instance); - return 0; - } + if (isempty(printed)) + printed = mfree(printed); - if (!unit_instance_is_valid(printed)) + if (printed && !unit_instance_is_valid(printed)) return log_syntax(unit, LOG_WARNING, filename, line, SYNTHETIC_ERRNO(EINVAL), "Invalid DefaultInstance= value \"%s\".", printed); - return free_and_replace(i->default_instance, printed); + return free_and_replace(info->default_instance, printed); } static int unit_file_load( - InstallContext *c, + InstallContext *ctx, UnitFileInstallInfo *info, const char *path, const char *root_dir, @@ -1230,7 +1309,7 @@ static int unit_file_load( { "Install", "WantedBy", config_parse_strv, 0, &info->wanted_by }, { "Install", "RequiredBy", config_parse_strv, 0, &info->required_by }, { "Install", "DefaultInstance", config_parse_default_instance, 0, info }, - { "Install", "Also", config_parse_also, 0, c }, + { "Install", "Also", config_parse_also, 0, ctx }, {} }; @@ -1303,8 +1382,8 @@ static int unit_file_load( if (!f) return -errno; - /* c is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */ - assert(c); + /* ctx is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */ + assert(ctx); r = config_parse(info->name, path, f, "Install\0" @@ -1324,7 +1403,7 @@ static int unit_file_load( 0, info, NULL); if (r < 0) - return log_debug_errno(r, "Failed to parse %s: %m", info->name); + return log_debug_errno(r, "Failed to parse \"%s\": %m", info->name); if ((flags & SEARCH_DROPIN) == 0) info->type = UNIT_FILE_TYPE_REGULAR; @@ -1336,89 +1415,42 @@ static int unit_file_load( } static int unit_file_load_or_readlink( - InstallContext *c, + InstallContext *ctx, UnitFileInstallInfo *info, const char *path, - const char *root_dir, + const LookupPaths *lp, SearchFlags flags) { - - _cleanup_free_ char *resolved = NULL; int r; - r = unit_file_load(c, info, path, root_dir, flags); + r = unit_file_load(ctx, info, path, lp->root_dir, flags); if (r != -ELOOP || (flags & SEARCH_DROPIN)) return r; - r = chase_symlinks(path, root_dir, CHASE_WARN | CHASE_NONEXISTENT, &resolved, NULL); - if (r >= 0 && - root_dir && - path_equal_ptr(path_startswith(resolved, root_dir), "dev/null")) - /* When looking under root_dir, we can't expect /dev/ to be mounted, - * so let's see if the path is a (possibly dangling) symlink to /dev/null. */ + /* This is a symlink, let's read and verify it. */ + r = unit_file_resolve_symlink(lp->root_dir, lp->search_path, + NULL, AT_FDCWD, path, + true, &info->symlink_target); + if (r < 0) + return r; + bool outside_search_path = r > 0; + + r = null_or_empty_path_with_root(info->symlink_target, lp->root_dir); + if (r < 0 && r != -ENOENT) + return log_debug_errno(r, "Failed to stat %s: %m", info->symlink_target); + if (r > 0) info->type = UNIT_FILE_TYPE_MASKED; - - else if (r > 0 && null_or_empty_path(resolved) > 0) - - info->type = UNIT_FILE_TYPE_MASKED; - - else { - _cleanup_free_ char *target = NULL; - const char *bn; - UnitType a, b; - - /* This is a symlink, let's read it. We read the link again, because last time - * we followed the link until resolution, and here we need to do one step. */ - - r = readlink_malloc(path, &target); - if (r < 0) - return r; - - bn = basename(target); - - if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN)) { - - if (!unit_name_is_valid(bn, UNIT_NAME_PLAIN)) - return -EINVAL; - - } else if (unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { - - if (!unit_name_is_valid(bn, UNIT_NAME_INSTANCE|UNIT_NAME_TEMPLATE)) - return -EINVAL; - - } else if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE)) { - - if (!unit_name_is_valid(bn, UNIT_NAME_TEMPLATE)) - return -EINVAL; - } else - return -EINVAL; - - /* Enforce that the symlink destination does not - * change the unit file type. */ - - a = unit_name_to_type(info->name); - b = unit_name_to_type(bn); - if (a < 0 || b < 0 || a != b) - return -EINVAL; - - if (path_is_absolute(target)) - /* This is an absolute path, prefix the root so that we always deal with fully qualified paths */ - info->symlink_target = path_join(root_dir, target); - else - /* This is a relative path, take it relative to the dir the symlink is located in. */ - info->symlink_target = file_in_same_dir(path, target); - if (!info->symlink_target) - return -ENOMEM; - - info->type = UNIT_FILE_TYPE_SYMLINK; - } + else if (outside_search_path) + info->type = UNIT_FILE_TYPE_LINKED; + else + info->type = UNIT_FILE_TYPE_ALIAS; return 0; } static int unit_file_search( - InstallContext *c, + InstallContext *ctx, UnitFileInstallInfo *info, - const LookupPaths *paths, + const LookupPaths *lp, SearchFlags flags) { const char *dropin_dir_name = NULL, *dropin_template_dir_name = NULL; @@ -1426,17 +1458,16 @@ static int unit_file_search( _cleanup_free_ char *template = NULL; bool found_unit = false; int r, result; - char **p; assert(info); - assert(paths); + assert(lp); /* Was this unit already loaded? */ if (info->type != _UNIT_FILE_TYPE_INVALID) return 0; if (info->path) - return unit_file_load_or_readlink(c, info, info->path, paths->root_dir, flags); + return unit_file_load_or_readlink(ctx, info, info->path, lp, flags); assert(info->name); @@ -1446,14 +1477,14 @@ static int unit_file_search( return r; } - STRV_FOREACH(p, paths->search_path) { + STRV_FOREACH(p, lp->search_path) { _cleanup_free_ char *path = NULL; path = path_join(*p, info->name); if (!path) return -ENOMEM; - r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); + r = unit_file_load_or_readlink(ctx, info, path, lp, flags); if (r >= 0) { info->path = TAKE_PTR(path); result = r; @@ -1469,14 +1500,14 @@ static int unit_file_search( * enablement was requested. We will check if it is * possible to load template unit file. */ - STRV_FOREACH(p, paths->search_path) { + STRV_FOREACH(p, lp->search_path) { _cleanup_free_ char *path = NULL; path = path_join(*p, template); if (!path) return -ENOMEM; - r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags); + r = unit_file_load_or_readlink(ctx, info, path, lp, flags); if (r >= 0) { info->path = TAKE_PTR(path); result = r; @@ -1498,7 +1529,7 @@ static int unit_file_search( /* Search for drop-in directories */ dropin_dir_name = strjoina(info->name, ".d"); - STRV_FOREACH(p, paths->search_path) { + STRV_FOREACH(p, lp->search_path) { char *path; path = path_join(*p, dropin_dir_name); @@ -1512,7 +1543,7 @@ static int unit_file_search( if (template) { dropin_template_dir_name = strjoina(template, ".d"); - STRV_FOREACH(p, paths->search_path) { + STRV_FOREACH(p, lp->search_path) { char *path; path = path_join(*p, dropin_template_dir_name); @@ -1532,39 +1563,38 @@ static int unit_file_search( return log_debug_errno(r, "Failed to get list of conf files: %m"); STRV_FOREACH(p, files) { - r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags | SEARCH_DROPIN); + r = unit_file_load_or_readlink(ctx, info, *p, lp, flags | SEARCH_DROPIN); if (r < 0) - return log_debug_errno(r, "Failed to load conf file %s: %m", *p); + return log_debug_errno(r, "Failed to load conf file \"%s\": %m", *p); } return result; } static int install_info_follow( - InstallContext *c, - UnitFileInstallInfo *i, - const char *root_dir, + InstallContext *ctx, + UnitFileInstallInfo *info, + const LookupPaths *lp, SearchFlags flags, bool ignore_different_name) { - assert(c); - assert(i); + assert(ctx); + assert(info); - if (i->type != UNIT_FILE_TYPE_SYMLINK) + if (!IN_SET(info->type, UNIT_FILE_TYPE_ALIAS, UNIT_FILE_TYPE_LINKED)) return -EINVAL; - if (!i->symlink_target) + if (!info->symlink_target) return -EINVAL; - /* If the basename doesn't match, the caller should add a - * complete new entry for this. */ + /* If the basename doesn't match, the caller should add a complete new entry for this. */ - if (!ignore_different_name && !streq(basename(i->symlink_target), i->name)) + if (!ignore_different_name && !streq(basename(info->symlink_target), info->name)) return -EXDEV; - free_and_replace(i->path, i->symlink_target); - i->type = _UNIT_FILE_TYPE_INVALID; + free_and_replace(info->path, info->symlink_target); + info->type = _UNIT_FILE_TYPE_INVALID; - return unit_file_load_or_readlink(c, i, i->path, root_dir, flags); + return unit_file_load_or_readlink(ctx, info, info->path, lp, flags); } /** @@ -1572,9 +1602,8 @@ static int install_info_follow( * target, maybe more than once. Propagate the instance name if present. */ static int install_info_traverse( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *paths, + InstallContext *ctx, + const LookupPaths *lp, UnitFileInstallInfo *start, SearchFlags flags, UnitFileInstallInfo **ret) { @@ -1583,37 +1612,37 @@ static int install_info_traverse( unsigned k = 0; int r; - assert(paths); + assert(lp); assert(start); - assert(c); + assert(ctx); - r = unit_file_search(c, start, paths, flags); + r = unit_file_search(ctx, start, lp, flags); if (r < 0) return r; i = start; - while (i->type == UNIT_FILE_TYPE_SYMLINK) { + while (IN_SET(i->type, UNIT_FILE_TYPE_ALIAS, UNIT_FILE_TYPE_LINKED)) { /* Follow the symlink */ if (++k > UNIT_FILE_FOLLOW_SYMLINK_MAX) return -ELOOP; if (!(flags & SEARCH_FOLLOW_CONFIG_SYMLINKS)) { - r = path_is_config(paths, i->path, true); + r = path_is_config(lp, i->path, true); if (r < 0) return r; if (r > 0) return -ELOOP; } - r = install_info_follow(c, i, paths->root_dir, flags, false); + r = install_info_follow(ctx, i, lp, flags, + /* If linked, don't look at the target name */ + /* ignore_different_name= */ i->type == UNIT_FILE_TYPE_LINKED); if (r == -EXDEV) { _cleanup_free_ char *buffer = NULL; const char *bn; - /* Target has a different name, create a new - * install info object for that, and continue - * with that. */ + /* Target is an alias, create a new install info object and continue with that. */ bn = basename(i->symlink_target); @@ -1632,10 +1661,10 @@ static int install_info_traverse( if (streq(buffer, i->name)) { - /* We filled in the instance, and the target stayed the same? If so, then let's - * honour the link as it is. */ + /* We filled in the instance, and the target stayed the same? If so, + * then let's honour the link as it is. */ - r = install_info_follow(c, i, paths->root_dir, flags, true); + r = install_info_follow(ctx, i, lp, flags, true); if (r < 0) return r; @@ -1645,12 +1674,12 @@ static int install_info_traverse( bn = buffer; } - r = install_info_add(c, bn, NULL, paths->root_dir, /* auxiliary= */ false, &i); + r = install_info_add(ctx, bn, NULL, lp->root_dir, /* auxiliary= */ false, &i); if (r < 0) return r; /* Try again, with the new target we found. */ - r = unit_file_search(c, i, paths, flags); + r = unit_file_search(ctx, i, lp, flags); if (r == -ENOENT) /* Translate error code to highlight this specific case */ return -ENOLINK; @@ -1671,44 +1700,43 @@ static int install_info_traverse( * or the name (otherwise). root_dir is prepended to the path. */ static int install_info_add_auto( - InstallContext *c, - const LookupPaths *paths, + InstallContext *ctx, + const LookupPaths *lp, const char *name_or_path, UnitFileInstallInfo **ret) { - assert(c); + assert(ctx); assert(name_or_path); if (path_is_absolute(name_or_path)) { const char *pp; - pp = prefix_roota(paths->root_dir, name_or_path); + pp = prefix_roota(lp->root_dir, name_or_path); - return install_info_add(c, NULL, pp, paths->root_dir, /* auxiliary= */ false, ret); + return install_info_add(ctx, NULL, pp, lp->root_dir, /* auxiliary= */ false, ret); } else - return install_info_add(c, name_or_path, NULL, paths->root_dir, /* auxiliary= */ false, ret); + return install_info_add(ctx, name_or_path, NULL, lp->root_dir, /* auxiliary= */ false, ret); } static int install_info_discover( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *paths, + InstallContext *ctx, + const LookupPaths *lp, const char *name, SearchFlags flags, UnitFileInstallInfo **ret, UnitFileChange **changes, size_t *n_changes) { - UnitFileInstallInfo *i; + UnitFileInstallInfo *info; int r; - assert(c); - assert(paths); + assert(ctx); + assert(lp); assert(name); - r = install_info_add_auto(c, paths, name, &i); + r = install_info_add_auto(ctx, lp, name, &info); if (r >= 0) - r = install_info_traverse(scope, c, paths, i, flags, ret); + r = install_info_traverse(ctx, lp, info, flags, ret); if (r < 0) unit_file_changes_add(changes, n_changes, r, name, NULL); @@ -1716,25 +1744,30 @@ static int install_info_discover( } static int install_info_discover_and_check( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *paths, - const char *name, - SearchFlags flags, - UnitFileInstallInfo **ret, - UnitFileChange **changes, - size_t *n_changes) { + InstallContext *ctx, + const LookupPaths *lp, + const char *name, + SearchFlags flags, + UnitFileInstallInfo **ret, + UnitFileChange **changes, + size_t *n_changes) { int r; - r = install_info_discover(scope, c, paths, name, flags, ret, changes, n_changes); + r = install_info_discover(ctx, lp, name, flags, ret, changes, n_changes); if (r < 0) return r; - return install_info_may_process(ret ? *ret : NULL, paths, changes, n_changes); + return install_info_may_process(ret ? *ret : NULL, lp, changes, n_changes); } -int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char **ret_dst) { +int unit_file_verify_alias( + const UnitFileInstallInfo *info, + const char *dst, + char **ret_dst, + UnitFileChange **changes, + size_t *n_changes) { + _cleanup_free_ char *dst_updated = NULL; int r; @@ -1744,6 +1777,11 @@ int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char * * ret_dst is set in cases where "instance propagation" happens, i.e. when the instance part is * inserted into dst. It is not normally set, even on success, so that the caller can easily * distinguish the case where instance propagation occurred. + * + * Returns: + * -EXDEV when the alias doesn't match the unit, + * -EUCLEAN when the name is invalid, + * -ELOOP when the alias it to the unit itself. */ const char *path_alias = strrchr(dst, '/'); @@ -1761,26 +1799,32 @@ int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char * p = endswith(dir, ".wants"); if (!p) p = endswith(dir, ".requires"); - if (!p) - return log_warning_errno(SYNTHETIC_ERRNO(EXDEV), - "Invalid path \"%s\" in alias.", dir); + if (!p) { + unit_file_changes_add(changes, n_changes, -EXDEV, dst, NULL); + return log_debug_errno(SYNTHETIC_ERRNO(EXDEV), "Invalid path \"%s\" in alias.", dir); + } + *p = '\0'; /* dir should now be a unit name */ UnitNameFlags type = unit_name_classify(dir); - if (type < 0) - return log_warning_errno(SYNTHETIC_ERRNO(EXDEV), - "Invalid unit name component \"%s\" in alias.", dir); + if (type < 0) { + unit_file_changes_add(changes, n_changes, -EXDEV, dst, NULL); + return log_debug_errno(SYNTHETIC_ERRNO(EXDEV), + "Invalid unit name component \"%s\" in alias.", dir); + } const bool instance_propagation = type == UNIT_NAME_TEMPLATE; /* That's the name we want to use for verification. */ - r = unit_symlink_name_compatible(path_alias, i->name, instance_propagation); + r = unit_symlink_name_compatible(path_alias, info->name, instance_propagation); if (r < 0) return log_error_errno(r, "Failed to verify alias validity: %m"); - if (r == 0) - return log_warning_errno(SYNTHETIC_ERRNO(EXDEV), - "Invalid unit %s symlink %s.", - i->name, dst); + if (r == 0) { + unit_file_changes_add(changes, n_changes, -EXDEV, dst, info->name); + return log_debug_errno(SYNTHETIC_ERRNO(EXDEV), + "Invalid unit \"%s\" symlink \"%s\".", + info->name, dst); + } } else { /* If the symlink target has an instance set and the symlink source doesn't, we "propagate @@ -1788,9 +1832,11 @@ int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char * if (unit_name_is_valid(dst, UNIT_NAME_TEMPLATE)) { _cleanup_free_ char *inst = NULL; - UnitNameFlags type = unit_name_to_instance(i->name, &inst); - if (type < 0) - return log_error_errno(type, "Failed to extract instance name from %s: %m", i->name); + UnitNameFlags type = unit_name_to_instance(info->name, &inst); + if (type < 0) { + unit_file_changes_add(changes, n_changes, -EUCLEAN, info->name, NULL); + return log_debug_errno(type, "Failed to extract instance name from \"%s\": %m", info->name); + } if (type == UNIT_NAME_INSTANCE) { r = unit_name_replace_instance(dst, inst, &dst_updated); @@ -1800,10 +1846,16 @@ int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char * } } - r = unit_validate_alias_symlink_and_warn(dst_updated ?: dst, i->name); - if (r < 0) + r = unit_validate_alias_symlink_or_warn(LOG_DEBUG, dst_updated ?: dst, info->name); + if (r == -ELOOP) /* -ELOOP means self-alias, which we (quietly) ignore */ return r; - + if (r < 0) { + unit_file_changes_add(changes, n_changes, + r == -EINVAL ? -EXDEV : r, + dst_updated ?: dst, + info->name); + return r; + } } *ret_dst = TAKE_PTR(dst_updated); @@ -1811,48 +1863,54 @@ int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char * } static int install_info_symlink_alias( - UnitFileInstallInfo *i, - const LookupPaths *paths, + LookupScope scope, + UnitFileInstallInfo *info, + const LookupPaths *lp, const char *config_path, bool force, UnitFileChange **changes, size_t *n_changes) { - char **s; int r = 0, q; - assert(i); - assert(paths); + assert(info); + assert(lp); assert(config_path); - STRV_FOREACH(s, i->aliases) { + STRV_FOREACH(s, info->aliases) { _cleanup_free_ char *alias_path = NULL, *dst = NULL, *dst_updated = NULL; - q = install_path_printf(i, *s, i->root, &dst); - if (q < 0) - return q; - - q = unit_file_verify_alias(i, dst, &dst_updated); - if (q < 0) + q = install_name_printf(scope, info, *s, &dst); + if (q < 0) { + unit_file_changes_add(changes, n_changes, q, *s, NULL); + r = r < 0 ? r : q; continue; + } + + q = unit_file_verify_alias(info, dst, &dst_updated, changes, n_changes); + if (q == -ELOOP) + continue; + if (q < 0) { + r = r < 0 ? r : q; + continue; + } alias_path = path_make_absolute(dst_updated ?: dst, config_path); if (!alias_path) return -ENOMEM; - q = create_symlink(paths, i->path, alias_path, force, changes, n_changes); - if (r == 0) - r = q; + q = create_symlink(lp, info->name, alias_path, force, changes, n_changes); + r = r < 0 ? r : q; } return r; } static int install_info_symlink_wants( - UnitFileScope scope, + LookupScope scope, UnitFileFlags file_flags, - UnitFileInstallInfo *i, - const LookupPaths *paths, + UnitFileInstallInfo *info, + const LookupPaths *lp, const char *config_path, char **list, const char *suffix, @@ -1862,21 +1920,20 @@ static int install_info_symlink_wants( _cleanup_free_ char *buf = NULL; UnitNameFlags valid_dst_type = UNIT_NAME_ANY; const char *n; - char **s; int r = 0, q; - assert(i); - assert(paths); + assert(info); + assert(lp); assert(config_path); if (strv_isempty(list)) return 0; - if (unit_name_is_valid(i->name, UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE)) + if (unit_name_is_valid(info->name, UNIT_NAME_PLAIN | UNIT_NAME_INSTANCE)) /* Not a template unit. Use the name directly. */ - n = i->name; + n = info->name; - else if (i->default_instance) { + else if (info->default_instance) { UnitFileInstallInfo instance = { .type = _UNIT_FILE_TYPE_INVALID, }; @@ -1884,12 +1941,12 @@ static int install_info_symlink_wants( /* If this is a template, and we have a default instance, use it. */ - r = unit_name_replace_instance(i->name, i->default_instance, &buf); + r = unit_name_replace_instance(info->name, info->default_instance, &buf); if (r < 0) return r; instance.name = buf; - r = unit_file_search(NULL, &instance, paths, SEARCH_FOLLOW_CONFIG_SYMLINKS); + r = unit_file_search(NULL, &instance, lp, SEARCH_FOLLOW_CONFIG_SYMLINKS); if (r < 0) return r; @@ -1907,15 +1964,17 @@ static int install_info_symlink_wants( * the instance from that unit. Cannot be used with non-instance units. */ valid_dst_type = UNIT_NAME_INSTANCE | UNIT_NAME_TEMPLATE; - n = i->name; + n = info->name; } STRV_FOREACH(s, list) { - _cleanup_free_ char *path = NULL, *dst = NULL; + _cleanup_free_ char *dst = NULL; - q = install_name_printf(i, *s, i->root, &dst); - if (q < 0) + q = install_name_printf(scope, info, *s, &dst); + if (q < 0) { + unit_file_changes_add(changes, n_changes, q, *s, NULL); return q; + } if (!unit_name_is_valid(dst, valid_dst_type)) { /* Generate a proper error here: EUCLEAN if the name is generally bad, EIDRM if the @@ -1939,24 +1998,28 @@ static int install_info_symlink_wants( continue; } - path = strjoin(config_path, "/", dst, suffix, n); + _cleanup_free_ char *path = strjoin(config_path, "/", dst, suffix, n); if (!path) return -ENOMEM; - q = create_symlink(paths, i->path, path, true, changes, n_changes); + _cleanup_free_ char *target = strjoin("../", info->name); + if (!target) + return -ENOMEM; + + q = create_symlink(lp, target, path, true, changes, n_changes); if (r == 0) r = q; - if (unit_file_exists(scope, paths, dst) == 0) - unit_file_changes_add(changes, n_changes, UNIT_FILE_DESTINATION_NOT_PRESENT, dst, i->path); + if (unit_file_exists(scope, lp, dst) == 0) + unit_file_changes_add(changes, n_changes, UNIT_FILE_DESTINATION_NOT_PRESENT, dst, info->path); } return r; } static int install_info_symlink_link( - UnitFileInstallInfo *i, - const LookupPaths *paths, + UnitFileInstallInfo *info, + const LookupPaths *lp, const char *config_path, bool force, UnitFileChange **changes, @@ -1965,67 +2028,68 @@ static int install_info_symlink_link( _cleanup_free_ char *path = NULL; int r; - assert(i); - assert(paths); + assert(info); + assert(lp); assert(config_path); - assert(i->path); + assert(info->path); - r = in_search_path(paths, i->path); + r = in_search_path(lp, info->path); if (r < 0) return r; if (r > 0) return 0; - path = path_join(config_path, i->name); + path = path_join(config_path, info->name); if (!path) return -ENOMEM; - return create_symlink(paths, i->path, path, force, changes, n_changes); + return create_symlink(lp, info->path, path, force, changes, n_changes); } static int install_info_apply( - UnitFileScope scope, + LookupScope scope, UnitFileFlags file_flags, - UnitFileInstallInfo *i, - const LookupPaths *paths, + UnitFileInstallInfo *info, + const LookupPaths *lp, const char *config_path, UnitFileChange **changes, size_t *n_changes) { int r, q; - assert(i); - assert(paths); + assert(info); + assert(lp); assert(config_path); - if (i->type != UNIT_FILE_TYPE_REGULAR) + if (info->type != UNIT_FILE_TYPE_REGULAR) return 0; bool force = file_flags & UNIT_FILE_FORCE; - r = install_info_symlink_alias(i, paths, config_path, force, changes, n_changes); - - q = install_info_symlink_wants(scope, file_flags, i, paths, config_path, i->wanted_by, ".wants/", changes, n_changes); - if (r == 0) - r = q; - - q = install_info_symlink_wants(scope, file_flags, i, paths, config_path, i->required_by, ".requires/", changes, n_changes); - if (r == 0) - r = q; - - q = install_info_symlink_link(i, paths, config_path, force, changes, n_changes); + r = install_info_symlink_link(info, lp, config_path, force, changes, n_changes); /* Do not count links to the unit file towards the "carries_install_info" count */ - if (r == 0 && q < 0) + if (r < 0) + /* If linking of the file failed, do not try to create other symlinks, + * because they might would pointing to a non-existent or wrong unit. */ + return r; + + r = install_info_symlink_alias(scope, info, lp, config_path, force, changes, n_changes); + + q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->wanted_by, ".wants/", changes, n_changes); + if (r == 0) + r = q; + + q = install_info_symlink_wants(scope, file_flags, info, lp, config_path, info->required_by, ".requires/", changes, n_changes); + if (r == 0) r = q; return r; } static int install_context_apply( - UnitFileScope scope, + InstallContext *ctx, + const LookupPaths *lp, UnitFileFlags file_flags, - InstallContext *c, - const LookupPaths *paths, const char *config_path, SearchFlags flags, UnitFileChange **changes, @@ -2034,26 +2098,26 @@ static int install_context_apply( UnitFileInstallInfo *i; int r; - assert(c); - assert(paths); + assert(ctx); + assert(lp); assert(config_path); - if (ordered_hashmap_isempty(c->will_process)) + if (ordered_hashmap_isempty(ctx->will_process)) return 0; - r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&ctx->have_processed, &string_hash_ops); if (r < 0) return r; r = 0; - while ((i = ordered_hashmap_first(c->will_process))) { + while ((i = ordered_hashmap_first(ctx->will_process))) { int q; - q = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); + q = ordered_hashmap_move_one(ctx->have_processed, ctx->will_process, i->name); if (q < 0) return q; - q = install_info_traverse(scope, c, paths, i, flags, NULL); + q = install_info_traverse(ctx, lp, i, flags, NULL); if (q < 0) { if (i->auxiliary) { q = unit_file_changes_add(changes, n_changes, UNIT_FILE_AUXILIARY_FAILED, NULL, i->name); @@ -2080,7 +2144,7 @@ static int install_context_apply( if (i->type != UNIT_FILE_TYPE_REGULAR) continue; - q = install_info_apply(scope, file_flags, i, paths, config_path, changes, n_changes); + q = install_info_apply(ctx->scope, file_flags, i, lp, config_path, changes, n_changes); if (r >= 0) { if (q < 0) r = q; @@ -2093,9 +2157,8 @@ static int install_context_apply( } static int install_context_mark_for_removal( - UnitFileScope scope, - InstallContext *c, - const LookupPaths *paths, + InstallContext *ctx, + const LookupPaths *lp, Set **remove_symlinks_to, const char *config_path, UnitFileChange **changes, @@ -2104,26 +2167,26 @@ static int install_context_mark_for_removal( UnitFileInstallInfo *i; int r; - assert(c); - assert(paths); + assert(ctx); + assert(lp); assert(config_path); /* Marks all items for removal */ - if (ordered_hashmap_isempty(c->will_process)) + if (ordered_hashmap_isempty(ctx->will_process)) return 0; - r = ordered_hashmap_ensure_allocated(&c->have_processed, &string_hash_ops); + r = ordered_hashmap_ensure_allocated(&ctx->have_processed, &string_hash_ops); if (r < 0) return r; - while ((i = ordered_hashmap_first(c->will_process))) { + while ((i = ordered_hashmap_first(ctx->will_process))) { - r = ordered_hashmap_move_one(c->have_processed, c->will_process, i->name); + r = ordered_hashmap_move_one(ctx->have_processed, ctx->will_process, i->name); if (r < 0) return r; - r = install_info_traverse(scope, c, paths, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); + r = install_info_traverse(ctx, lp, i, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, NULL); if (r == -ENOLINK) { log_debug_errno(r, "Name %s leads to a dangling symlink, removing name.", i->name); unit_file_changes_add(changes, n_changes, UNIT_FILE_IS_DANGLING, i->path ?: i->name, NULL); @@ -2157,26 +2220,25 @@ static int install_context_mark_for_removal( } int unit_file_mask( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; const char *config_path; - char **i; int r; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; - config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + config_path = (flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config; if (!config_path) return -ENXIO; @@ -2194,7 +2256,7 @@ int unit_file_mask( if (!path) return -ENOMEM; - q = create_symlink(&paths, "/dev/null", path, !!(flags & UNIT_FILE_FORCE), changes, n_changes); + q = create_symlink(&lp, "/dev/null", path, flags & UNIT_FILE_FORCE, changes, n_changes); if (q < 0 && r >= 0) r = q; } @@ -2203,34 +2265,32 @@ int unit_file_mask( } int unit_file_unmask( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; _cleanup_strv_free_ char **todo = NULL; const char *config_path; size_t n_todo = 0; - bool dry_run; - char **i; int r, q; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; - config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + config_path = (flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config; if (!config_path) return -ENXIO; - dry_run = !!(flags & UNIT_FILE_DRY_RUN); + bool dry_run = flags & UNIT_FILE_DRY_RUN; STRV_FOREACH(i, files) { _cleanup_free_ char *path = NULL; @@ -2283,13 +2343,13 @@ int unit_file_unmask( unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, path, NULL); - rp = skip_root(&paths, path); + rp = skip_root(lp.root_dir, path); q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: path); if (q < 0) return q; } - q = remove_marked_symlinks(remove_symlinks_to, config_path, &paths, dry_run, changes, n_changes); + q = remove_marked_symlinks(remove_symlinks_to, config_path, &lp, dry_run, changes, n_changes); if (r >= 0) r = q; @@ -2297,28 +2357,27 @@ int unit_file_unmask( } int unit_file_link( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; _cleanup_strv_free_ char **todo = NULL; const char *config_path; size_t n_todo = 0; - char **i; int r, q; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; - config_path = (flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + config_path = (flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config; if (!config_path) return -ENXIO; @@ -2334,7 +2393,7 @@ int unit_file_link( if (!unit_name_is_valid(fn, UNIT_NAME_ANY)) return -EINVAL; - full = path_join(paths.root_dir, *i); + full = path_join(lp.root_dir, *i); if (!full) return -ENOMEM; @@ -2344,7 +2403,7 @@ int unit_file_link( if (r < 0) return r; - q = in_search_path(&paths, *i); + q = in_search_path(&lp, *i); if (q < 0) return q; if (q > 0) @@ -2370,7 +2429,7 @@ int unit_file_link( if (!new_path) return -ENOMEM; - q = create_symlink(&paths, *i, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes); + q = create_symlink(&lp, *i, new_path, flags & UNIT_FILE_FORCE, changes, n_changes); if (q < 0 && r >= 0) r = q; } @@ -2378,37 +2437,36 @@ int unit_file_link( return r; } -static int path_shall_revert(const LookupPaths *paths, const char *path) { +static int path_shall_revert(const LookupPaths *lp, const char *path) { int r; - assert(paths); + assert(lp); assert(path); /* Checks whether the path is one where the drop-in directories shall be removed. */ - r = path_is_config(paths, path, true); + r = path_is_config(lp, path, true); if (r != 0) return r; - r = path_is_control(paths, path); + r = path_is_control(lp, path); if (r != 0) return r; - return path_is_transient(paths, path); + return path_is_transient(lp, path); } int unit_file_revert( - UnitFileScope scope, + LookupScope scope, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes) { _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - _cleanup_(lookup_paths_free) LookupPaths paths = {}; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; _cleanup_strv_free_ char **todo = NULL; size_t n_todo = 0; - char **i; int r, q; /* Puts a unit file back into vendor state. This means: @@ -2422,18 +2480,17 @@ int unit_file_revert( * We remove all that in both the runtime and the persistent directories, if that applies. */ - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; STRV_FOREACH(i, files) { bool has_vendor = false; - char **p; if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) return -EINVAL; - STRV_FOREACH(p, paths.search_path) { + STRV_FOREACH(p, lp.search_path) { _cleanup_free_ char *path = NULL, *dropin = NULL; struct stat st; @@ -2447,7 +2504,7 @@ int unit_file_revert( return -errno; } else if (S_ISREG(st.st_mode)) { /* Check if there's a vendor version */ - r = path_is_vendor_or_generator(&paths, path); + r = path_is_vendor_or_generator(&lp, path); if (r < 0) return r; if (r > 0) @@ -2464,7 +2521,7 @@ int unit_file_revert( return -errno; } else if (S_ISDIR(st.st_mode)) { /* Remove the drop-ins */ - r = path_shall_revert(&paths, dropin); + r = path_shall_revert(&lp, dropin); if (r < 0) return r; if (r > 0) { @@ -2480,7 +2537,7 @@ int unit_file_revert( continue; /* OK, there's a vendor version, hence drop all configuration versions */ - STRV_FOREACH(p, paths.search_path) { + STRV_FOREACH(p, lp.search_path) { _cleanup_free_ char *path = NULL; struct stat st; @@ -2493,7 +2550,7 @@ int unit_file_revert( if (errno != ENOENT) return -errno; } else if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) { - r = path_is_config(&paths, path, true); + r = path_is_config(&lp, path, true); if (r < 0) return r; if (r > 0) { @@ -2512,7 +2569,6 @@ int unit_file_revert( STRV_FOREACH(i, todo) { _cleanup_strv_free_ char **fs = NULL; const char *rp; - char **j; (void) get_files_in_directory(*i, &fs); @@ -2534,17 +2590,17 @@ int unit_file_revert( unit_file_changes_add(changes, n_changes, UNIT_FILE_UNLINK, *i, NULL); - rp = skip_root(&paths, *i); + rp = skip_root(lp.root_dir, *i); q = mark_symlink_for_removal(&remove_symlinks_to, rp ?: *i); if (q < 0) return q; } - q = remove_marked_symlinks(remove_symlinks_to, paths.runtime_config, &paths, false, changes, n_changes); + q = remove_marked_symlinks(remove_symlinks_to, lp.runtime_config, &lp, false, changes, n_changes); if (r >= 0) r = q; - q = remove_marked_symlinks(remove_symlinks_to, paths.persistent_config, &paths, false, changes, n_changes); + q = remove_marked_symlinks(remove_symlinks_to, lp.persistent_config, &lp, false, changes, n_changes); if (r >= 0) r = q; @@ -2552,7 +2608,7 @@ int unit_file_revert( } int unit_file_add_dependency( - UnitFileScope scope, + LookupScope scope, UnitFileFlags file_flags, const char *root_dir, char **files, @@ -2561,15 +2617,14 @@ int unit_file_add_dependency( UnitFileChange **changes, size_t *n_changes) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i, *target_info; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + UnitFileInstallInfo *info, *target_info; const char *config_path; - char **f; int r; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); assert(target); if (!IN_SET(dep, UNIT_WANTS, UNIT_REQUIRES)) @@ -2578,15 +2633,15 @@ int unit_file_add_dependency( if (!unit_name_is_valid(target, UNIT_NAME_ANY)) return -EINVAL; - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; - config_path = (file_flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + config_path = (file_flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config; if (!config_path) return -ENXIO; - r = install_info_discover_and_check(scope, &c, &paths, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, + r = install_info_discover_and_check(&ctx, &lp, target, SEARCH_FOLLOW_CONFIG_SYMLINKS, &target_info, changes, n_changes); if (r < 0) return r; @@ -2596,21 +2651,21 @@ int unit_file_add_dependency( STRV_FOREACH(f, files) { char ***l; - r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + r = install_info_discover_and_check(&ctx, &lp, *f, SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); if (r < 0) return r; - assert(i->type == UNIT_FILE_TYPE_REGULAR); + assert(info->type == UNIT_FILE_TYPE_REGULAR); /* We didn't actually load anything from the unit * file, but instead just add in our new symlink to * create. */ if (dep == UNIT_WANTS) - l = &i->wanted_by; + l = &info->wanted_by; else - l = &i->required_by; + l = &info->required_by; strv_free(*l); *l = strv_new(target_info->name); @@ -2618,43 +2673,30 @@ int unit_file_add_dependency( return -ENOMEM; } - return install_context_apply(scope, file_flags, &c, &paths, config_path, + return install_context_apply(&ctx, &lp, file_flags, config_path, SEARCH_FOLLOW_CONFIG_SYMLINKS, changes, n_changes); } -int unit_file_enable( - UnitFileScope scope, - UnitFileFlags file_flags, - const char *root_dir, +static int do_unit_file_enable( + const LookupPaths *lp, + LookupScope scope, + UnitFileFlags flags, + const char *config_path, char **files, UnitFileChange **changes, size_t *n_changes) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - const char *config_path; - UnitFileInstallInfo *i; - char **f; + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + UnitFileInstallInfo *info; int r; - assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); - - r = lookup_paths_init(&paths, scope, 0, root_dir); - if (r < 0) - return r; - - config_path = config_path_from_flags(&paths, file_flags); - if (!config_path) - return -ENXIO; - STRV_FOREACH(f, files) { - r = install_info_discover_and_check(scope, &c, &paths, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + r = install_info_discover_and_check(&ctx, lp, *f, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); if (r < 0) return r; - assert(i->type == UNIT_FILE_TYPE_REGULAR); + assert(info->type == UNIT_FILE_TYPE_REGULAR); } /* This will return the number of symlink rules that were @@ -2662,94 +2704,200 @@ int unit_file_enable( is useful to determine whether the passed files had any installation data at all. */ - return install_context_apply(scope, file_flags, &c, &paths, config_path, SEARCH_LOAD, changes, n_changes); + return install_context_apply(&ctx, lp, flags, config_path, + SEARCH_LOAD, changes, n_changes); } -int unit_file_disable( - UnitFileScope scope, +int unit_file_enable( + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - const char *config_path; - char **i; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; int r; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; - config_path = config_path_from_flags(&paths, flags); + const char *config_path = config_path_from_flags(&lp, flags); if (!config_path) return -ENXIO; + return do_unit_file_enable(&lp, scope, flags, config_path, files, changes, n_changes); +} + +static int do_unit_file_disable( + const LookupPaths *lp, + LookupScope scope, + UnitFileFlags flags, + const char *config_path, + char **files, + UnitFileChange **changes, + size_t *n_changes) { + + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; + int r; + STRV_FOREACH(i, files) { if (!unit_name_is_valid(*i, UNIT_NAME_ANY)) return -EINVAL; - r = install_info_add(&c, *i, NULL, paths.root_dir, /* auxiliary= */ false, NULL); + r = install_info_add(&ctx, *i, NULL, lp->root_dir, /* auxiliary= */ false, NULL); if (r < 0) return r; } - r = install_context_mark_for_removal(scope, &c, &paths, &remove_symlinks_to, config_path, changes, n_changes); + r = install_context_mark_for_removal(&ctx, lp, &remove_symlinks_to, config_path, changes, n_changes); if (r < 0) return r; - return remove_marked_symlinks(remove_symlinks_to, config_path, &paths, !!(flags & UNIT_FILE_DRY_RUN), changes, n_changes); + return remove_marked_symlinks(remove_symlinks_to, config_path, lp, flags & UNIT_FILE_DRY_RUN, changes, n_changes); } -int unit_file_reenable( - UnitFileScope scope, + +int unit_file_disable( + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes) { - char **n; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; int r; - size_t l, i; + + assert(scope >= 0); + assert(scope < _LOOKUP_SCOPE_MAX); + + r = lookup_paths_init(&lp, scope, 0, root_dir); + if (r < 0) + return r; + + const char *config_path = config_path_from_flags(&lp, flags); + if (!config_path) + return -ENXIO; + + return do_unit_file_disable(&lp, scope, flags, config_path, files, changes, n_changes); +} + +static int normalize_linked_files( + LookupScope scope, + const LookupPaths *lp, + char **names_or_paths, + char ***ret_names, + char ***ret_files) { + + /* This is similar to normalize_filenames()/normalize_names() in src/systemctl/, + * but operates on real unit names. For each argument we we look up the actual path + * where the unit is found. This way linked units can be reenabled successfully. */ + + _cleanup_free_ char **files = NULL, **names = NULL; + int r; + + STRV_FOREACH(a, names_or_paths) { + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + UnitFileInstallInfo *i = NULL; + _cleanup_free_ char *n = NULL; + + r = path_extract_filename(*a, &n); + if (r < 0) + return r; + if (r == O_DIRECTORY) + return log_debug_errno(SYNTHETIC_ERRNO(EISDIR), + "Unexpected path to a directory \"%s\", refusing.", *a); + + if (!is_path(*a)) { + r = install_info_discover(&ctx, lp, n, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, &i, NULL, NULL); + if (r < 0) + log_debug_errno(r, "Failed to discover unit \"%s\", operating on name: %m", n); + } + + r = strv_consume(&names, TAKE_PTR(n)); + if (r < 0) + return r; + + const char *p = NULL; + if (i && i->path) + /* Use startswith here, because we know that paths are normalized, and + * path_startswith() would give us a relative path, but we need an absolute path + * relative to i->root. + * + * In other words: /var/tmp/instroot.1234/etc/systemd/system/frobnicator.service + * is replaced by /etc/systemd/system/frobnicator.service, which is "absolute" + * in a sense, but only makes sense "relative" to /var/tmp/instroot.1234/. + */ + p = startswith(i->path, i->root); + + r = strv_extend(&files, p ?: *a); + if (r < 0) + return r; + } + + *ret_names = TAKE_PTR(names); + *ret_files = TAKE_PTR(files); + return 0; +} + +int unit_file_reenable( + LookupScope scope, + UnitFileFlags flags, + const char *root_dir, + char **names_or_paths, + UnitFileChange **changes, + size_t *n_changes) { + + _cleanup_(lookup_paths_free) LookupPaths lp = {}; + _cleanup_strv_free_ char **names = NULL, **files = NULL; + int r; + + assert(scope >= 0); + assert(scope < _LOOKUP_SCOPE_MAX); + + r = lookup_paths_init(&lp, scope, 0, root_dir); + if (r < 0) + return r; + + const char *config_path = config_path_from_flags(&lp, flags); + if (!config_path) + return -ENXIO; + + r = normalize_linked_files(scope, &lp, names_or_paths, &names, &files); + if (r < 0) + return r; /* First, we invoke the disable command with only the basename... */ - l = strv_length(files); - n = newa(char*, l+1); - for (i = 0; i < l; i++) - n[i] = basename(files[i]); - n[i] = NULL; - - r = unit_file_disable(scope, flags, root_dir, n, changes, n_changes); + r = do_unit_file_disable(&lp, scope, flags, config_path, names, changes, n_changes); if (r < 0) return r; /* But the enable command with the full name */ - return unit_file_enable(scope, flags, root_dir, files, changes, n_changes); + return do_unit_file_enable(&lp, scope, flags, config_path, files, changes, n_changes); } int unit_file_set_default( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, const char *name, UnitFileChange **changes, size_t *n_changes) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + UnitFileInstallInfo *info; const char *new_path; int r; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); assert(name); if (unit_name_to_type(name) != UNIT_TARGET) /* this also validates the name */ @@ -2757,46 +2905,46 @@ int unit_file_set_default( if (streq(name, SPECIAL_DEFAULT_TARGET)) return -EINVAL; - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; - r = install_info_discover_and_check(scope, &c, &paths, name, 0, &i, changes, n_changes); + r = install_info_discover_and_check(&ctx, &lp, name, 0, &info, changes, n_changes); if (r < 0) return r; - new_path = strjoina(paths.persistent_config, "/" SPECIAL_DEFAULT_TARGET); - return create_symlink(&paths, i->path, new_path, !!(flags & UNIT_FILE_FORCE), changes, n_changes); + new_path = strjoina(lp.persistent_config, "/" SPECIAL_DEFAULT_TARGET); + return create_symlink(&lp, info->name, new_path, flags & UNIT_FILE_FORCE, changes, n_changes); } int unit_file_get_default( - UnitFileScope scope, + LookupScope scope, const char *root_dir, char **name) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + UnitFileInstallInfo *info; char *n; int r; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); assert(name); - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; - r = install_info_discover(scope, &c, &paths, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, NULL, NULL); + r = install_info_discover(&ctx, &lp, SPECIAL_DEFAULT_TARGET, SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, NULL, NULL); if (r < 0) return r; - r = install_info_may_process(i, &paths, NULL, 0); + r = install_info_may_process(info, &lp, NULL, 0); if (r < 0) return r; - n = strdup(i->name); + n = strdup(info->name); if (!n) return -ENOMEM; @@ -2805,39 +2953,39 @@ int unit_file_get_default( } int unit_file_lookup_state( - UnitFileScope scope, - const LookupPaths *paths, + LookupScope scope, + const LookupPaths *lp, const char *name, UnitFileState *ret) { - _cleanup_(install_context_done) InstallContext c = {}; - UnitFileInstallInfo *i; + _cleanup_(install_context_done) InstallContext ctx = { .scope = scope }; + UnitFileInstallInfo *info; UnitFileState state; int r; - assert(paths); + assert(lp); assert(name); if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - r = install_info_discover(scope, &c, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, NULL, NULL); + r = install_info_discover(&ctx, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, NULL, NULL); if (r < 0) return log_debug_errno(r, "Failed to discover unit %s: %m", name); - assert(IN_SET(i->type, UNIT_FILE_TYPE_REGULAR, UNIT_FILE_TYPE_MASKED)); - log_debug("Found unit %s at %s (%s)", name, strna(i->path), - i->type == UNIT_FILE_TYPE_REGULAR ? "regular file" : "mask"); + assert(IN_SET(info->type, UNIT_FILE_TYPE_REGULAR, UNIT_FILE_TYPE_MASKED)); + log_debug("Found unit %s at %s (%s)", name, strna(info->path), + info->type == UNIT_FILE_TYPE_REGULAR ? "regular file" : "mask"); /* Shortcut things, if the caller just wants to know if this unit exists. */ if (!ret) return 0; - switch (i->type) { + switch (info->type) { case UNIT_FILE_TYPE_MASKED: - r = path_is_runtime(paths, i->path, true); + r = path_is_runtime(lp, info->path, true); if (r < 0) return r; @@ -2846,12 +2994,12 @@ int unit_file_lookup_state( case UNIT_FILE_TYPE_REGULAR: /* Check if the name we were querying is actually an alias */ - if (!streq(name, basename(i->path)) && !unit_name_is_valid(i->name, UNIT_NAME_INSTANCE)) { + if (!streq(name, basename(info->path)) && !unit_name_is_valid(info->name, UNIT_NAME_INSTANCE)) { state = UNIT_FILE_ALIAS; break; } - r = path_is_generator(paths, i->path); + r = path_is_generator(lp, info->path); if (r < 0) return r; if (r > 0) { @@ -2859,7 +3007,7 @@ int unit_file_lookup_state( break; } - r = path_is_transient(paths, i->path); + r = path_is_transient(lp, info->path); if (r < 0) return r; if (r > 0) { @@ -2870,7 +3018,7 @@ int unit_file_lookup_state( /* Check if any of the Alias= symlinks have been created. * We ignore other aliases, and only check those that would * be created by systemctl enable for this unit. */ - r = find_symlinks_in_scope(scope, paths, i, true, &state); + r = find_symlinks_in_scope(scope, lp, info, true, &state); if (r < 0) return r; if (r > 0) @@ -2878,15 +3026,15 @@ int unit_file_lookup_state( /* Check if the file is known under other names. If it is, * it might be in use. Report that as UNIT_FILE_INDIRECT. */ - r = find_symlinks_in_scope(scope, paths, i, false, &state); + r = find_symlinks_in_scope(scope, lp, info, false, &state); if (r < 0) return r; if (r > 0) state = UNIT_FILE_INDIRECT; else { - if (unit_file_install_info_has_rules(i)) + if (unit_file_install_info_has_rules(info)) state = UNIT_FILE_DISABLED; - else if (unit_file_install_info_has_also(i)) + else if (unit_file_install_info_has_also(info)) state = UNIT_FILE_INDIRECT; else state = UNIT_FILE_STATIC; @@ -2903,36 +3051,36 @@ int unit_file_lookup_state( } int unit_file_get_state( - UnitFileScope scope, + LookupScope scope, const char *root_dir, const char *name, UnitFileState *ret) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; int r; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); assert(name); - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; - return unit_file_lookup_state(scope, &paths, name, ret); + return unit_file_lookup_state(scope, &lp, name, ret); } -int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name) { - _cleanup_(install_context_done) InstallContext c = {}; +int unit_file_exists(LookupScope scope, const LookupPaths *lp, const char *name) { + _cleanup_(install_context_done) InstallContext c = { .scope = scope }; int r; - assert(paths); + assert(lp); assert(name); if (!unit_name_is_valid(name, UNIT_NAME_ANY)) return -EINVAL; - r = install_info_discover(scope, &c, paths, name, 0, NULL, NULL, NULL); + r = install_info_discover(&c, lp, name, 0, NULL, NULL, NULL); if (r == -ENOENT) return 0; if (r < 0) @@ -2974,17 +3122,17 @@ static int split_pattern_into_name_and_instances(const char *pattern, char **out return 0; } -static int presets_find_config(UnitFileScope scope, const char *root_dir, char ***files) { +static int presets_find_config(LookupScope scope, const char *root_dir, char ***files) { static const char* const system_dirs[] = {CONF_PATHS("systemd/system-preset"), NULL}; static const char* const user_dirs[] = {CONF_PATHS_USR("systemd/user-preset"), NULL}; const char* const* dirs; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); - if (scope == UNIT_FILE_SYSTEM) + if (scope == LOOKUP_SCOPE_SYSTEM) dirs = system_dirs; - else if (IN_SET(scope, UNIT_FILE_GLOBAL, UNIT_FILE_USER)) + else if (IN_SET(scope, LOOKUP_SCOPE_GLOBAL, LOOKUP_SCOPE_USER)) dirs = user_dirs; else assert_not_reached(); @@ -2992,14 +3140,13 @@ static int presets_find_config(UnitFileScope scope, const char *root_dir, char * return conf_files_list_strv(files, ".preset", root_dir, 0, dirs); } -static int read_presets(UnitFileScope scope, const char *root_dir, UnitFilePresets *presets) { +static int read_presets(LookupScope scope, const char *root_dir, UnitFilePresets *presets) { _cleanup_(unit_file_presets_freep) UnitFilePresets ps = {}; _cleanup_strv_free_ char **files = NULL; - char **p; int r; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); assert(presets); r = presets_find_config(scope, root_dir, &files); @@ -3113,7 +3260,6 @@ static int pattern_match_multiple_instances( if (unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) { _cleanup_strv_free_ char **out_strv = NULL; - char **iter; STRV_FOREACH(iter, rule.instances) { _cleanup_free_ char *name = NULL; @@ -3160,11 +3306,10 @@ static int query_presets(const char *name, const UnitFilePresets *presets, char log_debug("Preset files don't specify rule for %s. Enabling.", name); return 1; case PRESET_ENABLE: - if (instance_name_list && *instance_name_list) { - char **s; + if (instance_name_list && *instance_name_list) STRV_FOREACH(s, *instance_name_list) log_debug("Preset files say enable %s.", *s); - } else + else log_debug("Preset files say enable %s.", name); return 1; case PRESET_DISABLE: @@ -3175,7 +3320,7 @@ static int query_presets(const char *name, const UnitFilePresets *presets, char } } -int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) { +int unit_file_query_preset(LookupScope scope, const char *root_dir, const char *name, UnitFilePresets *cached) { _cleanup_(unit_file_presets_freep) UnitFilePresets tmp = {}; int r; @@ -3191,11 +3336,10 @@ int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char } static int execute_preset( - UnitFileScope scope, UnitFileFlags file_flags, InstallContext *plus, InstallContext *minus, - const LookupPaths *paths, + const LookupPaths *lp, const char *config_path, char **files, UnitFilePresetMode mode, @@ -3206,17 +3350,17 @@ static int execute_preset( assert(plus); assert(minus); - assert(paths); + assert(lp); assert(config_path); if (mode != UNIT_FILE_PRESET_ENABLE_ONLY) { _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; - r = install_context_mark_for_removal(scope, minus, paths, &remove_symlinks_to, config_path, changes, n_changes); + r = install_context_mark_for_removal(minus, lp, &remove_symlinks_to, config_path, changes, n_changes); if (r < 0) return r; - r = remove_marked_symlinks(remove_symlinks_to, config_path, paths, false, changes, n_changes); + r = remove_marked_symlinks(remove_symlinks_to, config_path, lp, false, changes, n_changes); } else r = 0; @@ -3224,9 +3368,10 @@ static int execute_preset( int q; /* Returns number of symlinks that where supposed to be installed. */ - q = install_context_apply(scope, + q = install_context_apply(plus, lp, file_flags | UNIT_FILE_IGNORE_AUXILIARY_FAILURE, - plus, paths, config_path, SEARCH_LOAD, changes, n_changes); + config_path, + SEARCH_LOAD, changes, n_changes); if (r >= 0) { if (q < 0) r = q; @@ -3239,29 +3384,29 @@ static int execute_preset( } static int preset_prepare_one( - UnitFileScope scope, + LookupScope scope, InstallContext *plus, InstallContext *minus, - LookupPaths *paths, + LookupPaths *lp, const char *name, const UnitFilePresets *presets, UnitFileChange **changes, size_t *n_changes) { - _cleanup_(install_context_done) InstallContext tmp = {}; + _cleanup_(install_context_done) InstallContext tmp = { .scope = scope }; _cleanup_strv_free_ char **instance_name_list = NULL; - UnitFileInstallInfo *i; + UnitFileInstallInfo *info; int r; if (install_info_find(plus, name) || install_info_find(minus, name)) return 0; - r = install_info_discover(scope, &tmp, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + r = install_info_discover(&tmp, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); if (r < 0) return r; - if (!streq(name, i->name)) { - log_debug("Skipping %s because it is an alias for %s.", name, i->name); + if (!streq(name, info->name)) { + log_debug("Skipping %s because it is an alias for %s.", name, info->name); return 0; } @@ -3270,30 +3415,29 @@ static int preset_prepare_one( return r; if (r > 0) { - if (instance_name_list) { - char **s; + if (instance_name_list) STRV_FOREACH(s, instance_name_list) { - r = install_info_discover_and_check(scope, plus, paths, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + r = install_info_discover_and_check(plus, lp, *s, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); if (r < 0) return r; } - } else { - r = install_info_discover_and_check(scope, plus, paths, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + else { + r = install_info_discover_and_check(plus, lp, name, SEARCH_LOAD|SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); if (r < 0) return r; } } else - r = install_info_discover(scope, minus, paths, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, - &i, changes, n_changes); + r = install_info_discover(minus, lp, name, SEARCH_FOLLOW_CONFIG_SYMLINKS, + &info, changes, n_changes); return r; } int unit_file_preset( - UnitFileScope scope, + LookupScope scope, UnitFileFlags file_flags, const char *root_dir, char **files, @@ -3302,21 +3446,20 @@ int unit_file_preset( size_t *n_changes) { _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; - _cleanup_(lookup_paths_free) LookupPaths paths = {}; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {}; const char *config_path; - char **i; int r; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; - config_path = (file_flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + config_path = (file_flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config; if (!config_path) return -ENXIO; @@ -3325,16 +3468,16 @@ int unit_file_preset( return r; STRV_FOREACH(i, files) { - r = preset_prepare_one(scope, &plus, &minus, &paths, *i, &presets, changes, n_changes); + r = preset_prepare_one(scope, &plus, &minus, &lp, *i, &presets, changes, n_changes); if (r < 0) return r; } - return execute_preset(scope, file_flags, &plus, &minus, &paths, config_path, files, mode, changes, n_changes); + return execute_preset(file_flags, &plus, &minus, &lp, config_path, files, mode, changes, n_changes); } int unit_file_preset_all( - UnitFileScope scope, + LookupScope scope, UnitFileFlags file_flags, const char *root_dir, UnitFilePresetMode mode, @@ -3342,21 +3485,20 @@ int unit_file_preset_all( size_t *n_changes) { _cleanup_(install_context_done) InstallContext plus = {}, minus = {}; - _cleanup_(lookup_paths_free) LookupPaths paths = {}; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; _cleanup_(unit_file_presets_freep) UnitFilePresets presets = {}; const char *config_path = NULL; - char **i; int r; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); assert(mode < _UNIT_FILE_PRESET_MAX); - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; - config_path = (file_flags & UNIT_FILE_RUNTIME) ? paths.runtime_config : paths.persistent_config; + config_path = (file_flags & UNIT_FILE_RUNTIME) ? lp.runtime_config : lp.persistent_config; if (!config_path) return -ENXIO; @@ -3364,7 +3506,7 @@ int unit_file_preset_all( if (r < 0) return r; - STRV_FOREACH(i, paths.search_path) { + STRV_FOREACH(i, lp.search_path) { _cleanup_closedir_ DIR *d = NULL; d = opendir(*i); @@ -3383,16 +3525,16 @@ int unit_file_preset_all( if (!IN_SET(de->d_type, DT_LNK, DT_REG)) continue; - r = preset_prepare_one(scope, &plus, &minus, &paths, de->d_name, &presets, changes, n_changes); + r = preset_prepare_one(scope, &plus, &minus, &lp, de->d_name, &presets, changes, n_changes); if (r < 0 && - !IN_SET(r, -EEXIST, -ERFKILL, -EADDRNOTAVAIL, -EIDRM, -EUCLEAN, -ELOOP, -ENOENT)) + !IN_SET(r, -EEXIST, -ERFKILL, -EADDRNOTAVAIL, -EBADSLT, -EIDRM, -EUCLEAN, -ELOOP, -ENOENT, -EUNATCH, -EXDEV)) /* Ignore generated/transient/missing/invalid units when applying preset, propagate other errors. * Coordinate with unit_file_dump_changes() above. */ return r; } } - return execute_preset(scope, file_flags, &plus, &minus, &paths, config_path, NULL, mode, changes, n_changes); + return execute_preset(file_flags, &plus, &minus, &lp, config_path, NULL, mode, changes, n_changes); } static UnitFileList* unit_file_list_free_one(UnitFileList *f) { @@ -3410,25 +3552,24 @@ Hashmap* unit_file_list_free(Hashmap *h) { DEFINE_TRIVIAL_CLEANUP_FUNC(UnitFileList*, unit_file_list_free_one); int unit_file_get_list( - UnitFileScope scope, + LookupScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns) { - _cleanup_(lookup_paths_free) LookupPaths paths = {}; - char **dirname; + _cleanup_(lookup_paths_free) LookupPaths lp = {}; int r; assert(scope >= 0); - assert(scope < _UNIT_FILE_SCOPE_MAX); + assert(scope < _LOOKUP_SCOPE_MAX); assert(h); - r = lookup_paths_init(&paths, scope, 0, root_dir); + r = lookup_paths_init(&lp, scope, 0, root_dir); if (r < 0) return r; - STRV_FOREACH(dirname, paths.search_path) { + STRV_FOREACH(dirname, lp.search_path) { _cleanup_closedir_ DIR *d = NULL; d = opendir(*dirname); @@ -3466,7 +3607,7 @@ int unit_file_get_list( if (!f->path) return -ENOMEM; - r = unit_file_lookup_state(scope, &paths, de->d_name, &f->state); + r = unit_file_lookup_state(scope, &lp, de->d_name, &f->state); if (r < 0) f->state = UNIT_FILE_BAD; diff --git a/src/shared/install.h b/src/shared/install.h index cdc543503..2ba7e8aea 100644 --- a/src/shared/install.h +++ b/src/shared/install.h @@ -15,6 +15,7 @@ typedef struct UnitFileInstallInfo UnitFileInstallInfo; #include "macro.h" #include "path-lookup.h" #include "strv.h" +#include "unit-file.h" #include "unit-name.h" enum UnitFilePresetMode { @@ -70,7 +71,8 @@ struct UnitFileList { enum UnitFileType { UNIT_FILE_TYPE_REGULAR, - UNIT_FILE_TYPE_SYMLINK, + UNIT_FILE_TYPE_LINKED, + UNIT_FILE_TYPE_ALIAS, UNIT_FILE_TYPE_MASKED, _UNIT_FILE_TYPE_MAX, _UNIT_FILE_TYPE_INVALID = -EINVAL, @@ -94,28 +96,28 @@ struct UnitFileInstallInfo { }; int unit_file_enable( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_disable( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_reenable( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, - char **files, + char **names_or_paths, UnitFileChange **changes, size_t *n_changes); int unit_file_preset( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, @@ -123,52 +125,52 @@ int unit_file_preset( UnitFileChange **changes, size_t *n_changes); int unit_file_preset_all( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, UnitFilePresetMode mode, UnitFileChange **changes, size_t *n_changes); int unit_file_mask( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_unmask( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_link( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_revert( - UnitFileScope scope, + LookupScope scope, const char *root_dir, char **files, UnitFileChange **changes, size_t *n_changes); int unit_file_set_default( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, const char *file, UnitFileChange **changes, size_t *n_changes); int unit_file_get_default( - UnitFileScope scope, + LookupScope scope, const char *root_dir, char **name); int unit_file_add_dependency( - UnitFileScope scope, + LookupScope scope, UnitFileFlags flags, const char *root_dir, char **files, @@ -178,22 +180,27 @@ int unit_file_add_dependency( size_t *n_changes); int unit_file_lookup_state( - UnitFileScope scope, + LookupScope scope, const LookupPaths *paths, const char *name, UnitFileState *ret); -int unit_file_get_state(UnitFileScope scope, const char *root_dir, const char *filename, UnitFileState *ret); -int unit_file_exists(UnitFileScope scope, const LookupPaths *paths, const char *name); +int unit_file_get_state(LookupScope scope, const char *root_dir, const char *filename, UnitFileState *ret); +int unit_file_exists(LookupScope scope, const LookupPaths *paths, const char *name); -int unit_file_get_list(UnitFileScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns); +int unit_file_get_list(LookupScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns); Hashmap* unit_file_list_free(Hashmap *h); int unit_file_changes_add(UnitFileChange **changes, size_t *n_changes, int type, const char *path, const char *source); void unit_file_changes_free(UnitFileChange *changes, size_t n_changes); void unit_file_dump_changes(int r, const char *verb, const UnitFileChange *changes, size_t n_changes, bool quiet); -int unit_file_verify_alias(const UnitFileInstallInfo *i, const char *dst, char **ret_dst); +int unit_file_verify_alias( + const UnitFileInstallInfo *info, + const char *dst, + char **ret_dst, + UnitFileChange **changes, + size_t *n_changes); typedef struct UnitFilePresetRule UnitFilePresetRule; @@ -204,7 +211,7 @@ typedef struct { } UnitFilePresets; void unit_file_presets_freep(UnitFilePresets *p); -int unit_file_query_preset(UnitFileScope scope, const char *root_dir, const char *name, UnitFilePresets *cached); +int unit_file_query_preset(LookupScope scope, const char *root_dir, const char *name, UnitFilePresets *cached); const char *unit_file_state_to_string(UnitFileState s) _const_; UnitFileState unit_file_state_from_string(const char *s) _pure_; diff --git a/src/shared/libcrypt-util.c b/src/shared/libcrypt-util.c index 5b315413a..0d72032f5 100644 --- a/src/shared/libcrypt-util.c +++ b/src/shared/libcrypt-util.c @@ -197,7 +197,6 @@ int test_password_one(const char *hashed_password, const char *password) { } int test_password_many(char **hashed_password, const char *password) { - char **hpw; int r; STRV_FOREACH(hpw, hashed_password) { diff --git a/src/shared/libfido2-util.c b/src/shared/libfido2-util.c index 87b88f04d..8c9fa88e3 100644 --- a/src/shared/libfido2-util.c +++ b/src/shared/libfido2-util.c @@ -313,8 +313,6 @@ static int fido2_use_hmac_hash_specific_token( bool retry_with_up = false, retry_with_pin = false; if (FLAGS_SET(required, FIDO2ENROLL_PIN)) { - char **i; - /* OK, we need a pin, try with all pins in turn */ if (strv_isempty(pins)) r = FIDO_ERR_PIN_REQUIRED; @@ -683,7 +681,6 @@ int fido2_generate_hmac_hash( for (;;) { _cleanup_(strv_free_erasep) char **pin = NULL; - char **i; r = ask_password_auto("Please enter security token PIN:", askpw_icon_name, NULL, "fido2-pin", "fido2-pin", USEC_INFINITY, 0, &pin); if (r < 0) diff --git a/src/shared/linux/bpf.h b/src/shared/linux/bpf.h index e6ceac3f7..ba5af15e2 100644 --- a/src/shared/linux/bpf.h +++ b/src/shared/linux/bpf.h @@ -19,7 +19,8 @@ /* ld/ldx fields */ #define BPF_DW 0x18 /* double word (64-bit) */ -#define BPF_XADD 0xc0 /* exclusive add */ +#define BPF_ATOMIC 0xc0 /* atomic memory ops - op type in immediate */ +#define BPF_XADD 0xc0 /* exclusive add - legacy name */ /* alu/jmp fields */ #define BPF_MOV 0xb0 /* mov reg to reg */ @@ -43,6 +44,11 @@ #define BPF_CALL 0x80 /* function call */ #define BPF_EXIT 0x90 /* function return */ +/* atomic op type fields (stored in immediate) */ +#define BPF_FETCH 0x01 /* not an opcode on its own, used to build others */ +#define BPF_XCHG (0xe0 | BPF_FETCH) /* atomic exchange */ +#define BPF_CMPXCHG (0xf0 | BPF_FETCH) /* atomic compare-and-write */ + /* Register numbers */ enum { BPF_REG_0 = 0, @@ -78,7 +84,7 @@ struct bpf_lpm_trie_key { struct bpf_cgroup_storage_key { __u64 cgroup_inode_id; /* cgroup inode id */ - __u32 attach_type; /* program attach type */ + __u32 attach_type; /* program attach type (enum bpf_attach_type) */ }; union bpf_iter_link_info { @@ -87,7 +93,748 @@ union bpf_iter_link_info { } map; }; -/* BPF syscall commands, see bpf(2) man-page for details. */ +/* BPF syscall commands, see bpf(2) man-page for more details. */ +/** + * DOC: eBPF Syscall Preamble + * + * The operation to be performed by the **bpf**\ () system call is determined + * by the *cmd* argument. Each operation takes an accompanying argument, + * provided via *attr*, which is a pointer to a union of type *bpf_attr* (see + * below). The size argument is the size of the union pointed to by *attr*. + */ +/** + * DOC: eBPF Syscall Commands + * + * BPF_MAP_CREATE + * Description + * Create a map and return a file descriptor that refers to the + * map. The close-on-exec file descriptor flag (see **fcntl**\ (2)) + * is automatically enabled for the new file descriptor. + * + * Applying **close**\ (2) to the file descriptor returned by + * **BPF_MAP_CREATE** will delete the map (but see NOTES). + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_MAP_LOOKUP_ELEM + * Description + * Look up an element with a given *key* in the map referred to + * by the file descriptor *map_fd*. + * + * The *flags* argument may be specified as one of the + * following: + * + * **BPF_F_LOCK** + * Look up the value of a spin-locked map without + * returning the lock. This must be specified if the + * elements contain a spinlock. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_MAP_UPDATE_ELEM + * Description + * Create or update an element (key/value pair) in a specified map. + * + * The *flags* argument should be specified as one of the + * following: + * + * **BPF_ANY** + * Create a new element or update an existing element. + * **BPF_NOEXIST** + * Create a new element only if it did not exist. + * **BPF_EXIST** + * Update an existing element. + * **BPF_F_LOCK** + * Update a spin_lock-ed map element. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * May set *errno* to **EINVAL**, **EPERM**, **ENOMEM**, + * **E2BIG**, **EEXIST**, or **ENOENT**. + * + * **E2BIG** + * The number of elements in the map reached the + * *max_entries* limit specified at map creation time. + * **EEXIST** + * If *flags* specifies **BPF_NOEXIST** and the element + * with *key* already exists in the map. + * **ENOENT** + * If *flags* specifies **BPF_EXIST** and the element with + * *key* does not exist in the map. + * + * BPF_MAP_DELETE_ELEM + * Description + * Look up and delete an element by key in a specified map. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_MAP_GET_NEXT_KEY + * Description + * Look up an element by key in a specified map and return the key + * of the next element. Can be used to iterate over all elements + * in the map. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * The following cases can be used to iterate over all elements of + * the map: + * + * * If *key* is not found, the operation returns zero and sets + * the *next_key* pointer to the key of the first element. + * * If *key* is found, the operation returns zero and sets the + * *next_key* pointer to the key of the next element. + * * If *key* is the last element, returns -1 and *errno* is set + * to **ENOENT**. + * + * May set *errno* to **ENOMEM**, **EFAULT**, **EPERM**, or + * **EINVAL** on error. + * + * BPF_PROG_LOAD + * Description + * Verify and load an eBPF program, returning a new file + * descriptor associated with the program. + * + * Applying **close**\ (2) to the file descriptor returned by + * **BPF_PROG_LOAD** will unload the eBPF program (but see NOTES). + * + * The close-on-exec file descriptor flag (see **fcntl**\ (2)) is + * automatically enabled for the new file descriptor. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_OBJ_PIN + * Description + * Pin an eBPF program or map referred by the specified *bpf_fd* + * to the provided *pathname* on the filesystem. + * + * The *pathname* argument must not contain a dot ("."). + * + * On success, *pathname* retains a reference to the eBPF object, + * preventing deallocation of the object when the original + * *bpf_fd* is closed. This allow the eBPF object to live beyond + * **close**\ (\ *bpf_fd*\ ), and hence the lifetime of the parent + * process. + * + * Applying **unlink**\ (2) or similar calls to the *pathname* + * unpins the object from the filesystem, removing the reference. + * If no other file descriptors or filesystem nodes refer to the + * same object, it will be deallocated (see NOTES). + * + * The filesystem type for the parent directory of *pathname* must + * be **BPF_FS_MAGIC**. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_OBJ_GET + * Description + * Open a file descriptor for the eBPF object pinned to the + * specified *pathname*. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_PROG_ATTACH + * Description + * Attach an eBPF program to a *target_fd* at the specified + * *attach_type* hook. + * + * The *attach_type* specifies the eBPF attachment point to + * attach the program to, and must be one of *bpf_attach_type* + * (see below). + * + * The *attach_bpf_fd* must be a valid file descriptor for a + * loaded eBPF program of a cgroup, flow dissector, LIRC, sockmap + * or sock_ops type corresponding to the specified *attach_type*. + * + * The *target_fd* must be a valid file descriptor for a kernel + * object which depends on the attach type of *attach_bpf_fd*: + * + * **BPF_PROG_TYPE_CGROUP_DEVICE**, + * **BPF_PROG_TYPE_CGROUP_SKB**, + * **BPF_PROG_TYPE_CGROUP_SOCK**, + * **BPF_PROG_TYPE_CGROUP_SOCK_ADDR**, + * **BPF_PROG_TYPE_CGROUP_SOCKOPT**, + * **BPF_PROG_TYPE_CGROUP_SYSCTL**, + * **BPF_PROG_TYPE_SOCK_OPS** + * + * Control Group v2 hierarchy with the eBPF controller + * enabled. Requires the kernel to be compiled with + * **CONFIG_CGROUP_BPF**. + * + * **BPF_PROG_TYPE_FLOW_DISSECTOR** + * + * Network namespace (eg /proc/self/ns/net). + * + * **BPF_PROG_TYPE_LIRC_MODE2** + * + * LIRC device path (eg /dev/lircN). Requires the kernel + * to be compiled with **CONFIG_BPF_LIRC_MODE2**. + * + * **BPF_PROG_TYPE_SK_SKB**, + * **BPF_PROG_TYPE_SK_MSG** + * + * eBPF map of socket type (eg **BPF_MAP_TYPE_SOCKHASH**). + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_PROG_DETACH + * Description + * Detach the eBPF program associated with the *target_fd* at the + * hook specified by *attach_type*. The program must have been + * previously attached using **BPF_PROG_ATTACH**. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_PROG_TEST_RUN + * Description + * Run the eBPF program associated with the *prog_fd* a *repeat* + * number of times against a provided program context *ctx_in* and + * data *data_in*, and return the modified program context + * *ctx_out*, *data_out* (for example, packet data), result of the + * execution *retval*, and *duration* of the test run. + * + * The sizes of the buffers provided as input and output + * parameters *ctx_in*, *ctx_out*, *data_in*, and *data_out* must + * be provided in the corresponding variables *ctx_size_in*, + * *ctx_size_out*, *data_size_in*, and/or *data_size_out*. If any + * of these parameters are not provided (ie set to NULL), the + * corresponding size field must be zero. + * + * Some program types have particular requirements: + * + * **BPF_PROG_TYPE_SK_LOOKUP** + * *data_in* and *data_out* must be NULL. + * + * **BPF_PROG_TYPE_RAW_TRACEPOINT**, + * **BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE** + * + * *ctx_out*, *data_in* and *data_out* must be NULL. + * *repeat* must be zero. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * **ENOSPC** + * Either *data_size_out* or *ctx_size_out* is too small. + * **ENOTSUPP** + * This command is not supported by the program type of + * the program referred to by *prog_fd*. + * + * BPF_PROG_GET_NEXT_ID + * Description + * Fetch the next eBPF program currently loaded into the kernel. + * + * Looks for the eBPF program with an id greater than *start_id* + * and updates *next_id* on success. If no other eBPF programs + * remain with ids higher than *start_id*, returns -1 and sets + * *errno* to **ENOENT**. + * + * Return + * Returns zero on success. On error, or when no id remains, -1 + * is returned and *errno* is set appropriately. + * + * BPF_MAP_GET_NEXT_ID + * Description + * Fetch the next eBPF map currently loaded into the kernel. + * + * Looks for the eBPF map with an id greater than *start_id* + * and updates *next_id* on success. If no other eBPF maps + * remain with ids higher than *start_id*, returns -1 and sets + * *errno* to **ENOENT**. + * + * Return + * Returns zero on success. On error, or when no id remains, -1 + * is returned and *errno* is set appropriately. + * + * BPF_PROG_GET_FD_BY_ID + * Description + * Open a file descriptor for the eBPF program corresponding to + * *prog_id*. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_MAP_GET_FD_BY_ID + * Description + * Open a file descriptor for the eBPF map corresponding to + * *map_id*. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_OBJ_GET_INFO_BY_FD + * Description + * Obtain information about the eBPF object corresponding to + * *bpf_fd*. + * + * Populates up to *info_len* bytes of *info*, which will be in + * one of the following formats depending on the eBPF object type + * of *bpf_fd*: + * + * * **struct bpf_prog_info** + * * **struct bpf_map_info** + * * **struct bpf_btf_info** + * * **struct bpf_link_info** + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_PROG_QUERY + * Description + * Obtain information about eBPF programs associated with the + * specified *attach_type* hook. + * + * The *target_fd* must be a valid file descriptor for a kernel + * object which depends on the attach type of *attach_bpf_fd*: + * + * **BPF_PROG_TYPE_CGROUP_DEVICE**, + * **BPF_PROG_TYPE_CGROUP_SKB**, + * **BPF_PROG_TYPE_CGROUP_SOCK**, + * **BPF_PROG_TYPE_CGROUP_SOCK_ADDR**, + * **BPF_PROG_TYPE_CGROUP_SOCKOPT**, + * **BPF_PROG_TYPE_CGROUP_SYSCTL**, + * **BPF_PROG_TYPE_SOCK_OPS** + * + * Control Group v2 hierarchy with the eBPF controller + * enabled. Requires the kernel to be compiled with + * **CONFIG_CGROUP_BPF**. + * + * **BPF_PROG_TYPE_FLOW_DISSECTOR** + * + * Network namespace (eg /proc/self/ns/net). + * + * **BPF_PROG_TYPE_LIRC_MODE2** + * + * LIRC device path (eg /dev/lircN). Requires the kernel + * to be compiled with **CONFIG_BPF_LIRC_MODE2**. + * + * **BPF_PROG_QUERY** always fetches the number of programs + * attached and the *attach_flags* which were used to attach those + * programs. Additionally, if *prog_ids* is nonzero and the number + * of attached programs is less than *prog_cnt*, populates + * *prog_ids* with the eBPF program ids of the programs attached + * at *target_fd*. + * + * The following flags may alter the result: + * + * **BPF_F_QUERY_EFFECTIVE** + * Only return information regarding programs which are + * currently effective at the specified *target_fd*. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_RAW_TRACEPOINT_OPEN + * Description + * Attach an eBPF program to a tracepoint *name* to access kernel + * internal arguments of the tracepoint in their raw form. + * + * The *prog_fd* must be a valid file descriptor associated with + * a loaded eBPF program of type **BPF_PROG_TYPE_RAW_TRACEPOINT**. + * + * No ABI guarantees are made about the content of tracepoint + * arguments exposed to the corresponding eBPF program. + * + * Applying **close**\ (2) to the file descriptor returned by + * **BPF_RAW_TRACEPOINT_OPEN** will delete the map (but see NOTES). + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_BTF_LOAD + * Description + * Verify and load BPF Type Format (BTF) metadata into the kernel, + * returning a new file descriptor associated with the metadata. + * BTF is described in more detail at + * https://www.kernel.org/doc/html/latest/bpf/btf.html. + * + * The *btf* parameter must point to valid memory providing + * *btf_size* bytes of BTF binary metadata. + * + * The returned file descriptor can be passed to other **bpf**\ () + * subcommands such as **BPF_PROG_LOAD** or **BPF_MAP_CREATE** to + * associate the BTF with those objects. + * + * Similar to **BPF_PROG_LOAD**, **BPF_BTF_LOAD** has optional + * parameters to specify a *btf_log_buf*, *btf_log_size* and + * *btf_log_level* which allow the kernel to return freeform log + * output regarding the BTF verification process. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_BTF_GET_FD_BY_ID + * Description + * Open a file descriptor for the BPF Type Format (BTF) + * corresponding to *btf_id*. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_TASK_FD_QUERY + * Description + * Obtain information about eBPF programs associated with the + * target process identified by *pid* and *fd*. + * + * If the *pid* and *fd* are associated with a tracepoint, kprobe + * or uprobe perf event, then the *prog_id* and *fd_type* will + * be populated with the eBPF program id and file descriptor type + * of type **bpf_task_fd_type**. If associated with a kprobe or + * uprobe, the *probe_offset* and *probe_addr* will also be + * populated. Optionally, if *buf* is provided, then up to + * *buf_len* bytes of *buf* will be populated with the name of + * the tracepoint, kprobe or uprobe. + * + * The resulting *prog_id* may be introspected in deeper detail + * using **BPF_PROG_GET_FD_BY_ID** and **BPF_OBJ_GET_INFO_BY_FD**. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_MAP_LOOKUP_AND_DELETE_ELEM + * Description + * Look up an element with the given *key* in the map referred to + * by the file descriptor *fd*, and if found, delete the element. + * + * For **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map + * types, the *flags* argument needs to be set to 0, but for other + * map types, it may be specified as: + * + * **BPF_F_LOCK** + * Look up and delete the value of a spin-locked map + * without returning the lock. This must be specified if + * the elements contain a spinlock. + * + * The **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map types + * implement this command as a "pop" operation, deleting the top + * element rather than one corresponding to *key*. + * The *key* and *key_len* parameters should be zeroed when + * issuing this operation for these map types. + * + * This command is only valid for the following map types: + * * **BPF_MAP_TYPE_QUEUE** + * * **BPF_MAP_TYPE_STACK** + * * **BPF_MAP_TYPE_HASH** + * * **BPF_MAP_TYPE_PERCPU_HASH** + * * **BPF_MAP_TYPE_LRU_HASH** + * * **BPF_MAP_TYPE_LRU_PERCPU_HASH** + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_MAP_FREEZE + * Description + * Freeze the permissions of the specified map. + * + * Write permissions may be frozen by passing zero *flags*. + * Upon success, no future syscall invocations may alter the + * map state of *map_fd*. Write operations from eBPF programs + * are still possible for a frozen map. + * + * Not supported for maps of type **BPF_MAP_TYPE_STRUCT_OPS**. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_BTF_GET_NEXT_ID + * Description + * Fetch the next BPF Type Format (BTF) object currently loaded + * into the kernel. + * + * Looks for the BTF object with an id greater than *start_id* + * and updates *next_id* on success. If no other BTF objects + * remain with ids higher than *start_id*, returns -1 and sets + * *errno* to **ENOENT**. + * + * Return + * Returns zero on success. On error, or when no id remains, -1 + * is returned and *errno* is set appropriately. + * + * BPF_MAP_LOOKUP_BATCH + * Description + * Iterate and fetch multiple elements in a map. + * + * Two opaque values are used to manage batch operations, + * *in_batch* and *out_batch*. Initially, *in_batch* must be set + * to NULL to begin the batched operation. After each subsequent + * **BPF_MAP_LOOKUP_BATCH**, the caller should pass the resultant + * *out_batch* as the *in_batch* for the next operation to + * continue iteration from the current point. + * + * The *keys* and *values* are output parameters which must point + * to memory large enough to hold *count* items based on the key + * and value size of the map *map_fd*. The *keys* buffer must be + * of *key_size* * *count*. The *values* buffer must be of + * *value_size* * *count*. + * + * The *elem_flags* argument may be specified as one of the + * following: + * + * **BPF_F_LOCK** + * Look up the value of a spin-locked map without + * returning the lock. This must be specified if the + * elements contain a spinlock. + * + * On success, *count* elements from the map are copied into the + * user buffer, with the keys copied into *keys* and the values + * copied into the corresponding indices in *values*. + * + * If an error is returned and *errno* is not **EFAULT**, *count* + * is set to the number of successfully processed elements. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * May set *errno* to **ENOSPC** to indicate that *keys* or + * *values* is too small to dump an entire bucket during + * iteration of a hash-based map type. + * + * BPF_MAP_LOOKUP_AND_DELETE_BATCH + * Description + * Iterate and delete all elements in a map. + * + * This operation has the same behavior as + * **BPF_MAP_LOOKUP_BATCH** with two exceptions: + * + * * Every element that is successfully returned is also deleted + * from the map. This is at least *count* elements. Note that + * *count* is both an input and an output parameter. + * * Upon returning with *errno* set to **EFAULT**, up to + * *count* elements may be deleted without returning the keys + * and values of the deleted elements. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_MAP_UPDATE_BATCH + * Description + * Update multiple elements in a map by *key*. + * + * The *keys* and *values* are input parameters which must point + * to memory large enough to hold *count* items based on the key + * and value size of the map *map_fd*. The *keys* buffer must be + * of *key_size* * *count*. The *values* buffer must be of + * *value_size* * *count*. + * + * Each element specified in *keys* is sequentially updated to the + * value in the corresponding index in *values*. The *in_batch* + * and *out_batch* parameters are ignored and should be zeroed. + * + * The *elem_flags* argument should be specified as one of the + * following: + * + * **BPF_ANY** + * Create new elements or update a existing elements. + * **BPF_NOEXIST** + * Create new elements only if they do not exist. + * **BPF_EXIST** + * Update existing elements. + * **BPF_F_LOCK** + * Update spin_lock-ed map elements. This must be + * specified if the map value contains a spinlock. + * + * On success, *count* elements from the map are updated. + * + * If an error is returned and *errno* is not **EFAULT**, *count* + * is set to the number of successfully processed elements. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * May set *errno* to **EINVAL**, **EPERM**, **ENOMEM**, or + * **E2BIG**. **E2BIG** indicates that the number of elements in + * the map reached the *max_entries* limit specified at map + * creation time. + * + * May set *errno* to one of the following error codes under + * specific circumstances: + * + * **EEXIST** + * If *flags* specifies **BPF_NOEXIST** and the element + * with *key* already exists in the map. + * **ENOENT** + * If *flags* specifies **BPF_EXIST** and the element with + * *key* does not exist in the map. + * + * BPF_MAP_DELETE_BATCH + * Description + * Delete multiple elements in a map by *key*. + * + * The *keys* parameter is an input parameter which must point + * to memory large enough to hold *count* items based on the key + * size of the map *map_fd*, that is, *key_size* * *count*. + * + * Each element specified in *keys* is sequentially deleted. The + * *in_batch*, *out_batch*, and *values* parameters are ignored + * and should be zeroed. + * + * The *elem_flags* argument may be specified as one of the + * following: + * + * **BPF_F_LOCK** + * Look up the value of a spin-locked map without + * returning the lock. This must be specified if the + * elements contain a spinlock. + * + * On success, *count* elements from the map are updated. + * + * If an error is returned and *errno* is not **EFAULT**, *count* + * is set to the number of successfully processed elements. If + * *errno* is **EFAULT**, up to *count* elements may be been + * deleted. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_LINK_CREATE + * Description + * Attach an eBPF program to a *target_fd* at the specified + * *attach_type* hook and return a file descriptor handle for + * managing the link. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_LINK_UPDATE + * Description + * Update the eBPF program in the specified *link_fd* to + * *new_prog_fd*. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_LINK_GET_FD_BY_ID + * Description + * Open a file descriptor for the eBPF Link corresponding to + * *link_id*. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_LINK_GET_NEXT_ID + * Description + * Fetch the next eBPF link currently loaded into the kernel. + * + * Looks for the eBPF link with an id greater than *start_id* + * and updates *next_id* on success. If no other eBPF links + * remain with ids higher than *start_id*, returns -1 and sets + * *errno* to **ENOENT**. + * + * Return + * Returns zero on success. On error, or when no id remains, -1 + * is returned and *errno* is set appropriately. + * + * BPF_ENABLE_STATS + * Description + * Enable eBPF runtime statistics gathering. + * + * Runtime statistics gathering for the eBPF runtime is disabled + * by default to minimize the corresponding performance overhead. + * This command enables statistics globally. + * + * Multiple programs may independently enable statistics. + * After gathering the desired statistics, eBPF runtime statistics + * may be disabled again by calling **close**\ (2) for the file + * descriptor returned by this function. Statistics will only be + * disabled system-wide when all outstanding file descriptors + * returned by prior calls for this subcommand are closed. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_ITER_CREATE + * Description + * Create an iterator on top of the specified *link_fd* (as + * previously created using **BPF_LINK_CREATE**) and return a + * file descriptor that can be used to trigger the iteration. + * + * If the resulting file descriptor is pinned to the filesystem + * using **BPF_OBJ_PIN**, then subsequent **read**\ (2) syscalls + * for that path will trigger the iterator to read kernel state + * using the eBPF program attached to *link_fd*. + * + * Return + * A new file descriptor (a nonnegative integer), or -1 if an + * error occurred (in which case, *errno* is set appropriately). + * + * BPF_LINK_DETACH + * Description + * Forcefully detach the specified *link_fd* from its + * corresponding attachment point. + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * BPF_PROG_BIND_MAP + * Description + * Bind a map to the lifetime of an eBPF program. + * + * The map identified by *map_fd* is bound to the program + * identified by *prog_fd* and only released when *prog_fd* is + * released. This may be used in cases where metadata should be + * associated with a program which otherwise does not contain any + * references to the map (for example, embedded in the eBPF + * program instructions). + * + * Return + * Returns zero on success. On error, -1 is returned and *errno* + * is set appropriately. + * + * NOTES + * eBPF objects (maps and programs) can be shared between processes. + * + * * After **fork**\ (2), the child inherits file descriptors + * referring to the same eBPF objects. + * * File descriptors referring to eBPF objects can be transferred over + * **unix**\ (7) domain sockets. + * * File descriptors referring to eBPF objects can be duplicated in the + * usual way, using **dup**\ (2) and similar calls. + * * File descriptors referring to eBPF objects can be pinned to the + * filesystem using the **BPF_OBJ_PIN** command of **bpf**\ (2). + * + * An eBPF object is deallocated only after all file descriptors referring + * to the object have been closed and no references remain pinned to the + * filesystem or attached (for example, bound to a program or device). + */ enum bpf_cmd { BPF_MAP_CREATE, BPF_MAP_LOOKUP_ELEM, @@ -100,6 +847,7 @@ enum bpf_cmd { BPF_PROG_ATTACH, BPF_PROG_DETACH, BPF_PROG_TEST_RUN, + BPF_PROG_RUN = BPF_PROG_TEST_RUN, BPF_PROG_GET_NEXT_ID, BPF_MAP_GET_NEXT_ID, BPF_PROG_GET_FD_BY_ID, @@ -157,6 +905,8 @@ enum bpf_map_type { BPF_MAP_TYPE_STRUCT_OPS, BPF_MAP_TYPE_RINGBUF, BPF_MAP_TYPE_INODE_STORAGE, + BPF_MAP_TYPE_TASK_STORAGE, + BPF_MAP_TYPE_BLOOM_FILTER, }; /* Note that tracing related programs such as @@ -199,6 +949,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_EXT, BPF_PROG_TYPE_LSM, BPF_PROG_TYPE_SK_LOOKUP, + BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */ }; enum bpf_attach_type { @@ -240,6 +991,10 @@ enum bpf_attach_type { BPF_XDP_CPUMAP, BPF_SK_LOOKUP, BPF_XDP, + BPF_SK_SKB_VERDICT, + BPF_SK_REUSEPORT_SELECT, + BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, + BPF_PERF_EVENT, __MAX_BPF_ATTACH_TYPE }; @@ -253,6 +1008,7 @@ enum bpf_link_type { BPF_LINK_TYPE_ITER = 4, BPF_LINK_TYPE_NETNS = 5, BPF_LINK_TYPE_XDP = 6, + BPF_LINK_TYPE_PERF_EVENT = 7, MAX_BPF_LINK_TYPE, }; @@ -358,8 +1114,8 @@ enum bpf_link_type { /* When BPF ldimm64's insn[0].src_reg != 0 then this can have * the following extensions: * - * insn[0].src_reg: BPF_PSEUDO_MAP_FD - * insn[0].imm: map fd + * insn[0].src_reg: BPF_PSEUDO_MAP_[FD|IDX] + * insn[0].imm: map fd or fd_idx * insn[1].imm: 0 * insn[0].off: 0 * insn[1].off: 0 @@ -367,15 +1123,19 @@ enum bpf_link_type { * verifier type: CONST_PTR_TO_MAP */ #define BPF_PSEUDO_MAP_FD 1 -/* insn[0].src_reg: BPF_PSEUDO_MAP_VALUE - * insn[0].imm: map fd +#define BPF_PSEUDO_MAP_IDX 5 + +/* insn[0].src_reg: BPF_PSEUDO_MAP_[IDX_]VALUE + * insn[0].imm: map fd or fd_idx * insn[1].imm: offset into value * insn[0].off: 0 * insn[1].off: 0 * ldimm64 rewrite: address of map[0]+offset * verifier type: PTR_TO_MAP_VALUE */ -#define BPF_PSEUDO_MAP_VALUE 2 +#define BPF_PSEUDO_MAP_VALUE 2 +#define BPF_PSEUDO_MAP_IDX_VALUE 6 + /* insn[0].src_reg: BPF_PSEUDO_BTF_ID * insn[0].imm: kernel btd id of VAR * insn[1].imm: 0 @@ -386,11 +1146,24 @@ enum bpf_link_type { * is struct/union. */ #define BPF_PSEUDO_BTF_ID 3 +/* insn[0].src_reg: BPF_PSEUDO_FUNC + * insn[0].imm: insn offset to the func + * insn[1].imm: 0 + * insn[0].off: 0 + * insn[1].off: 0 + * ldimm64 rewrite: address of the function + * verifier type: PTR_TO_FUNC. + */ +#define BPF_PSEUDO_FUNC 4 /* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative * offset to another bpf function */ #define BPF_PSEUDO_CALL 1 +/* when bpf_call->src_reg == BPF_PSEUDO_KFUNC_CALL, + * bpf_call->imm == btf_id of a BTF_KIND_FUNC in the running kernel + */ +#define BPF_PSEUDO_KFUNC_CALL 2 /* flags for BPF_MAP_UPDATE_ELEM command */ enum { @@ -502,6 +1275,13 @@ union bpf_attr { * struct stored as the * map value */ + /* Any per-map-type extra fields + * + * BPF_MAP_TYPE_BLOOM_FILTER - the lowest 4 bits indicate the + * number of hash functions (if 0, the bloom filter will default + * to using 5 hash functions). + */ + __u64 map_extra; }; struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ @@ -556,7 +1336,14 @@ union bpf_attr { __aligned_u64 line_info; /* line info */ __u32 line_info_cnt; /* number of bpf_line_info records */ __u32 attach_btf_id; /* in-kernel BTF type id to attach to */ - __u32 attach_prog_fd; /* 0 to attach to vmlinux */ + union { + /* valid prog_fd to attach to bpf prog */ + __u32 attach_prog_fd; + /* or valid module BTF object fd or 0 to attach to vmlinux */ + __u32 attach_btf_obj_fd; + }; + __u32 :32; /* pad */ + __aligned_u64 fd_array; /* array of FDs */ }; struct { /* anonymous struct used by BPF_OBJ_* commands */ @@ -669,6 +1456,13 @@ union bpf_attr { __aligned_u64 iter_info; /* extra bpf_iter_link_info */ __u32 iter_info_len; /* iter_info length */ }; + struct { + /* black box user-provided value passed through + * to BPF program at the execution time and + * accessible through bpf_get_attach_cookie() BPF helper + */ + __u64 bpf_cookie; + } perf_event; }; } link_create; @@ -708,7 +1502,7 @@ union bpf_attr { * parsed and used to produce a manual page. The workflow is the following, * and requires the rst2man utility: * - * $ ./scripts/bpf_helpers_doc.py \ + * $ ./scripts/bpf_doc.py \ * --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst * $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7 * $ man /tmp/bpf-helpers.7 @@ -843,7 +1637,7 @@ union bpf_attr { * u32 bpf_get_smp_processor_id(void) * Description * Get the SMP (symmetric multiprocessing) processor id. Note that - * all programs run with preemption disabled, which means that the + * all programs run with migration disabled, which means that the * SMP processor id is stable during all the execution of the * program. * Return @@ -1644,22 +2438,30 @@ union bpf_attr { * networking traffic statistics as it provides a global socket * identifier that can be assumed unique. * Return - * A 8-byte long non-decreasing number on success, or 0 if the - * socket field is missing inside *skb*. + * A 8-byte long unique number on success, or 0 if the socket + * field is missing inside *skb*. * * u64 bpf_get_socket_cookie(struct bpf_sock_addr *ctx) * Description * Equivalent to bpf_get_socket_cookie() helper that accepts * *skb*, but gets socket from **struct bpf_sock_addr** context. * Return - * A 8-byte long non-decreasing number. + * A 8-byte long unique number. * * u64 bpf_get_socket_cookie(struct bpf_sock_ops *ctx) * Description * Equivalent to **bpf_get_socket_cookie**\ () helper that accepts * *skb*, but gets socket from **struct bpf_sock_ops** context. * Return - * A 8-byte long non-decreasing number. + * A 8-byte long unique number. + * + * u64 bpf_get_socket_cookie(struct sock *sk) + * Description + * Equivalent to **bpf_get_socket_cookie**\ () helper that accepts + * *sk*, but gets socket from a BTF **struct sock**. This helper + * also works for sleepable programs. + * Return + * A 8-byte long unique number or 0 if *sk* is NULL. * * u32 bpf_get_socket_uid(struct sk_buff *skb) * Return @@ -1745,6 +2547,10 @@ union bpf_attr { * Use with ENCAP_L3/L4 flags to further specify the tunnel * type; *len* is the length of the inner MAC header. * + * * **BPF_F_ADJ_ROOM_ENCAP_L2_ETH**: + * Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the + * L2 type as Ethernet. + * * A call to this helper is susceptible to change the underlying * packet buffer. Therefore, at load time, all checks on pointers * previously done by the verifier are invalidated and must be @@ -1765,8 +2571,12 @@ union bpf_attr { * The lower two bits of *flags* are used as the return code if * the map lookup fails. This is so that the return value can be * one of the XDP program return codes up to **XDP_TX**, as chosen - * by the caller. Any higher bits in the *flags* argument must be - * unset. + * by the caller. The higher bits of *flags* can be set to + * BPF_F_BROADCAST or BPF_F_EXCLUDE_INGRESS as defined below. + * + * With BPF_F_BROADCAST the packet will be broadcasted to all the + * interfaces in the map, with BPF_F_EXCLUDE_INGRESS the ingress + * interface will be excluded when do broadcasting. * * See also **bpf_redirect**\ (), which only supports redirecting * to an ifindex, but doesn't require a map to do so. @@ -2219,6 +3029,9 @@ union bpf_attr { * * > 0 one of **BPF_FIB_LKUP_RET_** codes explaining why the * packet is not forwarded or needs assist from full stack * + * If lookup fails with BPF_FIB_LKUP_RET_FRAG_NEEDED, then the MTU + * was exceeded and output params->mtu_result contains the MTU. + * * long bpf_sock_hash_update(struct bpf_sock_ops *skops, struct bpf_map *map, void *key, u64 flags) * Description * Add an entry to, or update a sockhash *map* referencing sockets. @@ -2442,7 +3255,7 @@ union bpf_attr { * running simultaneously. * * A user should care about the synchronization by himself. - * For example, by using the **BPF_STX_XADD** instruction to alter + * For example, by using the **BPF_ATOMIC** instructions to alter * the shared data. * Return * A pointer to the local storage area. @@ -2450,7 +3263,7 @@ union bpf_attr { * long bpf_sk_select_reuseport(struct sk_reuseport_md *reuse, struct bpf_map *map, void *key, u64 flags) * Description * Select a **SO_REUSEPORT** socket from a - * **BPF_MAP_TYPE_REUSEPORT_ARRAY** *map*. + * **BPF_MAP_TYPE_REUSEPORT_SOCKARRAY** *map*. * It checks the selected socket is matching the incoming * request in the socket buffer. * Return @@ -2987,10 +3800,10 @@ union bpf_attr { * string length is larger than *size*, just *size*-1 bytes are * copied and the last byte is set to NUL. * - * On success, the length of the copied string is returned. This - * makes this helper useful in tracing programs for reading - * strings, and more importantly to get its length at runtime. See - * the following snippet: + * On success, returns the number of bytes that were written, + * including the terminal NUL. This makes this helper useful in + * tracing programs for reading strings, and more importantly to + * get its length at runtime. See the following snippet: * * :: * @@ -3018,7 +3831,7 @@ union bpf_attr { * **->mm->env_start**: using this helper and the return value, * one can quickly iterate at the right offset of the memory area. * Return - * On success, the strictly positive length of the string, + * On success, the strictly positive length of the output string, * including the trailing NUL character. On error, a negative * value. * @@ -3241,7 +4054,7 @@ union bpf_attr { * arguments. The *data* are a **u64** array and corresponding format string * values are stored in the array. For strings and pointers where pointees * are accessed, only the pointer values are stored in the *data* array. - * The *data_len* is the size of *data* in bytes. + * The *data_len* is the size of *data* in bytes - must be a multiple of 8. * * Formats **%s**, **%p{i,I}{4,6}** requires to read kernel memory. * Reading kernel memory may fail due to either invalid address or @@ -3310,12 +4123,20 @@ union bpf_attr { * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. + * If **0** is specified in *flags*, an adaptive notification + * of new data availability is sent. + * + * An adaptive notification is a notification sent whenever the user-space + * process has caught up and consumed all available payloads. In case the user-space + * process is still processing a previous payload, then no notification is needed + * as it will process the newly added payload automatically. * Return * 0 on success, or a negative error in case of failure. * * void *bpf_ringbuf_reserve(void *ringbuf, u64 size, u64 flags) * Description * Reserve *size* bytes of payload in a ring buffer *ringbuf*. + * *flags* must be 0. * Return * Valid pointer with *size* bytes of memory available; NULL, * otherwise. @@ -3327,6 +4148,10 @@ union bpf_attr { * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. + * If **0** is specified in *flags*, an adaptive notification + * of new data availability is sent. + * + * See 'bpf_ringbuf_output()' for the definition of adaptive notification. * Return * Nothing. Always succeeds. * @@ -3337,6 +4162,10 @@ union bpf_attr { * of new data availability is sent. * If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification * of new data availability is sent unconditionally. + * If **0** is specified in *flags*, an adaptive notification + * of new data availability is sent. + * + * See 'bpf_ringbuf_output()' for the definition of adaptive notification. * Return * Nothing. Always succeeds. * @@ -3742,6 +4571,373 @@ union bpf_attr { * Return * The helper returns **TC_ACT_REDIRECT** on success or * **TC_ACT_SHOT** on error. + * + * void *bpf_task_storage_get(struct bpf_map *map, struct task_struct *task, void *value, u64 flags) + * Description + * Get a bpf_local_storage from the *task*. + * + * Logically, it could be thought of as getting the value from + * a *map* with *task* as the **key**. From this + * perspective, the usage is not much different from + * **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this + * helper enforces the key must be an task_struct and the map must also + * be a **BPF_MAP_TYPE_TASK_STORAGE**. + * + * Underneath, the value is stored locally at *task* instead of + * the *map*. The *map* is used as the bpf-local-storage + * "type". The bpf-local-storage "type" (i.e. the *map*) is + * searched against all bpf_local_storage residing at *task*. + * + * An optional *flags* (**BPF_LOCAL_STORAGE_GET_F_CREATE**) can be + * used such that a new bpf_local_storage will be + * created if one does not exist. *value* can be used + * together with **BPF_LOCAL_STORAGE_GET_F_CREATE** to specify + * the initial value of a bpf_local_storage. If *value* is + * **NULL**, the new bpf_local_storage will be zero initialized. + * Return + * A bpf_local_storage pointer is returned on success. + * + * **NULL** if not found or there was an error in adding + * a new bpf_local_storage. + * + * long bpf_task_storage_delete(struct bpf_map *map, struct task_struct *task) + * Description + * Delete a bpf_local_storage from a *task*. + * Return + * 0 on success. + * + * **-ENOENT** if the bpf_local_storage cannot be found. + * + * struct task_struct *bpf_get_current_task_btf(void) + * Description + * Return a BTF pointer to the "current" task. + * This pointer can also be used in helpers that accept an + * *ARG_PTR_TO_BTF_ID* of type *task_struct*. + * Return + * Pointer to the current task. + * + * long bpf_bprm_opts_set(struct linux_binprm *bprm, u64 flags) + * Description + * Set or clear certain options on *bprm*: + * + * **BPF_F_BPRM_SECUREEXEC** Set the secureexec bit + * which sets the **AT_SECURE** auxv for glibc. The bit + * is cleared if the flag is not specified. + * Return + * **-EINVAL** if invalid *flags* are passed, zero otherwise. + * + * u64 bpf_ktime_get_coarse_ns(void) + * Description + * Return a coarse-grained version of the time elapsed since + * system boot, in nanoseconds. Does not include time the system + * was suspended. + * + * See: **clock_gettime**\ (**CLOCK_MONOTONIC_COARSE**) + * Return + * Current *ktime*. + * + * long bpf_ima_inode_hash(struct inode *inode, void *dst, u32 size) + * Description + * Returns the stored IMA hash of the *inode* (if it's avaialable). + * If the hash is larger than *size*, then only *size* + * bytes will be copied to *dst* + * Return + * The **hash_algo** is returned on success, + * **-EOPNOTSUP** if IMA is disabled or **-EINVAL** if + * invalid arguments are passed. + * + * struct socket *bpf_sock_from_file(struct file *file) + * Description + * If the given file represents a socket, returns the associated + * socket. + * Return + * A pointer to a struct socket on success or NULL if the file is + * not a socket. + * + * long bpf_check_mtu(void *ctx, u32 ifindex, u32 *mtu_len, s32 len_diff, u64 flags) + * Description + * Check packet size against exceeding MTU of net device (based + * on *ifindex*). This helper will likely be used in combination + * with helpers that adjust/change the packet size. + * + * The argument *len_diff* can be used for querying with a planned + * size change. This allows to check MTU prior to changing packet + * ctx. Providing an *len_diff* adjustment that is larger than the + * actual packet size (resulting in negative packet size) will in + * principle not exceed the MTU, why it is not considered a + * failure. Other BPF-helpers are needed for performing the + * planned size change, why the responsability for catch a negative + * packet size belong in those helpers. + * + * Specifying *ifindex* zero means the MTU check is performed + * against the current net device. This is practical if this isn't + * used prior to redirect. + * + * On input *mtu_len* must be a valid pointer, else verifier will + * reject BPF program. If the value *mtu_len* is initialized to + * zero then the ctx packet size is use. When value *mtu_len* is + * provided as input this specify the L3 length that the MTU check + * is done against. Remember XDP and TC length operate at L2, but + * this value is L3 as this correlate to MTU and IP-header tot_len + * values which are L3 (similar behavior as bpf_fib_lookup). + * + * The Linux kernel route table can configure MTUs on a more + * specific per route level, which is not provided by this helper. + * For route level MTU checks use the **bpf_fib_lookup**\ () + * helper. + * + * *ctx* is either **struct xdp_md** for XDP programs or + * **struct sk_buff** for tc cls_act programs. + * + * The *flags* argument can be a combination of one or more of the + * following values: + * + * **BPF_MTU_CHK_SEGS** + * This flag will only works for *ctx* **struct sk_buff**. + * If packet context contains extra packet segment buffers + * (often knows as GSO skb), then MTU check is harder to + * check at this point, because in transmit path it is + * possible for the skb packet to get re-segmented + * (depending on net device features). This could still be + * a MTU violation, so this flag enables performing MTU + * check against segments, with a different violation + * return code to tell it apart. Check cannot use len_diff. + * + * On return *mtu_len* pointer contains the MTU value of the net + * device. Remember the net device configured MTU is the L3 size, + * which is returned here and XDP and TC length operate at L2. + * Helper take this into account for you, but remember when using + * MTU value in your BPF-code. + * + * Return + * * 0 on success, and populate MTU value in *mtu_len* pointer. + * + * * < 0 if any input argument is invalid (*mtu_len* not updated) + * + * MTU violations return positive values, but also populate MTU + * value in *mtu_len* pointer, as this can be needed for + * implementing PMTU handing: + * + * * **BPF_MTU_CHK_RET_FRAG_NEEDED** + * * **BPF_MTU_CHK_RET_SEGS_TOOBIG** + * + * long bpf_for_each_map_elem(struct bpf_map *map, void *callback_fn, void *callback_ctx, u64 flags) + * Description + * For each element in **map**, call **callback_fn** function with + * **map**, **callback_ctx** and other map-specific parameters. + * The **callback_fn** should be a static function and + * the **callback_ctx** should be a pointer to the stack. + * The **flags** is used to control certain aspects of the helper. + * Currently, the **flags** must be 0. + * + * The following are a list of supported map types and their + * respective expected callback signatures: + * + * BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERCPU_HASH, + * BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH, + * BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PERCPU_ARRAY + * + * long (\*callback_fn)(struct bpf_map \*map, const void \*key, void \*value, void \*ctx); + * + * For per_cpu maps, the map_value is the value on the cpu where the + * bpf_prog is running. + * + * If **callback_fn** return 0, the helper will continue to the next + * element. If return value is 1, the helper will skip the rest of + * elements and return. Other return values are not used now. + * + * Return + * The number of traversed map elements for success, **-EINVAL** for + * invalid **flags**. + * + * long bpf_snprintf(char *str, u32 str_size, const char *fmt, u64 *data, u32 data_len) + * Description + * Outputs a string into the **str** buffer of size **str_size** + * based on a format string stored in a read-only map pointed by + * **fmt**. + * + * Each format specifier in **fmt** corresponds to one u64 element + * in the **data** array. For strings and pointers where pointees + * are accessed, only the pointer values are stored in the *data* + * array. The *data_len* is the size of *data* in bytes - must be + * a multiple of 8. + * + * Formats **%s** and **%p{i,I}{4,6}** require to read kernel + * memory. Reading kernel memory may fail due to either invalid + * address or valid address but requiring a major memory fault. If + * reading kernel memory fails, the string for **%s** will be an + * empty string, and the ip address for **%p{i,I}{4,6}** will be 0. + * Not returning error to bpf program is consistent with what + * **bpf_trace_printk**\ () does for now. + * + * Return + * The strictly positive length of the formatted string, including + * the trailing zero character. If the return value is greater than + * **str_size**, **str** contains a truncated string, guaranteed to + * be zero-terminated except when **str_size** is 0. + * + * Or **-EBUSY** if the per-CPU memory copy buffer is busy. + * + * long bpf_sys_bpf(u32 cmd, void *attr, u32 attr_size) + * Description + * Execute bpf syscall with given arguments. + * Return + * A syscall result. + * + * long bpf_btf_find_by_name_kind(char *name, int name_sz, u32 kind, int flags) + * Description + * Find BTF type with given name and kind in vmlinux BTF or in module's BTFs. + * Return + * Returns btf_id and btf_obj_fd in lower and upper 32 bits. + * + * long bpf_sys_close(u32 fd) + * Description + * Execute close syscall for given FD. + * Return + * A syscall result. + * + * long bpf_timer_init(struct bpf_timer *timer, struct bpf_map *map, u64 flags) + * Description + * Initialize the timer. + * First 4 bits of *flags* specify clockid. + * Only CLOCK_MONOTONIC, CLOCK_REALTIME, CLOCK_BOOTTIME are allowed. + * All other bits of *flags* are reserved. + * The verifier will reject the program if *timer* is not from + * the same *map*. + * Return + * 0 on success. + * **-EBUSY** if *timer* is already initialized. + * **-EINVAL** if invalid *flags* are passed. + * **-EPERM** if *timer* is in a map that doesn't have any user references. + * The user space should either hold a file descriptor to a map with timers + * or pin such map in bpffs. When map is unpinned or file descriptor is + * closed all timers in the map will be cancelled and freed. + * + * long bpf_timer_set_callback(struct bpf_timer *timer, void *callback_fn) + * Description + * Configure the timer to call *callback_fn* static function. + * Return + * 0 on success. + * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. + * **-EPERM** if *timer* is in a map that doesn't have any user references. + * The user space should either hold a file descriptor to a map with timers + * or pin such map in bpffs. When map is unpinned or file descriptor is + * closed all timers in the map will be cancelled and freed. + * + * long bpf_timer_start(struct bpf_timer *timer, u64 nsecs, u64 flags) + * Description + * Set timer expiration N nanoseconds from the current time. The + * configured callback will be invoked in soft irq context on some cpu + * and will not repeat unless another bpf_timer_start() is made. + * In such case the next invocation can migrate to a different cpu. + * Since struct bpf_timer is a field inside map element the map + * owns the timer. The bpf_timer_set_callback() will increment refcnt + * of BPF program to make sure that callback_fn code stays valid. + * When user space reference to a map reaches zero all timers + * in a map are cancelled and corresponding program's refcnts are + * decremented. This is done to make sure that Ctrl-C of a user + * process doesn't leave any timers running. If map is pinned in + * bpffs the callback_fn can re-arm itself indefinitely. + * bpf_map_update/delete_elem() helpers and user space sys_bpf commands + * cancel and free the timer in the given map element. + * The map can contain timers that invoke callback_fn-s from different + * programs. The same callback_fn can serve different timers from + * different maps if key/value layout matches across maps. + * Every bpf_timer_set_callback() can have different callback_fn. + * + * Return + * 0 on success. + * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier + * or invalid *flags* are passed. + * + * long bpf_timer_cancel(struct bpf_timer *timer) + * Description + * Cancel the timer and wait for callback_fn to finish if it was running. + * Return + * 0 if the timer was not active. + * 1 if the timer was active. + * **-EINVAL** if *timer* was not initialized with bpf_timer_init() earlier. + * **-EDEADLK** if callback_fn tried to call bpf_timer_cancel() on its + * own timer which would have led to a deadlock otherwise. + * + * u64 bpf_get_func_ip(void *ctx) + * Description + * Get address of the traced function (for tracing and kprobe programs). + * Return + * Address of the traced function. + * + * u64 bpf_get_attach_cookie(void *ctx) + * Description + * Get bpf_cookie value provided (optionally) during the program + * attachment. It might be different for each individual + * attachment, even if BPF program itself is the same. + * Expects BPF program context *ctx* as a first argument. + * + * Supported for the following program types: + * - kprobe/uprobe; + * - tracepoint; + * - perf_event. + * Return + * Value specified by user at BPF link creation/attachment time + * or 0, if it was not specified. + * + * long bpf_task_pt_regs(struct task_struct *task) + * Description + * Get the struct pt_regs associated with **task**. + * Return + * A pointer to struct pt_regs. + * + * long bpf_get_branch_snapshot(void *entries, u32 size, u64 flags) + * Description + * Get branch trace from hardware engines like Intel LBR. The + * hardware engine is stopped shortly after the helper is + * called. Therefore, the user need to filter branch entries + * based on the actual use case. To capture branch trace + * before the trigger point of the BPF program, the helper + * should be called at the beginning of the BPF program. + * + * The data is stored as struct perf_branch_entry into output + * buffer *entries*. *size* is the size of *entries* in bytes. + * *flags* is reserved for now and must be zero. + * + * Return + * On success, number of bytes written to *buf*. On error, a + * negative value. + * + * **-EINVAL** if *flags* is not zero. + * + * **-ENOENT** if architecture does not support branch records. + * + * long bpf_trace_vprintk(const char *fmt, u32 fmt_size, const void *data, u32 data_len) + * Description + * Behaves like **bpf_trace_printk**\ () helper, but takes an array of u64 + * to format and can handle more format args as a result. + * + * Arguments are to be used as in **bpf_seq_printf**\ () helper. + * Return + * The number of bytes written to the buffer, or a negative error + * in case of failure. + * + * struct unix_sock *bpf_skc_to_unix_sock(void *sk) + * Description + * Dynamically cast a *sk* pointer to a *unix_sock* pointer. + * Return + * *sk* if casting is valid, or **NULL** otherwise. + * + * long bpf_kallsyms_lookup_name(const char *name, int name_sz, int flags, u64 *res) + * Description + * Get the address of a kernel symbol, returned in *res*. *res* is + * set to 0 if the symbol is not found. + * Return + * On success, zero. On error, a negative value. + * + * **-EINVAL** if *flags* is not zero. + * + * **-EINVAL** if string *name* is not the same size as *name_sz*. + * + * **-ENOENT** if symbol is not found. + * + * **-EPERM** if caller does not have permission to obtain kernel address. */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -3897,9 +5093,33 @@ union bpf_attr { FN(seq_printf_btf), \ FN(skb_cgroup_classid), \ FN(redirect_neigh), \ - FN(bpf_per_cpu_ptr), \ - FN(bpf_this_cpu_ptr), \ + FN(per_cpu_ptr), \ + FN(this_cpu_ptr), \ FN(redirect_peer), \ + FN(task_storage_get), \ + FN(task_storage_delete), \ + FN(get_current_task_btf), \ + FN(bprm_opts_set), \ + FN(ktime_get_coarse_ns), \ + FN(ima_inode_hash), \ + FN(sock_from_file), \ + FN(check_mtu), \ + FN(for_each_map_elem), \ + FN(snprintf), \ + FN(sys_bpf), \ + FN(btf_find_by_name_kind), \ + FN(sys_close), \ + FN(timer_init), \ + FN(timer_set_callback), \ + FN(timer_start), \ + FN(timer_cancel), \ + FN(get_func_ip), \ + FN(get_attach_cookie), \ + FN(task_pt_regs), \ + FN(get_branch_snapshot), \ + FN(trace_vprintk), \ + FN(skc_to_unix_sock), \ + FN(kallsyms_lookup_name), \ /* */ /* integer value in 'imm' field of BPF_CALL instruction selects which helper @@ -3993,6 +5213,7 @@ enum { BPF_F_ADJ_ROOM_ENCAP_L4_GRE = (1ULL << 3), BPF_F_ADJ_ROOM_ENCAP_L4_UDP = (1ULL << 4), BPF_F_ADJ_ROOM_NO_CSUM_RESET = (1ULL << 5), + BPF_F_ADJ_ROOM_ENCAP_L2_ETH = (1ULL << 6), }; enum { @@ -4071,6 +5292,17 @@ enum bpf_lwt_encap_mode { BPF_LWT_ENCAP_IP, }; +/* Flags for bpf_bprm_opts_set helper */ +enum { + BPF_F_BPRM_SECUREEXEC = (1ULL << 0), +}; + +/* Flags for bpf_redirect_map helper */ +enum { + BPF_F_BROADCAST = (1ULL << 3), + BPF_F_EXCLUDE_INGRESS = (1ULL << 4), +}; + #define __bpf_md_ptr(type, name) \ union { \ type name; \ @@ -4117,6 +5349,8 @@ struct __sk_buff { __u32 gso_segs; __bpf_md_ptr(struct bpf_sock *, sk); __u32 gso_size; + __u32 :32; /* Padding, future use. */ + __u64 hwtstamp; }; struct bpf_tunnel_key { @@ -4355,6 +5589,20 @@ struct sk_reuseport_md { __u32 ip_protocol; /* IP protocol. e.g. IPPROTO_TCP, IPPROTO_UDP */ __u32 bind_inany; /* Is sock bound to an INANY address? */ __u32 hash; /* A hash of the packet 4 tuples */ + /* When reuse->migrating_sk is NULL, it is selecting a sk for the + * new incoming connection request (e.g. selecting a listen sk for + * the received SYN in the TCP case). reuse->sk is one of the sk + * in the reuseport group. The bpf prog can use reuse->sk to learn + * the local listening ip/port without looking into the skb. + * + * When reuse->migrating_sk is not NULL, reuse->sk is closed and + * reuse->migrating_sk is the socket that needs to be migrated + * to another listening socket. migrating_sk could be a fullsock + * sk that is fully established or a reqsk that is in-the-middle + * of 3-way handshake. + */ + __bpf_md_ptr(struct bpf_sock *, sk); + __bpf_md_ptr(struct bpf_sock *, migrating_sk); }; #define BPF_TAG_SIZE 8 @@ -4395,6 +5643,8 @@ struct bpf_prog_info { __aligned_u64 prog_tags; __u64 run_time_ns; __u64 run_cnt; + __u64 recursion_misses; + __u32 verified_insns; } __attribute__((aligned(8))); struct bpf_map_info { @@ -4412,12 +5662,17 @@ struct bpf_map_info { __u32 btf_id; __u32 btf_key_type_id; __u32 btf_value_type_id; + __u32 :32; /* alignment pad */ + __u64 map_extra; } __attribute__((aligned(8))); struct bpf_btf_info { __aligned_u64 btf; __u32 btf_size; __u32 id; + __aligned_u64 name; + __u32 name_len; + __u32 kernel_btf; } __attribute__((aligned(8))); struct bpf_link_info { @@ -4431,6 +5686,8 @@ struct bpf_link_info { } raw_tracepoint; struct { __u32 attach_type; + __u32 target_obj_id; /* prog_id for PROG_EXT, otherwise btf object id */ + __u32 target_btf_id; /* BTF type id inside the object */ } tracing; struct { __u64 cgroup_id; @@ -4872,9 +6129,13 @@ struct bpf_fib_lookup { __be16 sport; __be16 dport; - /* total length of packet from network header - used for MTU check */ - __u16 tot_len; + union { /* used for MTU check */ + /* input to lookup */ + __u16 tot_len; /* L3 length from network hdr (iph->tot_len) */ + /* output: MTU value */ + __u16 mtu_result; + }; /* input: L3 device index for lookup * output: device index from FIB lookup */ @@ -4920,6 +6181,17 @@ struct bpf_redir_neigh { }; }; +/* bpf_check_mtu flags*/ +enum bpf_check_mtu_flags { + BPF_MTU_CHK_SEGS = (1U << 0), +}; + +enum bpf_check_mtu_ret { + BPF_MTU_CHK_RET_SUCCESS, /* check and lookup successful */ + BPF_MTU_CHK_RET_FRAG_NEEDED, /* fragmentation required to fwd */ + BPF_MTU_CHK_RET_SEGS_TOOBIG, /* GSO re-segmentation needed to fwd */ +}; + enum bpf_task_fd_type { BPF_FD_TYPE_RAW_TRACEPOINT, /* tp name */ BPF_FD_TYPE_TRACEPOINT, /* tp name */ @@ -4979,6 +6251,11 @@ struct bpf_spin_lock { __u32 val; }; +struct bpf_timer { + __u64 :64; + __u64 :64; +} __attribute__((aligned(8))); + struct bpf_sysctl { __u32 write; /* Sysctl is being read (= 0) or written (= 1). * Allows 1,2,4-byte read, but no write. @@ -5006,7 +6283,10 @@ struct bpf_pidns_info { /* User accessible data for SK_LOOKUP programs. Add new fields at the end. */ struct bpf_sk_lookup { - __bpf_md_ptr(struct bpf_sock *, sk); /* Selected socket */ + union { + __bpf_md_ptr(struct bpf_sock *, sk); /* Selected socket */ + __u64 cookie; /* Non-zero if socket was selected in PROG_TEST_RUN */ + }; __u32 family; /* Protocol family (AF_INET, AF_INET6) */ __u32 protocol; /* IP protocol (IPPROTO_TCP, IPPROTO_UDP) */ diff --git a/src/shared/linux/bpf_insn.h b/src/shared/linux/bpf_insn.h index c459c03c5..92ec06b0e 100644 --- a/src/shared/linux/bpf_insn.h +++ b/src/shared/linux/bpf_insn.h @@ -134,15 +134,31 @@ struct bpf_insn; .off = OFF, \ .imm = 0 }) -/* Atomic memory add, *(uint *)(dst_reg + off16) += src_reg */ +/* + * Atomic operations: + * + * BPF_ADD *(uint *) (dst_reg + off16) += src_reg + * BPF_AND *(uint *) (dst_reg + off16) &= src_reg + * BPF_OR *(uint *) (dst_reg + off16) |= src_reg + * BPF_XOR *(uint *) (dst_reg + off16) ^= src_reg + * BPF_ADD | BPF_FETCH src_reg = atomic_fetch_add(dst_reg + off16, src_reg); + * BPF_AND | BPF_FETCH src_reg = atomic_fetch_and(dst_reg + off16, src_reg); + * BPF_OR | BPF_FETCH src_reg = atomic_fetch_or(dst_reg + off16, src_reg); + * BPF_XOR | BPF_FETCH src_reg = atomic_fetch_xor(dst_reg + off16, src_reg); + * BPF_XCHG src_reg = atomic_xchg(dst_reg + off16, src_reg) + * BPF_CMPXCHG r0 = atomic_cmpxchg(dst_reg + off16, r0, src_reg) + */ -#define BPF_STX_XADD(SIZE, DST, SRC, OFF) \ +#define BPF_ATOMIC_OP(SIZE, OP, DST, SRC, OFF) \ ((struct bpf_insn) { \ - .code = BPF_STX | BPF_SIZE(SIZE) | BPF_XADD, \ + .code = BPF_STX | BPF_SIZE(SIZE) | BPF_ATOMIC, \ .dst_reg = DST, \ .src_reg = SRC, \ .off = OFF, \ - .imm = 0 }) + .imm = OP }) + +/* Legacy alias */ +#define BPF_STX_XADD(SIZE, DST, SRC, OFF) BPF_ATOMIC_OP(SIZE, BPF_ADD, DST, SRC, OFF) /* Memory store, *(uint *) (dst_reg + off16) = imm32 */ diff --git a/src/shared/linux/dm-ioctl.h b/src/shared/linux/dm-ioctl.h index ab312e13f..a6cc1cc40 100644 --- a/src/shared/linux/dm-ioctl.h +++ b/src/shared/linux/dm-ioctl.h @@ -193,8 +193,22 @@ struct dm_name_list { __u32 next; /* offset to the next record from the _start_ of this */ char name[0]; + + /* + * The following members can be accessed by taking a pointer that + * points immediately after the terminating zero character in "name" + * and aligning this pointer to next 8-byte boundary. + * Uuid is present if the flag DM_NAME_LIST_FLAG_HAS_UUID is set. + * + * __u32 event_nr; + * __u32 flags; + * char uuid[0]; + */ }; +#define DM_NAME_LIST_FLAG_HAS_UUID 1 +#define DM_NAME_LIST_FLAG_DOESNT_HAVE_UUID 2 + /* * Used to retrieve the target versions */ @@ -274,7 +288,7 @@ enum { #define DM_VERSION_MAJOR 4 #define DM_VERSION_MINOR 27 #define DM_VERSION_PATCHLEVEL 0 -#define DM_VERSION_EXTRA "-ioctl (2020-10-01)" +#define DM_VERSION_EXTRA "-ioctl (2021-03-22)" /* Status bits */ #define DM_READONLY_FLAG (1 << 0) /* In/Out */ @@ -362,4 +376,10 @@ enum { */ #define DM_INTERNAL_SUSPEND_FLAG (1 << 18) /* Out */ +/* + * If set, returns in the in buffer passed by UM, the raw table information + * that would be measured by IMA subsystem on device state change. + */ +#define DM_IMA_MEASUREMENT_FLAG (1 << 19) /* In */ + #endif /* _LINUX_DM_IOCTL_H */ diff --git a/src/shared/linux/ethtool.h b/src/shared/linux/ethtool.h index 974d4292e..cf20b6dba 100644 --- a/src/shared/linux/ethtool.h +++ b/src/shared/linux/ethtool.h @@ -14,7 +14,7 @@ #ifndef _UAPI_LINUX_ETHTOOL_H #define _UAPI_LINUX_ETHTOOL_H -#include +#include #include #include @@ -30,6 +30,14 @@ * have the same layout for 32-bit and 64-bit userland. */ +/* Note on reserved space. + * Reserved fields must not be accessed directly by user space because + * they may be replaced by a different field in the future. They must + * be initialized to zero before making the request, e.g. via memset + * of the entire structure or implicitly by not being set in a structure + * initializer. + */ + /** * struct ethtool_cmd - DEPRECATED, link control and status * This structure is DEPRECATED, please use struct ethtool_link_settings. @@ -71,6 +79,7 @@ * and other link features that the link partner advertised * through autonegotiation; 0 if unknown or not applicable. * Read-only. + * @reserved: Reserved for future use; see the note on reserved space. * * The link speed in Mbps is split between @speed and @speed_hi. Use * the ethtool_cmd_speed() and ethtool_cmd_speed_set() functions to @@ -159,6 +168,7 @@ static inline __u32 ethtool_cmd_speed(const struct ethtool_cmd *ep) * @bus_info: Device bus address. This should match the dev_name() * string for the underlying bus device, if there is one. May be * an empty string. + * @reserved2: Reserved for future use; see the note on reserved space. * @n_priv_flags: Number of flags valid for %ETHTOOL_GPFLAGS and * %ETHTOOL_SPFLAGS commands; also the number of strings in the * %ETH_SS_PRIV_FLAGS set @@ -227,7 +237,7 @@ enum tunable_id { ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */ /* * Add your fresh new tunable attribute above and remember to update - * tunable_strings[] in net/core/ethtool.c + * tunable_strings[] in net/ethtool/common.c */ __ETHTOOL_TUNABLE_COUNT, }; @@ -291,7 +301,7 @@ enum phy_tunable_id { ETHTOOL_PHY_EDPD, /* * Add your fresh new phy tunable attribute above and remember to update - * phy_tunable_strings[] in net/core/ethtool.c + * phy_tunable_strings[] in net/ethtool/common.c */ __ETHTOOL_PHY_TUNABLE_COUNT, }; @@ -360,6 +370,7 @@ struct ethtool_eeprom { * @tx_lpi_timer: Time in microseconds the interface delays prior to asserting * its tx lpi (after reaching 'idle' state). Effective only when eee * was negotiated and tx_lpi_enabled was set. + * @reserved: Reserved for future use; see the note on reserved space. */ struct ethtool_eee { __u32 cmd; @@ -378,6 +389,7 @@ struct ethtool_eee { * @cmd: %ETHTOOL_GMODULEINFO * @type: Standard the module information conforms to %ETH_MODULE_SFF_xxxx * @eeprom_len: Length of the eeprom + * @reserved: Reserved for future use; see the note on reserved space. * * This structure is used to return the information to * properly size memory for a subsequent call to %ETHTOOL_GMODULEEEPROM. @@ -583,9 +595,7 @@ struct ethtool_pauseparam { __u32 tx_pause; }; -/** - * enum ethtool_link_ext_state - link extended state - */ +/* Link extended state */ enum ethtool_link_ext_state { ETHTOOL_LINK_EXT_STATE_AUTONEG, ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE, @@ -597,12 +607,10 @@ enum ethtool_link_ext_state { ETHTOOL_LINK_EXT_STATE_CALIBRATION_FAILURE, ETHTOOL_LINK_EXT_STATE_POWER_BUDGET_EXCEEDED, ETHTOOL_LINK_EXT_STATE_OVERHEAT, + ETHTOOL_LINK_EXT_STATE_MODULE, }; -/** - * enum ethtool_link_ext_substate_autoneg - more information in addition to - * ETHTOOL_LINK_EXT_STATE_AUTONEG. - */ +/* More information in addition to ETHTOOL_LINK_EXT_STATE_AUTONEG. */ enum ethtool_link_ext_substate_autoneg { ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_PARTNER_DETECTED = 1, ETHTOOL_LINK_EXT_SUBSTATE_AN_ACK_NOT_RECEIVED, @@ -612,9 +620,7 @@ enum ethtool_link_ext_substate_autoneg { ETHTOOL_LINK_EXT_SUBSTATE_AN_NO_HCD, }; -/** - * enum ethtool_link_ext_substate_link_training - more information in addition to - * ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE. +/* More information in addition to ETHTOOL_LINK_EXT_STATE_LINK_TRAINING_FAILURE. */ enum ethtool_link_ext_substate_link_training { ETHTOOL_LINK_EXT_SUBSTATE_LT_KR_FRAME_LOCK_NOT_ACQUIRED = 1, @@ -623,9 +629,7 @@ enum ethtool_link_ext_substate_link_training { ETHTOOL_LINK_EXT_SUBSTATE_LT_REMOTE_FAULT, }; -/** - * enum ethtool_link_ext_substate_logical_mismatch - more information in addition - * to ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH. +/* More information in addition to ETHTOOL_LINK_EXT_STATE_LINK_LOGICAL_MISMATCH. */ enum ethtool_link_ext_substate_link_logical_mismatch { ETHTOOL_LINK_EXT_SUBSTATE_LLM_PCS_DID_NOT_ACQUIRE_BLOCK_LOCK = 1, @@ -635,24 +639,26 @@ enum ethtool_link_ext_substate_link_logical_mismatch { ETHTOOL_LINK_EXT_SUBSTATE_LLM_RS_FEC_IS_NOT_LOCKED, }; -/** - * enum ethtool_link_ext_substate_bad_signal_integrity - more information in - * addition to ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY. +/* More information in addition to ETHTOOL_LINK_EXT_STATE_BAD_SIGNAL_INTEGRITY. */ enum ethtool_link_ext_substate_bad_signal_integrity { ETHTOOL_LINK_EXT_SUBSTATE_BSI_LARGE_NUMBER_OF_PHYSICAL_ERRORS = 1, ETHTOOL_LINK_EXT_SUBSTATE_BSI_UNSUPPORTED_RATE, + ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_REFERENCE_CLOCK_LOST, + ETHTOOL_LINK_EXT_SUBSTATE_BSI_SERDES_ALOS, }; -/** - * enum ethtool_link_ext_substate_cable_issue - more information in - * addition to ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE. - */ +/* More information in addition to ETHTOOL_LINK_EXT_STATE_CABLE_ISSUE. */ enum ethtool_link_ext_substate_cable_issue { ETHTOOL_LINK_EXT_SUBSTATE_CI_UNSUPPORTED_CABLE = 1, ETHTOOL_LINK_EXT_SUBSTATE_CI_CABLE_TEST_FAILURE, }; +/* More information in addition to ETHTOOL_LINK_EXT_STATE_MODULE. */ +enum ethtool_link_ext_substate_module { + ETHTOOL_LINK_EXT_SUBSTATE_MODULE_CMIS_NOT_READY = 1, +}; + #define ETH_GSTRING_LEN 32 /** @@ -665,6 +671,7 @@ enum ethtool_link_ext_substate_cable_issue { * now deprecated * @ETH_SS_FEATURES: Device feature names * @ETH_SS_RSS_HASH_FUNCS: RSS hush function names + * @ETH_SS_TUNABLES: tunable names * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS * @ETH_SS_PHY_TUNABLES: PHY tunable names * @ETH_SS_LINK_MODES: link mode names @@ -674,6 +681,13 @@ enum ethtool_link_ext_substate_cable_issue { * @ETH_SS_TS_TX_TYPES: timestamping Tx types * @ETH_SS_TS_RX_FILTERS: timestamping Rx filters * @ETH_SS_UDP_TUNNEL_TYPES: UDP tunnel types + * @ETH_SS_STATS_STD: standardized stats + * @ETH_SS_STATS_ETH_PHY: names of IEEE 802.3 PHY statistics + * @ETH_SS_STATS_ETH_MAC: names of IEEE 802.3 MAC statistics + * @ETH_SS_STATS_ETH_CTRL: names of IEEE 802.3 MAC Control statistics + * @ETH_SS_STATS_RMON: names of RMON statistics + * + * @ETH_SS_COUNT: number of defined string sets */ enum ethtool_stringset { ETH_SS_TEST = 0, @@ -692,11 +706,39 @@ enum ethtool_stringset { ETH_SS_TS_TX_TYPES, ETH_SS_TS_RX_FILTERS, ETH_SS_UDP_TUNNEL_TYPES, + ETH_SS_STATS_STD, + ETH_SS_STATS_ETH_PHY, + ETH_SS_STATS_ETH_MAC, + ETH_SS_STATS_ETH_CTRL, + ETH_SS_STATS_RMON, /* add new constants above here */ ETH_SS_COUNT }; +/** + * enum ethtool_module_power_mode_policy - plug-in module power mode policy + * @ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH: Module is always in high power mode. + * @ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO: Module is transitioned by the host + * to high power mode when the first port using it is put administratively + * up and to low power mode when the last port using it is put + * administratively down. + */ +enum ethtool_module_power_mode_policy { + ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH = 1, + ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO, +}; + +/** + * enum ethtool_module_power_mode - plug-in module power mode + * @ETHTOOL_MODULE_POWER_MODE_LOW: Module is in low power mode. + * @ETHTOOL_MODULE_POWER_MODE_HIGH: Module is in high power mode. + */ +enum ethtool_module_power_mode { + ETHTOOL_MODULE_POWER_MODE_LOW = 1, + ETHTOOL_MODULE_POWER_MODE_HIGH, +}; + /** * struct ethtool_gstrings - string set for data tagging * @cmd: Command number = %ETHTOOL_GSTRINGS @@ -719,6 +761,7 @@ struct ethtool_gstrings { /** * struct ethtool_sset_info - string set information * @cmd: Command number = %ETHTOOL_GSSET_INFO + * @reserved: Reserved for future use; see the note on reserved space. * @sset_mask: On entry, a bitmask of string sets to query, with bits * numbered according to &enum ethtool_stringset. On return, a * bitmask of those string sets queried that are supported. @@ -763,6 +806,7 @@ enum ethtool_test_flags { * @flags: A bitmask of flags from &enum ethtool_test_flags. Some * flags may be set by the user on entry; others may be set by * the driver on return. + * @reserved: Reserved for future use; see the note on reserved space. * @len: On return, the number of test results * @data: Array of test results * @@ -963,6 +1007,7 @@ union ethtool_flow_union { * @vlan_etype: VLAN EtherType * @vlan_tci: VLAN tag control information * @data: user defined data + * @padding: Reserved for future use; see the note on reserved space. * * Note, @vlan_etype, @vlan_tci, and @data are only valid if %FLOW_EXT * is set in &struct ethtool_rx_flow_spec @flow_type. @@ -1138,7 +1183,8 @@ struct ethtool_rxfh_indir { * hardware hash key. * @hfunc: Defines the current RSS hash function used by HW (or to be set to). * Valid values are one of the %ETH_RSS_HASH_*. - * @rsvd: Reserved for future extensions. + * @rsvd8: Reserved for future use; see the note on reserved space. + * @rsvd32: Reserved for future use; see the note on reserved space. * @rss_config: RX ring/queue index for each hash value i.e., indirection table * of @indir_size __u32 elements, followed by hash key of @key_size * bytes. @@ -1306,7 +1352,9 @@ struct ethtool_sfeatures { * @so_timestamping: bit mask of the sum of the supported SO_TIMESTAMPING flags * @phc_index: device index of the associated PHC, or -1 if there is none * @tx_types: bit mask of the supported hwtstamp_tx_types enumeration values + * @tx_reserved: Reserved for future use; see the note on reserved space. * @rx_filters: bit mask of the supported hwtstamp_rx_filters enumeration values + * @rx_reserved: Reserved for future use; see the note on reserved space. * * The bits in the 'tx_types' and 'rx_filters' fields correspond to * the 'hwtstamp_tx_types' and 'hwtstamp_rx_filters' enumeration values, @@ -1380,15 +1428,33 @@ struct ethtool_per_queue_op { }; /** - * struct ethtool_fecparam - Ethernet forward error correction(fec) parameters + * struct ethtool_fecparam - Ethernet Forward Error Correction parameters * @cmd: Command number = %ETHTOOL_GFECPARAM or %ETHTOOL_SFECPARAM - * @active_fec: FEC mode which is active on porte - * @fec: Bitmask of supported/configured FEC modes - * @rsvd: Reserved for future extensions. i.e FEC bypass feature. + * @active_fec: FEC mode which is active on the port, single bit set, GET only. + * @fec: Bitmask of configured FEC modes. + * @reserved: Reserved for future extensions, ignore on GET, write 0 for SET. * - * Drivers should reject a non-zero setting of @autoneg when - * autoneogotiation is disabled (or not supported) for the link. + * Note that @reserved was never validated on input and ethtool user space + * left it uninitialized when calling SET. Hence going forward it can only be + * used to return a value to userspace with GET. * + * FEC modes supported by the device can be read via %ETHTOOL_GLINKSETTINGS. + * FEC settings are configured by link autonegotiation whenever it's enabled. + * With autoneg on %ETHTOOL_GFECPARAM can be used to read the current mode. + * + * When autoneg is disabled %ETHTOOL_SFECPARAM controls the FEC settings. + * It is recommended that drivers only accept a single bit set in @fec. + * When multiple bits are set in @fec drivers may pick mode in an implementation + * dependent way. Drivers should reject mixing %ETHTOOL_FEC_AUTO_BIT with other + * FEC modes, because it's unclear whether in this case other modes constrain + * AUTO or are independent choices. + * Drivers must reject SET requests if they support none of the requested modes. + * + * If device does not support FEC drivers may use %ETHTOOL_FEC_NONE instead + * of returning %EOPNOTSUPP from %ETHTOOL_GFECPARAM. + * + * See enum ethtool_fec_config_bits for definition of valid bits for both + * @fec and @active_fec. */ struct ethtool_fecparam { __u32 cmd; @@ -1400,11 +1466,16 @@ struct ethtool_fecparam { /** * enum ethtool_fec_config_bits - flags definition of ethtool_fec_configuration - * @ETHTOOL_FEC_NONE: FEC mode configuration is not supported - * @ETHTOOL_FEC_AUTO: Default/Best FEC mode provided by driver - * @ETHTOOL_FEC_OFF: No FEC Mode - * @ETHTOOL_FEC_RS: Reed-Solomon Forward Error Detection mode - * @ETHTOOL_FEC_BASER: Base-R/Reed-Solomon Forward Error Detection mode + * @ETHTOOL_FEC_NONE_BIT: FEC mode configuration is not supported. Should not + * be used together with other bits. GET only. + * @ETHTOOL_FEC_AUTO_BIT: Select default/best FEC mode automatically, usually + * based link mode and SFP parameters read from module's + * EEPROM. This bit does _not_ mean autonegotiation. + * @ETHTOOL_FEC_OFF_BIT: No FEC Mode + * @ETHTOOL_FEC_RS_BIT: Reed-Solomon FEC Mode + * @ETHTOOL_FEC_BASER_BIT: Base-R/Reed-Solomon FEC Mode + * @ETHTOOL_FEC_LLRS_BIT: Low Latency Reed Solomon FEC Mode (25G/50G Ethernet + * Consortium) */ enum ethtool_fec_config_bits { ETHTOOL_FEC_NONE_BIT, @@ -1962,6 +2033,11 @@ enum ethtool_reset_flags { * autonegotiation; 0 if unknown or not applicable. Read-only. * @transceiver: Used to distinguish different possible PHY types, * reported consistently by PHYLIB. Read-only. + * @master_slave_cfg: Master/slave port mode. + * @master_slave_state: Master/slave port state. + * @reserved: Reserved for future use; see the note on reserved space. + * @reserved1: Reserved for future use; see the note on reserved space. + * @link_mode_masks: Variable length bitmaps. * * If autonegotiation is disabled, the speed and @duplex represent the * fixed link mode and are writable if the driver supports multiple diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index cf83eb6bc..acc1dc9b9 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -1510,7 +1510,7 @@ int add_matches_for_user_unit(sd_journal *j, const char *unit, uid_t uid) { static int get_boot_id_for_machine(const char *machine, sd_id128_t *boot_id) { _cleanup_close_pair_ int pair[2] = { -1, -1 }; _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, rootfd = -1; - char buf[ID128_UUID_STRING_MAX]; + char buf[SD_ID128_UUID_STRING_MAX]; pid_t pid, child; ssize_t k; int r; diff --git a/src/shared/machine-id-setup.c b/src/shared/machine-id-setup.c index e483675a7..df4ac419c 100644 --- a/src/shared/machine-id-setup.c +++ b/src/shared/machine-id-setup.c @@ -197,7 +197,7 @@ finish: int machine_id_commit(const char *root) { _cleanup_close_ int fd = -1, initial_mntns_fd = -1; - const char *etc_machine_id, *sync_path; + const char *etc_machine_id; sd_id128_t id; int r; diff --git a/src/shared/main-func.h b/src/shared/main-func.h index 05cdffeec..81a5c1813 100644 --- a/src/shared/main-func.h +++ b/src/shared/main-func.h @@ -15,6 +15,7 @@ #define _DEFINE_MAIN_FUNCTION(intro, impl, ret) \ int main(int argc, char *argv[]) { \ int r; \ + assert_se(argc > 0 && !isempty(argv[0])); \ save_argc_argv(argc, argv); \ intro; \ r = impl; \ diff --git a/src/shared/meson.build b/src/shared/meson.build index 1e4fcbf11..4333c9a0a 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -105,6 +105,8 @@ shared_sources = files( 'dns-domain.h', 'dropin.c', 'dropin.h', + 'efi-api.c', + 'efi-api.h', 'efi-loader.c', 'efi-loader.h', 'elf-util.c', @@ -125,6 +127,8 @@ shared_sources = files( 'fdset.h', 'fileio-label.c', 'fileio-label.h', + 'find-esp.c', + 'find-esp.h', 'firewall-util-nft.c', 'firewall-util-private.h', 'firewall-util.c', @@ -176,8 +180,8 @@ shared_sources = files( 'json.h', 'kbd-util.c', 'kbd-util.h', - 'keyring-util.h', 'keyring-util.c', + 'keyring-util.h', 'killall.c', 'killall.h', 'label.c', @@ -224,6 +228,8 @@ shared_sources = files( 'net-condition.h', 'netif-naming-scheme.c', 'netif-naming-scheme.h', + 'netif-sriov.c', + 'netif-sriov.h', 'netif-util.c', 'netif-util.h', 'nscd-flush.h', @@ -433,7 +439,7 @@ target2 = custom_target( shared_generated_gperf_headers = [target1, target2] shared_sources += shared_generated_gperf_headers -libshared_name = 'systemd-shared-@0@'.format(meson.project_version()) +libshared_name = 'systemd-shared-@0@'.format(shared_lib_tag) libshared_deps = [threads, libacl, @@ -468,13 +474,13 @@ libshared_static = static_library( libshared = shared_library( libshared_name, include_directories : includes, + c_args : ['-fvisibility=default'], link_args : ['-shared', '-Wl,--version-script=' + libshared_sym_path], link_whole : [libshared_static, libbasic, libbasic_gcrypt, libsystemd_static], - c_args : ['-fvisibility=default'], dependencies : libshared_deps, install : true, install_dir : rootlibexecdir) diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index 061a2c2ef..accd64f9e 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -94,7 +94,7 @@ int make_filesystem( bool discard) { _cleanup_free_ char *mkfs = NULL, *mangled_label = NULL; - char vol_id[CONST_MAX(ID128_UUID_STRING_MAX, 8 + 1)] = {}; + char vol_id[CONST_MAX(SD_ID128_UUID_STRING_MAX, 8U + 1U)] = {}; int r; assert(node); @@ -144,7 +144,7 @@ int make_filesystem( } if (isempty(vol_id)) - id128_to_uuid_string(uuid, vol_id); + assert_se(sd_id128_to_uuid_string(uuid, vol_id)); r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_RLIMIT_NOFILE_SAFE|FORK_DEATHSIG|FORK_LOG|FORK_WAIT|FORK_STDOUT_TO_STDERR, NULL); if (r < 0) diff --git a/src/shared/mount-setup.c b/src/shared/mount-setup.c index 791796849..975c027f4 100644 --- a/src/shared/mount-setup.c +++ b/src/shared/mount-setup.c @@ -126,9 +126,6 @@ bool mount_point_is_api(const char *path) { } bool mount_point_ignore(const char *path) { - - const char *i; - /* These are API file systems that might be mounted by other software, we just list them here so that * we know that we should ignore them. */ FOREACH_STRING(i, @@ -247,8 +244,6 @@ static const char *join_with(const char *controller) { NULL }; - const char *const *x, *const *y; - assert(controller); /* This will lookup which controller to mount another controller with. Input is a controller name, and output @@ -436,7 +431,6 @@ static int relabel_cgroup_filesystems(void) { static int relabel_extra(void) { _cleanup_strv_free_ char **files = NULL; - char **file; int r, c = 0; /* Support for relabelling additional files or directories after loading the policy. For this, code in the @@ -518,7 +512,6 @@ int mount_setup(bool loaded_policy, bool leave_propagation) { * use the same label for all their files. */ if (loaded_policy) { usec_t before_relabel, after_relabel; - const char *i; int n_extra; before_relabel = now(CLOCK_MONOTONIC); diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index c75c02f5b..694c0383f 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -274,7 +274,6 @@ int bind_remount_recursive_with_mountinfo( * we shall operate on. */ if (!path_equal(path, prefix)) { bool deny_listed = false; - char **i; STRV_FOREACH(i, deny_list) { if (path_equal(*i, prefix)) @@ -592,9 +591,9 @@ int mode_to_inaccessible_node( return 0; } -int mount_flags_to_string(long unsigned flags, char **ret) { +int mount_flags_to_string(unsigned long flags, char **ret) { static const struct { - long unsigned flag; + unsigned long flag; const char *name; } map[] = { { .flag = MS_RDONLY, .name = "MS_RDONLY", }, @@ -816,7 +815,7 @@ static int mount_in_namespace( return log_debug_errno(errno, "Failed to fstat mount namespace FD of systemd: %m"); /* We can't add new mounts at runtime if the process wasn't started in a namespace */ - if (st.st_ino == self_mntns_st.st_ino && st.st_dev == self_mntns_st.st_dev) + if (stat_inode_same(&st, &self_mntns_st)) return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to activate bind mount in target, not running in a mount namespace"); /* One day, when bind mounting /proc/self/fd/n works across namespace boundaries we should rework @@ -1049,14 +1048,31 @@ int make_mount_point(const char *path) { return 1; } -static int make_userns(uid_t uid_shift, uid_t uid_range) { - char line[DECIMAL_STR_MAX(uid_t)*3+3+1]; +static int make_userns(uid_t uid_shift, uid_t uid_range, RemountIdmapFlags flags) { _cleanup_close_ int userns_fd = -1; + _cleanup_free_ char *line = NULL; /* Allocates a userns file descriptor with the mapping we need. For this we'll fork off a child * process whose only purpose is to give us a new user namespace. It's killed when we got it. */ - xsprintf(line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0, uid_shift, uid_range); + if (asprintf(&line, UID_FMT " " UID_FMT " " UID_FMT "\n", 0, uid_shift, uid_range) < 0) + return log_oom_debug(); + + /* If requested we'll include an entry in the mapping so that the host root user can make changes to + * the uidmapped mount like it normally would. Specifically, we'll map the user with UID_HOST_ROOT on + * the backing fs to UID 0. This is useful, since nspawn code wants to create various missing inodes + * in the OS tree before booting into it, and this becomes very easy and straightforward to do if it + * can just do it under its own regular UID. Note that in that case the container's runtime uidmap + * (i.e. the one the container payload processes run in) will leave this UID unmapped, i.e. if we + * accidentally leave files owned by host root in the already uidmapped tree around they'll show up + * as owned by 'nobody', which is safe. (Of course, we shouldn't leave such inodes around, but always + * chown() them to the container's own UID range, but it's good to have a safety net, in case we + * forget it.) */ + if (flags & REMOUNT_IDMAP_HOST_ROOT) + if (strextendf(&line, + UID_FMT " " UID_FMT " " UID_FMT "\n", + UID_MAPPED_ROOT, 0, 1) < 0) + return log_oom_debug(); /* We always assign the same UID and GID ranges */ userns_fd = userns_acquire(line, line); @@ -1069,7 +1085,8 @@ static int make_userns(uid_t uid_shift, uid_t uid_range) { int remount_idmap( const char *p, uid_t uid_shift, - uid_t uid_range) { + uid_t uid_range, + RemountIdmapFlags flags) { _cleanup_close_ int mount_fd = -1, userns_fd = -1; int r; @@ -1085,7 +1102,7 @@ int remount_idmap( return log_debug_errno(errno, "Failed to open tree of mounted filesystem '%s': %m", p); /* Create a user namespace mapping */ - userns_fd = make_userns(uid_shift, uid_range); + userns_fd = make_userns(uid_shift, uid_range, flags); if (userns_fd < 0) return userns_fd; diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h index ce73aebd4..960d586ba 100644 --- a/src/shared/mount-util.h +++ b/src/shared/mount-util.h @@ -94,7 +94,7 @@ int mount_option_mangle( char **ret_remaining_options); int mode_to_inaccessible_node(const char *runtime_dir, mode_t mode, char **dest); -int mount_flags_to_string(long unsigned flags, char **ret); +int mount_flags_to_string(unsigned long flags, char **ret); /* Useful for usage with _cleanup_(), unmounts, removes a directory and frees the pointer */ static inline char* umount_and_rmdir_and_free(char *p) { @@ -112,7 +112,18 @@ int mount_image_in_namespace(pid_t target, const char *propagate_path, const cha int make_mount_point(const char *path); -int remount_idmap(const char *p, uid_t uid_shift, uid_t uid_range); +typedef enum RemountIdmapFlags { + /* Include a mapping from UID_MAPPED_ROOT (i.e. UID 2^31-2) on the backing fs to UID 0 on the + * uidmapped fs. This is useful to ensure that the host root user can safely add inodes to the + * uidmapped fs (which otherwise wouldn't work as the host root user is not defined on the uidmapped + * mount and any attempts to create inodes will then be refused with EOVERFLOW). The idea is that + * these inodes are quickly re-chown()ed to more suitable UIDs/GIDs. Any code that intends to be able + * to add inodes to file systems mapped this way should set this flag, but given it comes with + * certain security implications defaults to off, and requires explicit opt-in. */ + REMOUNT_IDMAP_HOST_ROOT = 1 << 0, +} RemountIdmapFlags; + +int remount_idmap(const char *p, uid_t uid_shift, uid_t uid_range, RemountIdmapFlags flags); /* Creates a mount point (not parents) based on the source path or stat - ie, a file or a directory */ int make_mount_point_inode_from_stat(const struct stat *st, const char *dest, mode_t mode); diff --git a/src/shared/net-condition.c b/src/shared/net-condition.c index 006676d97..15e3d94de 100644 --- a/src/shared/net-condition.c +++ b/src/shared/net-condition.c @@ -22,6 +22,7 @@ void net_match_clear(NetMatch *match) { match->path = strv_free(match->path); match->driver = strv_free(match->driver); match->iftype = strv_free(match->iftype); + match->kind = strv_free(match->kind); match->ifname = strv_free(match->ifname); match->property = strv_free(match->property); match->wlan_iftype = strv_free(match->wlan_iftype); @@ -38,6 +39,7 @@ bool net_match_is_empty(const NetMatch *match) { strv_isempty(match->path) && strv_isempty(match->driver) && strv_isempty(match->iftype) && + strv_isempty(match->kind) && strv_isempty(match->ifname) && strv_isempty(match->property) && strv_isempty(match->wlan_iftype) && @@ -46,7 +48,6 @@ bool net_match_is_empty(const NetMatch *match) { } static bool net_condition_test_strv(char * const *patterns, const char *string) { - char * const *p; bool match = false, has_positive_rule = false; if (strv_isempty(patterns)) @@ -77,7 +78,6 @@ static bool net_condition_test_ifname(char * const *patterns, const char *ifname if (net_condition_test_strv(patterns, ifname)) return true; - char * const *p; STRV_FOREACH(p, alternative_names) if (net_condition_test_strv(patterns, *p)) return true; @@ -86,8 +86,6 @@ static bool net_condition_test_ifname(char * const *patterns, const char *ifname } static int net_condition_test_property(char * const *match_property, sd_device *device) { - char * const *p; - if (strv_isempty(match_property)) return true; @@ -126,6 +124,7 @@ int net_match_config( const struct hw_addr_data *permanent_hw_addr, const char *driver, unsigned short iftype, + const char *kind, const char *ifname, char * const *alternative_names, enum nl80211_iftype wlan_iftype, @@ -160,6 +159,9 @@ int net_match_config( if (!net_condition_test_strv(match->iftype, iftype_str)) return false; + if (!net_condition_test_strv(match->kind, kind)) + return false; + if (!net_condition_test_ifname(match->ifname, ifname, alternative_names)) return false; diff --git a/src/shared/net-condition.h b/src/shared/net-condition.h index e76743933..0884d43f4 100644 --- a/src/shared/net-condition.h +++ b/src/shared/net-condition.h @@ -15,7 +15,8 @@ typedef struct NetMatch { Set *permanent_hw_addr; char **path; char **driver; - char **iftype; + char **iftype; /* udev's DEVTYPE field or ARPHRD_XXX, e.g. ether, wlan. */ + char **kind; /* IFLA_INFO_KIND attribute, e.g. gre, gretap, erspan. */ char **ifname; char **property; char **wlan_iftype; @@ -33,6 +34,7 @@ int net_match_config( const struct hw_addr_data *permanent_hw_addr, const char *driver, unsigned short iftype, + const char *kind, const char *ifname, char * const *alternative_names, enum nl80211_iftype wlan_iftype, diff --git a/src/shared/netif-naming-scheme.c b/src/shared/netif-naming-scheme.c index 245466c4c..a2bc4fa55 100644 --- a/src/shared/netif-naming-scheme.c +++ b/src/shared/netif-naming-scheme.c @@ -29,7 +29,7 @@ static const NamingScheme naming_schemes[] = { }; const NamingScheme* naming_scheme_from_name(const char *name) { - /* "latest" may either be defined explicitly by the extra map, in which case we we will find it in + /* "latest" may either be defined explicitly by the extra map, in which case we will find it in * the table like any other name. After iterating through the table, we check for "latest" again, * which means that if not mapped explicitly, it maps to the last defined entry, whatever that is. */ diff --git a/src/shared/netif-sriov.c b/src/shared/netif-sriov.c new file mode 100644 index 000000000..9edc17412 --- /dev/null +++ b/src/shared/netif-sriov.c @@ -0,0 +1,649 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "alloc-util.h" +#include "device-util.h" +#include "netlink-util.h" +#include "netif-sriov.h" +#include "parse-util.h" +#include "set.h" +#include "stdio-util.h" +#include "string-util.h" + +static int sr_iov_new(SRIOV **ret) { + SRIOV *sr_iov; + + assert(ret); + + sr_iov = new(SRIOV, 1); + if (!sr_iov) + return -ENOMEM; + + *sr_iov = (SRIOV) { + .vf = UINT32_MAX, + .vlan_proto = ETH_P_8021Q, + .vf_spoof_check_setting = -1, + .trust = -1, + .query_rss = -1, + .link_state = _SR_IOV_LINK_STATE_INVALID, + }; + + *ret = TAKE_PTR(sr_iov); + + return 0; +} + +static int sr_iov_new_static(OrderedHashmap **sr_iov_by_section, const char *filename, unsigned section_line, SRIOV **ret) { + _cleanup_(config_section_freep) ConfigSection *n = NULL; + _cleanup_(sr_iov_freep) SRIOV *sr_iov = NULL; + SRIOV *existing = NULL; + int r; + + assert(sr_iov_by_section); + assert(filename); + assert(section_line > 0); + assert(ret); + + r = config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + existing = ordered_hashmap_get(*sr_iov_by_section, n); + if (existing) { + *ret = existing; + return 0; + } + + r = sr_iov_new(&sr_iov); + if (r < 0) + return r; + + r = ordered_hashmap_ensure_put(sr_iov_by_section, &config_section_hash_ops, n, sr_iov); + if (r < 0) + return r; + + sr_iov->section = TAKE_PTR(n); + sr_iov->sr_iov_by_section = *sr_iov_by_section; + + *ret = TAKE_PTR(sr_iov); + return 0; +} + +SRIOV *sr_iov_free(SRIOV *sr_iov) { + if (!sr_iov) + return NULL; + + if (sr_iov->sr_iov_by_section && sr_iov->section) + ordered_hashmap_remove(sr_iov->sr_iov_by_section, sr_iov->section); + + config_section_free(sr_iov->section); + + return mfree(sr_iov); +} + +void sr_iov_hash_func(const SRIOV *sr_iov, struct siphash *state) { + assert(sr_iov); + assert(state); + + siphash24_compress(&sr_iov->vf, sizeof(sr_iov->vf), state); +} + +int sr_iov_compare_func(const SRIOV *s1, const SRIOV *s2) { + assert(s1); + assert(s2); + + return CMP(s1->vf, s2->vf); +} + +DEFINE_PRIVATE_HASH_OPS( + sr_iov_hash_ops, + SRIOV, + sr_iov_hash_func, + sr_iov_compare_func); + +int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req) { + int r; + + assert(sr_iov); + assert(req); + + r = sd_netlink_message_open_container(req, IFLA_VFINFO_LIST); + if (r < 0) + return r; + + r = sd_netlink_message_open_container(req, IFLA_VF_INFO); + if (r < 0) + return r; + + if (!ether_addr_is_null(&sr_iov->mac)) { + struct ifla_vf_mac ivm = { + .vf = sr_iov->vf, + }; + + memcpy(ivm.mac, &sr_iov->mac, ETH_ALEN); + r = sd_netlink_message_append_data(req, IFLA_VF_MAC, &ivm, sizeof(struct ifla_vf_mac)); + if (r < 0) + return r; + } + + if (sr_iov->vf_spoof_check_setting >= 0) { + struct ifla_vf_spoofchk ivs = { + .vf = sr_iov->vf, + .setting = sr_iov->vf_spoof_check_setting, + }; + + r = sd_netlink_message_append_data(req, IFLA_VF_SPOOFCHK, &ivs, sizeof(struct ifla_vf_spoofchk)); + if (r < 0) + return r; + } + + if (sr_iov->query_rss >= 0) { + struct ifla_vf_rss_query_en ivs = { + .vf = sr_iov->vf, + .setting = sr_iov->query_rss, + }; + + r = sd_netlink_message_append_data(req, IFLA_VF_RSS_QUERY_EN, &ivs, sizeof(struct ifla_vf_rss_query_en)); + if (r < 0) + return r; + } + + if (sr_iov->trust >= 0) { + struct ifla_vf_trust ivt = { + .vf = sr_iov->vf, + .setting = sr_iov->trust, + }; + + r = sd_netlink_message_append_data(req, IFLA_VF_TRUST, &ivt, sizeof(struct ifla_vf_trust)); + if (r < 0) + return r; + } + + if (sr_iov->link_state >= 0) { + struct ifla_vf_link_state ivl = { + .vf = sr_iov->vf, + .link_state = sr_iov->link_state, + }; + + r = sd_netlink_message_append_data(req, IFLA_VF_LINK_STATE, &ivl, sizeof(struct ifla_vf_link_state)); + if (r < 0) + return r; + } + + if (sr_iov->vlan > 0) { + /* Because of padding, first the buffer must be initialized with 0. */ + struct ifla_vf_vlan_info ivvi = {}; + ivvi.vf = sr_iov->vf; + ivvi.vlan = sr_iov->vlan; + ivvi.qos = sr_iov->qos; + ivvi.vlan_proto = htobe16(sr_iov->vlan_proto); + + r = sd_netlink_message_open_container(req, IFLA_VF_VLAN_LIST); + if (r < 0) + return r; + + r = sd_netlink_message_append_data(req, IFLA_VF_VLAN_INFO, &ivvi, sizeof(struct ifla_vf_vlan_info)); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(req); + if (r < 0) + return r; + } + + r = sd_netlink_message_close_container(req); + if (r < 0) + return r; + + r = sd_netlink_message_close_container(req); + if (r < 0) + return r; + + return 0; +} + +int sr_iov_get_num_vfs(sd_device *device, uint32_t *ret) { + const char *str; + uint32_t n; + int r; + + assert(device); + assert(ret); + + r = sd_device_get_sysattr_value(device, "device/sriov_numvfs", &str); + if (r < 0) + return r; + + r = safe_atou32(str, &n); + if (r < 0) + return r; + + *ret = n; + return 0; +} + +int sr_iov_set_num_vfs(sd_device *device, uint32_t num_vfs, OrderedHashmap *sr_iov_by_section) { + char val[DECIMAL_STR_MAX(uint32_t)]; + const char *str; + int r; + + assert(device); + + if (num_vfs == UINT32_MAX) { + uint32_t current_num_vfs; + SRIOV *sr_iov; + + /* If the number of virtual functions is not specified, then use the maximum number of VF + 1. */ + + num_vfs = 0; + ORDERED_HASHMAP_FOREACH(sr_iov, sr_iov_by_section) + num_vfs = MAX(num_vfs, sr_iov->vf + 1); + + if (num_vfs == 0) /* No VF is configured. */ + return 0; + + r = sr_iov_get_num_vfs(device, ¤t_num_vfs); + if (r < 0) + return log_device_debug_errno(device, r, "Failed to get the current number of SR-IOV virtual functions: %m"); + + /* Enough VFs already exist. */ + if (num_vfs <= current_num_vfs) + return 0; + + } else if (num_vfs == 0) { + r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", "0"); + if (r < 0) + log_device_debug_errno(device, r, "Failed to write device/sriov_numvfs sysfs attribute, ignoring: %m"); + + /* Gracefully handle the error in disabling VFs when the interface does not support SR-IOV. */ + return r == -ENOENT ? 0 : r; + } + + /* So, the interface does not have enough VFs. Before increasing the number of VFs, check the + * maximum allowed number of VFs from the sriov_totalvfs sysattr. Note that the sysattr + * currently exists only for PCI drivers. Hence, ignore -ENOENT. + * TODO: netdevsim provides the information in debugfs. */ + r = sd_device_get_sysattr_value(device, "device/sriov_totalvfs", &str); + if (r >= 0) { + uint32_t max_num_vfs; + + r = safe_atou32(str, &max_num_vfs); + if (r < 0) + return log_device_debug_errno(device, r, "Failed to parse device/sriov_totalvfs sysfs attribute '%s': %m", str); + + if (num_vfs > max_num_vfs) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(ERANGE), + "Specified number of virtual functions is out of range. " + "The maximum allowed value is %"PRIu32".", + max_num_vfs); + + } else if (r != -ENOENT) /* Currently, only PCI driver has the attribute. */ + return log_device_debug_errno(device, r, "Failed to read device/sriov_totalvfs sysfs attribute: %m"); + + xsprintf(val, "%"PRIu32, num_vfs); + r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", val); + if (r == -EBUSY) { + /* Some devices e.g. netdevsim refuse to set sriov_numvfs if it has non-zero value. */ + r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", "0"); + if (r >= 0) + r = sd_device_set_sysattr_value(device, "device/sriov_numvfs", val); + } + if (r < 0) + return log_device_debug_errno(device, r, "Failed to write device/sriov_numvfs sysfs attribute: %m"); + + log_device_debug(device, "device/sriov_numvfs sysfs attribute set to '%s'.", val); + return 0; +} + +static int sr_iov_section_verify(uint32_t num_vfs, SRIOV *sr_iov) { + assert(sr_iov); + + if (section_is_invalid(sr_iov->section)) + return -EINVAL; + + if (sr_iov->vf == UINT32_MAX) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: [SR-IOV] section without VirtualFunction= field configured. " + "Ignoring [SR-IOV] section from line %u.", + sr_iov->section->filename, sr_iov->section->line); + + if (sr_iov->vf >= num_vfs) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: VirtualFunction= must be smaller than the value specified in SR-IOVVirtualFunctions=. " + "Ignoring [SR-IOV] section from line %u.", + sr_iov->section->filename, sr_iov->section->line); + + return 0; +} + +int sr_iov_drop_invalid_sections(uint32_t num_vfs, OrderedHashmap *sr_iov_by_section) { + _cleanup_set_free_ Set *set = NULL; + SRIOV *sr_iov; + int r; + + ORDERED_HASHMAP_FOREACH(sr_iov, sr_iov_by_section) { + SRIOV *dup; + + if (sr_iov_section_verify(num_vfs, sr_iov) < 0) { + sr_iov_free(sr_iov); + continue; + } + + dup = set_remove(set, sr_iov); + if (dup) { + log_warning("%s: Conflicting [SR-IOV] section is specified at line %u and %u, " + "dropping the [SR-IOV] section specified at line %u.", + dup->section->filename, sr_iov->section->line, + dup->section->line, dup->section->line); + sr_iov_free(dup); + } + + r = set_ensure_put(&set, &sr_iov_hash_ops, sr_iov); + if (r < 0) + return log_oom(); + assert(r > 0); + } + + return 0; +} + +int config_parse_sr_iov_uint32( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; + OrderedHashmap **sr_iov_by_section = data; + uint32_t k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov); + if (r < 0) + return r; + + if (isempty(rvalue)) { + if (streq(lvalue, "VirtualFunction")) + sr_iov->vf = UINT32_MAX; + else if (streq(lvalue, "VLANId")) + sr_iov->vlan = 0; + else if (streq(lvalue, "QualityOfService")) + sr_iov->qos = 0; + else + assert_not_reached(); + + TAKE_PTR(sr_iov); + return 0; + } + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + if (streq(lvalue, "VLANId")) { + if (k == 0 || k > 4095) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV VLANId: %d", k); + return 0; + } + sr_iov->vlan = k; + } else if (streq(lvalue, "VirtualFunction")) { + if (k >= INT_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, "Invalid SR-IOV virtual function: %d", k); + return 0; + } + sr_iov->vf = k; + } else if (streq(lvalue, "QualityOfService")) + sr_iov->qos = k; + else + assert_not_reached(); + + TAKE_PTR(sr_iov); + return 0; +} + +int config_parse_sr_iov_vlan_proto( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; + OrderedHashmap **sr_iov_by_section = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov); + if (r < 0) + return r; + + if (isempty(rvalue) || streq(rvalue, "802.1Q")) + sr_iov->vlan_proto = ETH_P_8021Q; + else if (streq(rvalue, "802.1ad")) + sr_iov->vlan_proto = ETH_P_8021AD; + else { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "Invalid SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + TAKE_PTR(sr_iov); + return 0; +} + +int config_parse_sr_iov_link_state( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; + OrderedHashmap **sr_iov_by_section = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov); + if (r < 0) + return r; + + /* Unfortunately, SR_IOV_LINK_STATE_DISABLE is 2, not 0. So, we cannot use + * DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN() macro. */ + + if (isempty(rvalue)) { + sr_iov->link_state = _SR_IOV_LINK_STATE_INVALID; + TAKE_PTR(sr_iov); + return 0; + } + + if (streq(rvalue, "auto")) { + sr_iov->link_state = SR_IOV_LINK_STATE_AUTO; + TAKE_PTR(sr_iov); + return 0; + } + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + sr_iov->link_state = r ? SR_IOV_LINK_STATE_ENABLE : SR_IOV_LINK_STATE_DISABLE; + TAKE_PTR(sr_iov); + return 0; +} + +int config_parse_sr_iov_boolean( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; + OrderedHashmap **sr_iov_by_section = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov); + if (r < 0) + return r; + + if (isempty(rvalue)) { + if (streq(lvalue, "MACSpoofCheck")) + sr_iov->vf_spoof_check_setting = -1; + else if (streq(lvalue, "QueryReceiveSideScaling")) + sr_iov->query_rss = -1; + else if (streq(lvalue, "Trust")) + sr_iov->trust = -1; + else + assert_not_reached(); + + TAKE_PTR(sr_iov); + return 0; + } + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse '%s=', ignoring: %s", lvalue, rvalue); + return 0; + } + + if (streq(lvalue, "MACSpoofCheck")) + sr_iov->vf_spoof_check_setting = r; + else if (streq(lvalue, "QueryReceiveSideScaling")) + sr_iov->query_rss = r; + else if (streq(lvalue, "Trust")) + sr_iov->trust = r; + else + assert_not_reached(); + + TAKE_PTR(sr_iov); + return 0; +} + +int config_parse_sr_iov_mac( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(sr_iov_free_or_set_invalidp) SRIOV *sr_iov = NULL; + OrderedHashmap **sr_iov_by_section = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = sr_iov_new_static(sr_iov_by_section, filename, section_line, &sr_iov); + if (r < 0) + return r; + + if (isempty(rvalue)) { + sr_iov->mac = ETHER_ADDR_NULL; + TAKE_PTR(sr_iov); + return 0; + } + + r = parse_ether_addr(rvalue, &sr_iov->mac); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse SR-IOV '%s=', ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + TAKE_PTR(sr_iov); + return 0; +} + +int config_parse_sr_iov_num_vfs( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint32_t n, *num_vfs = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *num_vfs = UINT32_MAX; + return 0; + } + + r = safe_atou32(rvalue, &n); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + if (n > INT_MAX) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "The number of SR-IOV virtual functions is too large. It must be equal to " + "or smaller than 2147483647. Ignoring assignment: %"PRIu32, n); + return 0; + } + + *num_vfs = n; + return 0; +} diff --git a/src/shared/netif-sriov.h b/src/shared/netif-sriov.h new file mode 100644 index 000000000..ee769575e --- /dev/null +++ b/src/shared/netif-sriov.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "sd-device.h" + +#include "conf-parser.h" +#include "ether-addr-util.h" +#include "hashmap.h" + +typedef enum SRIOVLinkState { + SR_IOV_LINK_STATE_AUTO = IFLA_VF_LINK_STATE_AUTO, + SR_IOV_LINK_STATE_ENABLE = IFLA_VF_LINK_STATE_ENABLE, + SR_IOV_LINK_STATE_DISABLE = IFLA_VF_LINK_STATE_DISABLE, + _SR_IOV_LINK_STATE_MAX, + _SR_IOV_LINK_STATE_INVALID = -EINVAL, +} SRIOVLinkState; + +typedef struct SRIOV { + ConfigSection *section; + OrderedHashmap *sr_iov_by_section; + + uint32_t vf; /* 0 - 2147483646 */ + uint32_t vlan; /* 0 - 4095, 0 disables VLAN filter */ + uint32_t qos; + uint16_t vlan_proto; /* ETH_P_8021Q or ETH_P_8021AD */ + int vf_spoof_check_setting; + int query_rss; + int trust; + SRIOVLinkState link_state; + struct ether_addr mac; +} SRIOV; + +SRIOV *sr_iov_free(SRIOV *sr_iov); +void sr_iov_hash_func(const SRIOV *sr_iov, struct siphash *state); +int sr_iov_compare_func(const SRIOV *s1, const SRIOV *s2); +int sr_iov_set_netlink_message(SRIOV *sr_iov, sd_netlink_message *req); +int sr_iov_get_num_vfs(sd_device *device, uint32_t *ret); +int sr_iov_set_num_vfs(sd_device *device, uint32_t num_vfs, OrderedHashmap *sr_iov_by_section); +int sr_iov_drop_invalid_sections(uint32_t num_vfs, OrderedHashmap *sr_iov_by_section); + +DEFINE_SECTION_CLEANUP_FUNCTIONS(SRIOV, sr_iov_free); + +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_uint32); +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_boolean); +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_link_state); +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_vlan_proto); +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_mac); +CONFIG_PARSER_PROTOTYPE(config_parse_sr_iov_num_vfs); diff --git a/src/shared/netif-util.c b/src/shared/netif-util.c index 603d4de10..f56c5646c 100644 --- a/src/shared/netif-util.c +++ b/src/shared/netif-util.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include #include #include "arphrd-util.h" @@ -11,6 +12,20 @@ #include "sparse-endian.h" #include "strv.h" +bool netif_has_carrier(uint8_t operstate, unsigned flags) { + /* see Documentation/networking/operstates.txt in the kernel sources */ + + if (operstate == IF_OPER_UP) + return true; + + if (operstate != IF_OPER_UNKNOWN) + return false; + + /* operstate may not be implemented, so fall back to flags */ + return FLAGS_SET(flags, IFF_LOWER_UP | IFF_RUNNING) && + !FLAGS_SET(flags, IFF_DORMANT); +} + int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) { const char *t; char *p; @@ -39,14 +54,15 @@ int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) { } const char *net_get_persistent_name(sd_device *device) { - const char *name, *field; - assert(device); /* fetch some persistent data unique (on this machine) to this device */ - FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") + FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") { + const char *name; + if (sd_device_get_property_value(device, field, &name) >= 0) return name; + } return NULL; } diff --git a/src/shared/netif-util.h b/src/shared/netif-util.h index c26dab63c..fb6a27cce 100644 --- a/src/shared/netif-util.h +++ b/src/shared/netif-util.h @@ -9,6 +9,7 @@ #include "ether-addr-util.h" +bool netif_has_carrier(uint8_t operstate, unsigned flags); int net_get_type_string(sd_device *device, uint16_t iftype, char **ret); const char *net_get_persistent_name(sd_device *device); int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret); diff --git a/src/shared/nscd-flush.c b/src/shared/nscd-flush.c index 065503063..95dfe24b2 100644 --- a/src/shared/nscd-flush.c +++ b/src/shared/nscd-flush.c @@ -132,7 +132,6 @@ static int nscd_flush_cache_one(const char *database, usec_t end) { int nscd_flush_cache(char **databases) { usec_t end; int r = 0; - char **i; /* Tries to invalidate the specified database in nscd. We do this carefully, with a 5s timeout, so that we * don't block indefinitely on another service. */ diff --git a/src/shared/pager.c b/src/shared/pager.c index f75ef62d2..1a93deb62 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -86,6 +86,7 @@ static int no_quit_on_interrupt(int exe_name_fd, const char *less_opts) { void pager_open(PagerFlags flags) { _cleanup_close_pair_ int fd[2] = { -1, -1 }, exe_name_pipe[2] = { -1, -1 }; _cleanup_strv_free_ char **pager_args = NULL; + _cleanup_free_ char *l = NULL; const char *pager, *less_opts; int r; @@ -131,15 +132,19 @@ void pager_open(PagerFlags flags) { less_opts = getenv("SYSTEMD_LESS"); if (!less_opts) less_opts = "FRSXMK"; - if (flags & PAGER_JUMP_TO_END) - less_opts = strjoina(less_opts, " +G"); + if (flags & PAGER_JUMP_TO_END) { + l = strjoin(less_opts, " +G"); + if (!l) + return (void) log_oom(); + less_opts = l; + } /* We set SIGINT as PR_DEATHSIG signal here, to match the "K" parameter we set in $LESS, which enables SIGINT behaviour. */ r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGINT|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pager_pid); if (r < 0) return; if (r == 0) { - const char *less_charset, *exe; + const char *less_charset; /* In the child start the pager */ diff --git a/src/shared/pkcs11-util.c b/src/shared/pkcs11-util.c index 67ea44515..80feeb1fa 100644 --- a/src/shared/pkcs11-util.c +++ b/src/shared/pkcs11-util.c @@ -275,15 +275,16 @@ int pkcs11_token_login( for (unsigned tries = 0; tries < 3; tries++) { _cleanup_strv_free_erase_ char **passwords = NULL; - char **i, *e; + _cleanup_(erase_and_freep) char *envpin = NULL; - e = getenv("PIN"); - if (e) { - passwords = strv_new(e); + r = getenv_steal_erase("PIN", &envpin); + if (r < 0) + return log_error_errno(r, "Failed to acquire PIN from environment: %m"); + if (r > 0) { + passwords = strv_new(envpin); if (!passwords) return log_oom(); - assert_se(unsetenv_erase("PIN") >= 0); } else if (headless) return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the 'PIN' environment variable."); else { diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c index 26daec345..98619c25d 100644 --- a/src/shared/pretty-print.c +++ b/src/shared/pretty-print.c @@ -168,7 +168,6 @@ static int cat_file(const char *filename, bool newline) { } int cat_files(const char *file, char **dropins, CatFlags flags) { - char **path; int r; if (file) { @@ -284,10 +283,9 @@ static int guess_type(const char **name, char ***prefixes, bool *is_collection, int conf_files_cat(const char *root, const char *name) { _cleanup_strv_free_ char **dirs = NULL, **files = NULL; _cleanup_free_ char *path = NULL; - char **prefix, **prefixes = NULL; /* explicit initialization to appease gcc */ + char **prefixes = NULL; /* explicit initialization to appease gcc */ bool is_collection; const char *extension; - char **t; int r; r = guess_type(&name, &prefixes, &is_collection, &extension); diff --git a/src/shared/psi-util.c b/src/shared/psi-util.c index 009095e8c..8bdd0d4a8 100644 --- a/src/shared/psi-util.c +++ b/src/shared/psi-util.c @@ -106,8 +106,6 @@ int read_resource_pressure(const char *path, PressureType type, ResourcePressure } int is_pressure_supported(void) { - const char *p; - /* The pressure files, both under /proc and in cgroups, will exist * even if the kernel has PSI support disabled; we have to read * the file to make sure it doesn't return -EOPNOTSUPP */ diff --git a/src/shared/qrcode-util.c b/src/shared/qrcode-util.c index db48c7361..1ad3474b3 100644 --- a/src/shared/qrcode-util.c +++ b/src/shared/qrcode-util.c @@ -11,6 +11,9 @@ #include "terminal-util.h" #define ANSI_WHITE_ON_BLACK "\033[40;37;1m" +#define UNICODE_FULL_BLOCK u8"█" +#define UNICODE_LOWER_HALF_BLOCK u8"▄" +#define UNICODE_UPPER_HALF_BLOCK u8"▀" static void *qrcode_dl = NULL; @@ -30,7 +33,7 @@ static void print_border(FILE *output, unsigned width) { fputs(ANSI_WHITE_ON_BLACK, output); for (unsigned x = 0; x < 4 + width + 4; x++) - fputs("\342\226\210", output); + fputs(UNICODE_FULL_BLOCK, output); fputs(ANSI_NORMAL "\n", output); } @@ -50,7 +53,7 @@ static void write_qrcode(FILE *output, QRcode *qr) { fputs(ANSI_WHITE_ON_BLACK, output); for (unsigned x = 0; x < 4; x++) - fputs("\342\226\210", output); + fputs(UNICODE_FULL_BLOCK, output); for (unsigned x = 0; x < (unsigned) qr->width; x++) { bool a, b; @@ -61,15 +64,15 @@ static void write_qrcode(FILE *output, QRcode *qr) { if (a && b) fputc(' ', output); else if (a) - fputs("\342\226\204", output); + fputs(UNICODE_LOWER_HALF_BLOCK, output); else if (b) - fputs("\342\226\200", output); + fputs(UNICODE_UPPER_HALF_BLOCK, output); else - fputs("\342\226\210", output); + fputs(UNICODE_FULL_BLOCK, output); } for (unsigned x = 0; x < 4; x++) - fputs("\342\226\210", output); + fputs(UNICODE_FULL_BLOCK, output); fputs(ANSI_NORMAL "\n", output); } diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 32bd8aa73..e597a156c 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -1837,7 +1837,6 @@ int seccomp_restrict_archs(Set *archs) { int parse_syscall_archs(char **l, Set **ret_archs) { _cleanup_set_free_ Set *archs = NULL; - char **s; int r; assert(l); diff --git a/src/shared/serialize.c b/src/shared/serialize.c index 47996b9ea..cd4828635 100644 --- a/src/shared/serialize.c +++ b/src/shared/serialize.c @@ -117,7 +117,6 @@ int serialize_dual_timestamp(FILE *f, const char *name, const dual_timestamp *t) int serialize_strv(FILE *f, const char *key, char **l) { int ret = 0, r; - char **i; /* Returns the first error, or positive if anything was serialized, 0 otherwise. */ diff --git a/src/shared/specifier.c b/src/shared/specifier.c index 1fd76b1d1..16eb8830d 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -18,6 +18,8 @@ #include "id128-util.h" #include "macro.h" #include "os-util.h" +#include "path-lookup.h" +#include "path-util.h" #include "specifier.h" #include "string-util.h" #include "strv.h" @@ -35,11 +37,11 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[], const char *root, const void *userdata, char **ret) { _cleanup_free_ char *result = NULL; bool percent = false; - const char *f; size_t l; char *t; int r; + assert(ret); assert(text); assert(table); @@ -48,8 +50,10 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[ return -ENOMEM; t = result; - for (f = text; *f != '\0'; f++, l--) { + for (const char *f = text; *f != '\0'; f++, l--) { if (percent) { + percent = false; + if (*f == '%') *(t++) = '%'; else { @@ -66,6 +70,8 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[ r = i->lookup(i->specifier, i->data, root, userdata, &w); if (r < 0) return r; + if (isempty(w)) + continue; j = t - result; k = strlen(w); @@ -82,8 +88,6 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[ *(t++) = *f; } } - - percent = false; } else if (*f == '%') percent = true; else @@ -108,27 +112,59 @@ int specifier_printf(const char *text, size_t max_length, const Specifier table[ /* Generic handler for simple string replacements */ int specifier_string(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - char *n; + char *n = NULL; - n = strdup(strempty(data)); - if (!n) - return -ENOMEM; + assert(ret); + + if (!isempty(data)) { + n = strdup(data); + if (!n) + return -ENOMEM; + } *ret = n; return 0; } +int specifier_real_path(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + const char *path = data; + + assert(ret); + + if (!path) + return -ENOENT; + + return chase_symlinks(path, root, 0, ret, NULL); +} + +int specifier_real_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + _cleanup_free_ char *path = NULL; + int r; + + assert(ret); + + r = specifier_real_path(specifier, data, root, userdata, &path); + if (r < 0) + return r; + + assert(path); + return path_extract_directory(path, ret); +} + int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { sd_id128_t id; char *n; int r; + assert(ret); + if (root) { _cleanup_close_ int fd = -1; fd = chase_symlinks_and_open("/etc/machine-id", root, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL); if (fd < 0) - return fd; + /* Translate error for missing os-release file to EUNATCH. */ + return fd == -ENOENT ? -EUNATCH : fd; r = id128_read_fd(fd, ID128_PLAIN, &id); } else @@ -149,6 +185,8 @@ int specifier_boot_id(char specifier, const void *data, const char *root, const char *n; int r; + assert(ret); + r = sd_id128_get_boot(&id); if (r < 0) return r; @@ -164,6 +202,8 @@ int specifier_boot_id(char specifier, const void *data, const char *root, const int specifier_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) { char *n; + assert(ret); + n = gethostname_malloc(); if (!n) return -ENOMEM; @@ -175,6 +215,8 @@ int specifier_host_name(char specifier, const void *data, const char *root, cons int specifier_short_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) { char *n; + assert(ret); + n = gethostname_short_malloc(); if (!n) return -ENOMEM; @@ -183,13 +225,28 @@ int specifier_short_host_name(char specifier, const void *data, const char *root return 0; } +int specifier_pretty_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + char *n = NULL; + + assert(ret); + + if (get_pretty_hostname(&n) < 0) { + n = gethostname_short_malloc(); + if (!n) + return -ENOMEM; + } + + *ret = n; + return 0; +} + int specifier_kernel_release(char specifier, const void *data, const char *root, const void *userdata, char **ret) { struct utsname uts; char *n; - int r; - r = uname(&uts); - if (r < 0) + assert(ret); + + if (uname(&uts) < 0) return -errno; n = strdup(uts.release); @@ -203,6 +260,8 @@ int specifier_kernel_release(char specifier, const void *data, const char *root, int specifier_architecture(char specifier, const void *data, const char *root, const void *userdata, char **ret) { char *t; + assert(ret); + t = strdup(architecture_to_string(uname_architecture())); if (!t) return -ENOMEM; @@ -211,53 +270,55 @@ int specifier_architecture(char specifier, const void *data, const char *root, c return 0; } -static int specifier_os_release_common(const char *field, const char *root, char **ret) { - char *t = NULL; +/* Note: fields in /etc/os-release might quite possibly be missing, even if everything is entirely valid + * otherwise. We'll return an empty value or NULL in that case from the functions below. But if the + * os-release file is missing, we'll return -EUNATCH. This means that something is seriously wrong with the + * installation. */ + +static int parse_os_release_specifier(const char *root, const char *id, char **ret) { int r; - r = parse_os_release(root, field, &t); - if (r < 0) - return r; - if (!t) { - /* fields in /etc/os-release might quite possibly be missing, even if everything is entirely - * valid otherwise. Let's hence return "" in that case. */ - t = strdup(""); - if (!t) - return -ENOMEM; - } + assert(ret); - *ret = t; - return 0; + /* Translate error for missing os-release file to EUNATCH. */ + r = parse_os_release(root, id, ret); + return r == -ENOENT ? -EUNATCH : r; } int specifier_os_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - return specifier_os_release_common("ID", root, ret); + return parse_os_release_specifier(root, "ID", ret); } int specifier_os_version_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - return specifier_os_release_common("VERSION_ID", root, ret); + return parse_os_release_specifier(root, "VERSION_ID", ret); } int specifier_os_build_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - return specifier_os_release_common("BUILD_ID", root, ret); + return parse_os_release_specifier(root, "BUILD_ID", ret); } int specifier_os_variant_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - return specifier_os_release_common("VARIANT_ID", root, ret); + return parse_os_release_specifier(root, "VARIANT_ID", ret); } int specifier_os_image_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - return specifier_os_release_common("IMAGE_ID", root, ret); + return parse_os_release_specifier(root, "IMAGE_ID", ret); } int specifier_os_image_version(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - return specifier_os_release_common("IMAGE_VERSION", root, ret); + return parse_os_release_specifier(root, "IMAGE_VERSION", ret); } int specifier_group_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + LookupScope scope = PTR_TO_INT(data); char *t; - t = gid_to_name(getgid()); + assert(ret); + + if (scope == LOOKUP_SCOPE_GLOBAL) + return -EINVAL; + + t = gid_to_name(scope == LOOKUP_SCOPE_USER ? getgid() : 0); if (!t) return -ENOMEM; @@ -266,23 +327,42 @@ int specifier_group_name(char specifier, const void *data, const char *root, con } int specifier_group_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { - if (asprintf(ret, UID_FMT, getgid()) < 0) + LookupScope scope = PTR_TO_INT(data); + gid_t gid; + + assert(ret); + + if (scope == LOOKUP_SCOPE_GLOBAL) + return -EINVAL; + + gid = scope == LOOKUP_SCOPE_USER ? getgid() : 0; + + if (asprintf(ret, UID_FMT, gid) < 0) return -ENOMEM; return 0; } int specifier_user_name(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + LookupScope scope = PTR_TO_INT(data); + uid_t uid; char *t; - /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want to be able - * to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed. + assert(ret); - * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain consistent with - * specifer_user_id() below. + if (scope == LOOKUP_SCOPE_GLOBAL) + return -EINVAL; + + uid = scope == LOOKUP_SCOPE_USER ? getuid() : 0; + + /* If we are UID 0 (root), this will not result in NSS, otherwise it might. This is good, as we want + * to be able to run this in PID 1, where our user ID is 0, but where NSS lookups are not allowed. + + * We don't use getusername_malloc() here, because we don't want to look at $USER, to remain + * consistent with specifer_user_id() below. */ - t = uid_to_name(getuid()); + t = uid_to_name(uid); if (!t) return -ENOMEM; @@ -291,14 +371,24 @@ int specifier_user_name(char specifier, const void *data, const char *root, cons } int specifier_user_id(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + LookupScope scope = PTR_TO_INT(data); + uid_t uid; - if (asprintf(ret, UID_FMT, getuid()) < 0) + assert(ret); + + if (scope == LOOKUP_SCOPE_GLOBAL) + return -EINVAL; + + uid = scope == LOOKUP_SCOPE_USER ? getuid() : 0; + + if (asprintf(ret, UID_FMT, uid) < 0) return -ENOMEM; return 0; } int specifier_user_home(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + assert(ret); /* On PID 1 (which runs as root) this will not result in NSS, * which is good. See above */ @@ -307,6 +397,7 @@ int specifier_user_home(char specifier, const void *data, const char *root, cons } int specifier_user_shell(char specifier, const void *data, const char *root, const void *userdata, char **ret) { + assert(ret); /* On PID 1 (which runs as root) this will not result in NSS, * which is good. See above */ @@ -319,6 +410,8 @@ int specifier_tmp_dir(char specifier, const void *data, const char *root, const char *copy; int r; + assert(ret); + if (root) /* If root dir is set, don't honour $TMP or similar */ p = "/tmp"; else { @@ -339,6 +432,8 @@ int specifier_var_tmp_dir(char specifier, const void *data, const char *root, co char *copy; int r; + assert(ret); + if (root) p = "/var/tmp"; else { diff --git a/src/shared/specifier.h b/src/shared/specifier.h index c433ee2d6..78b10f846 100644 --- a/src/shared/specifier.h +++ b/src/shared/specifier.h @@ -14,11 +14,14 @@ typedef struct Specifier { int specifier_printf(const char *text, size_t max_length, const Specifier table[], const char *root, const void *userdata, char **ret); int specifier_string(char specifier, const void *data, const char *root, const void *userdata, char **ret); +int specifier_real_path(char specifier, const void *data, const char *root, const void *userdata, char **ret); +int specifier_real_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret); int specifier_machine_id(char specifier, const void *data, const char *root, const void *userdata, char **ret); int specifier_boot_id(char specifier, const void *data, const char *root, const void *userdata, char **ret); int specifier_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret); int specifier_short_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret); +int specifier_pretty_host_name(char specifier, const void *data, const char *root, const void *userdata, char **ret); int specifier_kernel_release(char specifier, const void *data, const char *root, const void *userdata, char **ret); int specifier_architecture(char specifier, const void *data, const char *root, const void *userdata, char **ret); int specifier_os_id(char specifier, const void *data, const char *root, const void *userdata, char **ret); @@ -73,6 +76,7 @@ int specifier_var_tmp_dir(char specifier, const void *data, const char *root, co { 'B', specifier_os_build_id, NULL }, \ { 'H', specifier_host_name, NULL }, \ { 'l', specifier_short_host_name, NULL }, \ + { 'R', specifier_pretty_host_name,NULL }, \ { 'm', specifier_machine_id, NULL }, \ { 'M', specifier_os_image_id, NULL }, \ { 'o', specifier_os_id, NULL }, \ @@ -80,11 +84,11 @@ int specifier_var_tmp_dir(char specifier, const void *data, const char *root, co { 'w', specifier_os_version_id, NULL }, \ { 'W', specifier_os_variant_id, NULL } -#define COMMON_CREDS_SPECIFIERS \ - { 'g', specifier_group_name, NULL }, \ - { 'G', specifier_group_id, NULL }, \ - { 'u', specifier_user_name, NULL }, \ - { 'U', specifier_user_id, NULL } +#define COMMON_CREDS_SPECIFIERS(scope) \ + { 'g', specifier_group_name, INT_TO_PTR(scope) }, \ + { 'G', specifier_group_id, INT_TO_PTR(scope) }, \ + { 'u', specifier_user_name, INT_TO_PTR(scope) }, \ + { 'U', specifier_user_id, INT_TO_PTR(scope) } #define COMMON_TMP_SPECIFIERS \ { 'T', specifier_tmp_dir, NULL }, \ diff --git a/src/shared/switch-root.c b/src/shared/switch-root.c index 99cd57419..1a444841f 100644 --- a/src/shared/switch-root.c +++ b/src/shared/switch-root.c @@ -33,7 +33,6 @@ int switch_root(const char *new_root, _cleanup_free_ char *resolved_old_root_after = NULL; _cleanup_close_ int old_root_fd = -1; bool old_root_remove; - const char *i; int r; assert(new_root); @@ -64,12 +63,12 @@ int switch_root(const char *new_root, if (mount(NULL, "/", NULL, MS_REC|MS_PRIVATE, NULL) < 0) return log_error_errno(errno, "Failed to set \"/\" mount propagation to private: %m"); - FOREACH_STRING(i, "/sys", "/dev", "/run", "/proc") { + FOREACH_STRING(path, "/sys", "/dev", "/run", "/proc") { _cleanup_free_ char *chased = NULL; - r = chase_symlinks(i, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &chased, NULL); + r = chase_symlinks(path, new_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &chased, NULL); if (r < 0) - return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, i); + return log_error_errno(r, "Failed to resolve %s/%s: %m", new_root, path); if (r > 0) { /* Already exists. Let's see if it is a mount point already. */ r = path_is_mount_point(chased, NULL, 0); @@ -81,8 +80,8 @@ int switch_root(const char *new_root, /* Doesn't exist yet? */ (void) mkdir_p_label(chased, 0755); - if (mount(i, chased, NULL, mount_flags, NULL) < 0) - return log_error_errno(errno, "Failed to mount %s to %s: %m", i, chased); + if (mount(path, chased, NULL, mount_flags, NULL) < 0) + return log_error_errno(errno, "Failed to mount %s to %s: %m", path, chased); } /* Do not fail if base_filesystem_create() fails. Not all switch roots are like base_filesystem_create() wants diff --git a/src/shared/tests.c b/src/shared/tests.c index b00006b41..70fbbb45a 100644 --- a/src/shared/tests.c +++ b/src/shared/tests.c @@ -25,6 +25,7 @@ #include "cgroup-util.h" #include "env-file.h" #include "env-util.h" +#include "fd-util.h" #include "fs-util.h" #include "log.h" #include "namespace-util.h" @@ -33,6 +34,7 @@ #include "random-util.h" #include "strv.h" #include "tests.h" +#include "tmpfile-util.h" char* setup_fake_runtime_dir(void) { char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p; @@ -49,7 +51,6 @@ static void load_testdata_env(void) { _cleanup_free_ char *s = NULL; _cleanup_free_ char *envpath = NULL; _cleanup_strv_free_ char **pairs = NULL; - char **k, **v; if (called) return; @@ -133,6 +134,23 @@ int log_tests_skipped_errno(int r, const char *message) { return EXIT_TEST_SKIP; } +int write_tmpfile(char *pattern, const char *contents) { + _cleanup_close_ int fd = -1; + + assert(pattern); + assert(contents); + + fd = mkostemp_safe(pattern); + if (fd < 0) + return fd; + + ssize_t l = strlen(contents); + errno = 0; + if (write(fd, contents, l) != l) + return errno_or_else(EIO); + return 0; +} + bool have_namespaces(void) { siginfo_t si = {}; pid_t pid; @@ -307,16 +325,15 @@ const char *ci_environment(void) { * just the general CI environment type, but also whether we're sanitizing or not, etc. The caller is * expected to use strstr on the returned value. */ static const char *ans = POINTER_MAX; - const char *p; int r; if (ans != POINTER_MAX) return ans; /* We allow specifying the environment with $CITYPE. Nobody uses this so far, but we are ready. */ - p = getenv("CITYPE"); - if (!isempty(p)) - return (ans = p); + const char *citype = getenv("CITYPE"); + if (!isempty(citype)) + return (ans = citype); if (getenv_bool("TRAVIS") > 0) return (ans = "travis"); @@ -327,12 +344,12 @@ const char *ci_environment(void) { if (getenv("AUTOPKGTEST_ARTIFACTS") || getenv("AUTOPKGTEST_TMP")) return (ans = "autopkgtest"); - FOREACH_STRING(p, "CI", "CONTINOUS_INTEGRATION") { + FOREACH_STRING(var, "CI", "CONTINOUS_INTEGRATION") { /* Those vars are booleans according to Semaphore and Travis docs: * https://docs.travis-ci.com/user/environment-variables/#default-environment-variables * https://docs.semaphoreci.com/ci-cd-environment/environment-variables/#ci */ - r = getenv_bool(p); + r = getenv_bool(var); if (r > 0) return (ans = "unknown"); /* Some other unknown thing */ if (r == 0) diff --git a/src/shared/tests.h b/src/shared/tests.h index 3b93aab49..ade527590 100644 --- a/src/shared/tests.h +++ b/src/shared/tests.h @@ -6,6 +6,7 @@ #include "sd-daemon.h" #include "macro.h" +#include "static-destruct.h" #include "util.h" static inline bool manager_errno_skip_test(int r) { @@ -29,6 +30,8 @@ void test_setup_logging(int level); int log_tests_skipped(const char *message); int log_tests_skipped_errno(int r, const char *message); +int write_tmpfile(char *pattern, const char *contents); + bool have_namespaces(void); /* We use the small but non-trivial limit here */ @@ -109,15 +112,28 @@ static inline int run_test_table(void) { return r; } -#define DEFINE_CUSTOM_TEST_MAIN(log_level, intro, outro) \ - int main(int argc, char *argv[]) { \ - int _r = EXIT_SUCCESS; \ - test_setup_logging(log_level); \ - save_argc_argv(argc, argv); \ - intro; \ - _r = run_test_table(); \ - outro; \ - return _r; \ +#define DEFINE_TEST_MAIN_FULL(log_level, intro, outro) \ + int main(int argc, char *argv[]) { \ + int (*_intro)(void) = intro; \ + int (*_outro)(void) = outro; \ + int _r, _q; \ + test_setup_logging(log_level); \ + save_argc_argv(argc, argv); \ + _r = _intro ? _intro() : EXIT_SUCCESS; \ + if (_r == EXIT_SUCCESS) \ + _r = run_test_table(); \ + _q = _outro ? _outro() : EXIT_SUCCESS; \ + static_destruct(); \ + if (_r < 0) \ + return EXIT_FAILURE; \ + if (_r != EXIT_SUCCESS) \ + return _r; \ + if (_q < 0) \ + return EXIT_FAILURE; \ + return _q; \ } -#define DEFINE_TEST_MAIN(log_level) DEFINE_CUSTOM_TEST_MAIN(log_level, , ) +#define DEFINE_TEST_MAIN_WITH_INTRO(log_level, intro) \ + DEFINE_TEST_MAIN_FULL(log_level, intro, NULL) +#define DEFINE_TEST_MAIN(log_level) \ + DEFINE_TEST_MAIN_FULL(log_level, NULL, NULL) diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c index 70a292943..3dfc5d8b7 100644 --- a/src/shared/tpm2-util.c +++ b/src/shared/tpm2-util.c @@ -14,6 +14,7 @@ #include "hexdecoct.h" #include "memory-util.h" #include "random-util.h" +#include "sha256.h" #include "time-util.h" static void *libtss2_esys_dl = NULL; @@ -30,10 +31,13 @@ TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1, ESYS_ TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion) = NULL; TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle) = NULL; TSS2_RC (*sym_Esys_PCR_Read)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1,ESYS_TR shandle2, ESYS_TR shandle3, const TPML_PCR_SELECTION *pcrSelectionIn, UINT32 *pcrUpdateCounter, TPML_PCR_SELECTION **pcrSelectionOut, TPML_DIGEST **pcrValues); +TSS2_RC (*sym_Esys_PolicyAuthValue)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3) = NULL; TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest) = NULL; TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs) = NULL; TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle) = NULL; TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType) = NULL; +TSS2_RC (*sym_Esys_TRSess_SetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION flags, TPMA_SESSION mask); +TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue) = NULL; TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData) = NULL; const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc) = NULL; @@ -58,10 +62,13 @@ int dlopen_tpm2(void) { DLSYM_ARG(Esys_Initialize), DLSYM_ARG(Esys_Load), DLSYM_ARG(Esys_PCR_Read), + DLSYM_ARG(Esys_PolicyAuthValue), DLSYM_ARG(Esys_PolicyGetDigest), DLSYM_ARG(Esys_PolicyPCR), DLSYM_ARG(Esys_StartAuthSession), DLSYM_ARG(Esys_Startup), + DLSYM_ARG(Esys_TRSess_SetAttributes), + DLSYM_ARG(Esys_TR_SetAuth), DLSYM_ARG(Esys_Unseal)); if (r < 0) return r; @@ -590,10 +597,74 @@ static int tpm2_get_best_pcr_bank( return 0; } +static int tpm2_make_encryption_session( + ESYS_CONTEXT *c, + ESYS_TR tpmKey, + ESYS_TR *ret_session) { + + static const TPMT_SYM_DEF symmetric = { + .algorithm = TPM2_ALG_AES, + .keyBits = { + .aes = 128, + }, + .mode = { + .aes = TPM2_ALG_CFB, + }, + }; + const TPMA_SESSION sessionAttributes = TPMA_SESSION_DECRYPT | TPMA_SESSION_ENCRYPT | + TPMA_SESSION_CONTINUESESSION; + ESYS_TR session = ESYS_TR_NONE; + TSS2_RC rc; + + assert(c); + + log_debug("Starting HMAC encryption session."); + + /* Start a salted, unbound HMAC session with a well-known key (e.g. primary key) as tpmKey, which + * means that the random salt will be encrypted with the well-known key. That way, only the TPM can + * recover the salt, which is then used for key derivation. */ + rc = sym_Esys_StartAuthSession( + c, + tpmKey, + ESYS_TR_NONE, + ESYS_TR_NONE, + ESYS_TR_NONE, + ESYS_TR_NONE, + NULL, + TPM2_SE_HMAC, + &symmetric, + TPM2_ALG_SHA256, + &session); + if (rc != TSS2_RC_SUCCESS) + return log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to open session in TPM: %s", sym_Tss2_RC_Decode(rc)); + + /* Enable parameter encryption/decryption with AES in CFB mode. Together with HMAC digests (which are + * always used for sessions), this provides confidentiality, integrity and replay protection for + * operations that use this session. */ + rc = sym_Esys_TRSess_SetAttributes(c, session, sessionAttributes, 0xff); + if (rc != TSS2_RC_SUCCESS) + return log_error_errno( + SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to configure TPM session: %s", + sym_Tss2_RC_Decode(rc)); + + if (ret_session) { + *ret_session = session; + session = ESYS_TR_NONE; + } + + session = flush_context_verbose(c, session); + return 0; +} + static int tpm2_make_pcr_session( ESYS_CONTEXT *c, + ESYS_TR tpmKey, + ESYS_TR parent_session, uint32_t pcr_mask, uint16_t pcr_bank, /* If UINT16_MAX, pick best bank automatically, otherwise specify bank explicitly. */ + bool use_pin, ESYS_TR *ret_session, TPM2B_DIGEST **ret_policy_digest, TPMI_ALG_HASH *ret_pcr_bank) { @@ -639,9 +710,9 @@ static int tpm2_make_pcr_session( rc = sym_Esys_StartAuthSession( c, + tpmKey, ESYS_TR_NONE, - ESYS_TR_NONE, - ESYS_TR_NONE, + parent_session, ESYS_TR_NONE, ESYS_TR_NONE, NULL, @@ -669,6 +740,21 @@ static int tpm2_make_pcr_session( goto finish; } + if (use_pin) { + rc = sym_Esys_PolicyAuthValue( + c, + session, + ESYS_TR_NONE, + ESYS_TR_NONE, + ESYS_TR_NONE); + if (rc != TSS2_RC_SUCCESS) { + r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to add authValue policy to TPM: %s", + sym_Tss2_RC_Decode(rc)); + goto finish; + } + } + if (DEBUG_LOGGING || ret_policy_digest) { log_debug("Acquiring policy digest."); @@ -717,9 +803,22 @@ finish: return r; } +static void hash_pin(const char *pin, size_t len, uint8_t ret_digest[static SHA256_DIGEST_SIZE]) { + struct sha256_ctx hash; + + assert(pin); + + sha256_init_ctx(&hash); + sha256_process_bytes(pin, len, &hash); + sha256_finish_ctx(&hash, ret_digest); + + explicit_bzero_safe(&hash, sizeof(hash)); +} + int tpm2_seal( const char *device, uint32_t pcr_mask, + const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, @@ -737,7 +836,7 @@ int tpm2_seal( _cleanup_(erase_and_freep) void *secret = NULL; _cleanup_free_ void *blob = NULL, *hash = NULL; TPM2B_SENSITIVE_CREATE hmac_sensitive; - ESYS_TR primary = ESYS_TR_NONE; + ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE; TPMI_ALG_PUBLIC primary_alg; TPM2B_PUBLIC hmac_template; TPMI_ALG_HASH pcr_bank; @@ -782,7 +881,12 @@ int tpm2_seal( if (r < 0) return r; - r = tpm2_make_pcr_session(c.esys_context, pcr_mask, UINT16_MAX, NULL, &policy_digest, &pcr_bank); + r = tpm2_make_encryption_session(c.esys_context, primary, &session); + if (r < 0) + goto finish; + + r = tpm2_make_pcr_session(c.esys_context, primary, session, pcr_mask, UINT16_MAX, !!pin, NULL, + &policy_digest, &pcr_bank); if (r < 0) goto finish; @@ -813,6 +917,10 @@ int tpm2_seal( .size = sizeof(hmac_sensitive.sensitive), .sensitive.data.size = 32, }; + if (pin) { + hash_pin(pin, strlen(pin), hmac_sensitive.sensitive.userAuth.buffer); + hmac_sensitive.sensitive.userAuth.size = SHA256_DIGEST_SIZE; + } assert(sizeof(hmac_sensitive.sensitive.data.buffer) >= hmac_sensitive.sensitive.data.size); (void) tpm2_credit_random(c.esys_context); @@ -830,7 +938,7 @@ int tpm2_seal( rc = sym_Esys_Create( c.esys_context, primary, - ESYS_TR_PASSWORD, + session, /* use HMAC session to enable parameter encryption */ ESYS_TR_NONE, ESYS_TR_NONE, &hmac_sensitive, @@ -910,7 +1018,9 @@ int tpm2_seal( r = 0; finish: + explicit_bzero_safe(&hmac_sensitive, sizeof(hmac_sensitive)); primary = flush_context_verbose(c.esys_context, primary); + session = flush_context_verbose(c.esys_context, session); return r; } @@ -923,11 +1033,13 @@ int tpm2_unseal( size_t blob_size, const void *known_policy_hash, size_t known_policy_hash_size, + const char *pin, void **ret_secret, size_t *ret_secret_size) { _cleanup_(tpm2_context_destroy) struct tpm2_context c = {}; - ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE, hmac_key = ESYS_TR_NONE; + ESYS_TR primary = ESYS_TR_NONE, session = ESYS_TR_NONE, hmac_session = ESYS_TR_NONE, + hmac_key = ESYS_TR_NONE; _cleanup_(Esys_Freep) TPM2B_SENSITIVE_DATA* unsealed = NULL; _cleanup_(Esys_Freep) TPM2B_DIGEST *policy_digest = NULL; _cleanup_(erase_and_freep) char *secret = NULL; @@ -978,7 +1090,16 @@ int tpm2_unseal( if (r < 0) return r; - r = tpm2_make_pcr_session(c.esys_context, pcr_mask, pcr_bank, &session, &policy_digest, NULL); + r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL); + if (r < 0) + return r; + + r = tpm2_make_encryption_session(c.esys_context, primary, &hmac_session); + if (r < 0) + goto finish; + + r = tpm2_make_pcr_session(c.esys_context, primary, hmac_session, pcr_mask, pcr_bank, !!pin, &session, + &policy_digest, NULL); if (r < 0) goto finish; @@ -989,34 +1110,57 @@ int tpm2_unseal( return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Current policy digest does not match stored policy digest, cancelling TPM2 authentication attempt."); - r = tpm2_make_primary(c.esys_context, &primary, primary_alg, NULL); - if (r < 0) - return r; - log_debug("Loading HMAC key into TPM."); rc = sym_Esys_Load( c.esys_context, primary, - ESYS_TR_PASSWORD, + hmac_session, /* use HMAC session to enable parameter encryption */ ESYS_TR_NONE, ESYS_TR_NONE, &private, &public, &hmac_key); if (rc != TSS2_RC_SUCCESS) { - r = log_error_errno(SYNTHETIC_ERRNO(ENOTRECOVERABLE), - "Failed to load HMAC key in TPM: %s", sym_Tss2_RC_Decode(rc)); + /* If we're in dictionary attack lockout mode, we should see a lockout error here, which we + * need to translate for the caller. */ + if (rc == TPM2_RC_LOCKOUT) + r = log_error_errno( + SYNTHETIC_ERRNO(ENOLCK), + "TPM2 device is in dictionary attack lockout mode."); + else + r = log_error_errno( + SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to load HMAC key in TPM: %s", + sym_Tss2_RC_Decode(rc)); goto finish; } + if (pin) { + TPM2B_AUTH auth = { + .size = SHA256_DIGEST_SIZE + }; + + hash_pin(pin, strlen(pin), auth.buffer); + + rc = sym_Esys_TR_SetAuth(c.esys_context, hmac_key, &auth); + explicit_bzero_safe(&auth, sizeof(auth)); + if (rc != TSS2_RC_SUCCESS) { + r = log_error_errno( + SYNTHETIC_ERRNO(ENOTRECOVERABLE), + "Failed to load PIN in TPM: %s", + sym_Tss2_RC_Decode(rc)); + goto finish; + } + } + log_debug("Unsealing HMAC key."); rc = sym_Esys_Unseal( c.esys_context, hmac_key, session, - ESYS_TR_NONE, + hmac_session, /* use HMAC session to enable parameter encryption */ ESYS_TR_NONE, &unsealed); if (rc != TSS2_RC_SUCCESS) { @@ -1223,6 +1367,7 @@ int tpm2_make_luks2_json( size_t blob_size, const void *policy_hash, size_t policy_hash_size, + TPM2Flags flags, JsonVariant **ret) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL, *a = NULL; @@ -1263,7 +1408,9 @@ int tpm2_make_luks2_json( JSON_BUILD_PAIR("tpm2-pcrs", JSON_BUILD_VARIANT(a)), JSON_BUILD_PAIR_CONDITION(!!tpm2_pcr_bank_to_string(pcr_bank), "tpm2-pcr-bank", JSON_BUILD_STRING(tpm2_pcr_bank_to_string(pcr_bank))), JSON_BUILD_PAIR_CONDITION(!!tpm2_primary_alg_to_string(primary_alg), "tpm2-primary-alg", JSON_BUILD_STRING(tpm2_primary_alg_to_string(primary_alg))), - JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size)))); + JSON_BUILD_PAIR("tpm2-policy-hash", JSON_BUILD_HEX(policy_hash, policy_hash_size)), + JSON_BUILD_PAIR("tpm2-pin", JSON_BUILD_BOOLEAN(flags & TPM2_FLAGS_USE_PIN))) + ); if (r < 0) return r; diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h index cb57a847e..f9dedd670 100644 --- a/src/shared/tpm2-util.h +++ b/src/shared/tpm2-util.h @@ -1,9 +1,15 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once +#include + #include "json.h" #include "macro.h" +typedef enum TPM2Flags { + TPM2_FLAGS_USE_PIN = 1 << 0, +} TPM2Flags; + #if HAVE_TPM2 #include @@ -20,10 +26,13 @@ extern TSS2_RC (*sym_Esys_GetRandom)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1 extern TSS2_RC (*sym_Esys_Initialize)(ESYS_CONTEXT **esys_context, TSS2_TCTI_CONTEXT *tcti, TSS2_ABI_VERSION *abiVersion); extern TSS2_RC (*sym_Esys_Load)(ESYS_CONTEXT *esysContext, ESYS_TR parentHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_PRIVATE *inPrivate, const TPM2B_PUBLIC *inPublic, ESYS_TR *objectHandle); extern TSS2_RC (*sym_Esys_PCR_Read)(ESYS_CONTEXT *esysContext, ESYS_TR shandle1,ESYS_TR shandle2, ESYS_TR shandle3, const TPML_PCR_SELECTION *pcrSelectionIn, UINT32 *pcrUpdateCounter, TPML_PCR_SELECTION **pcrSelectionOut, TPML_DIGEST **pcrValues); +extern TSS2_RC (*sym_Esys_PolicyAuthValue)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3); extern TSS2_RC (*sym_Esys_PolicyGetDigest)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_DIGEST **policyDigest); extern TSS2_RC (*sym_Esys_PolicyPCR)(ESYS_CONTEXT *esysContext, ESYS_TR policySession, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_DIGEST *pcrDigest, const TPML_PCR_SELECTION *pcrs); extern TSS2_RC (*sym_Esys_StartAuthSession)(ESYS_CONTEXT *esysContext, ESYS_TR tpmKey, ESYS_TR bind, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, const TPM2B_NONCE *nonceCaller, TPM2_SE sessionType, const TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash, ESYS_TR *sessionHandle); extern TSS2_RC (*sym_Esys_Startup)(ESYS_CONTEXT *esysContext, TPM2_SU startupType); +extern TSS2_RC (*sym_Esys_TRSess_SetAttributes)(ESYS_CONTEXT *esysContext, ESYS_TR session, TPMA_SESSION flags, TPMA_SESSION mask); +extern TSS2_RC (*sym_Esys_TR_SetAuth)(ESYS_CONTEXT *esysContext, ESYS_TR handle, TPM2B_AUTH const *authValue); extern TSS2_RC (*sym_Esys_Unseal)(ESYS_CONTEXT *esysContext, ESYS_TR itemHandle, ESYS_TR shandle1, ESYS_TR shandle2, ESYS_TR shandle3, TPM2B_SENSITIVE_DATA **outData); extern const char* (*sym_Tss2_RC_Decode)(TSS2_RC rc); @@ -35,8 +44,8 @@ extern TSS2_RC (*sym_Tss2_MU_TPM2B_PUBLIC_Unmarshal)(uint8_t const buffer[], siz int dlopen_tpm2(void); -int tpm2_seal(const char *device, uint32_t pcr_mask, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg); -int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, void **ret_secret, size_t *ret_secret_size); +int tpm2_seal(const char *device, uint32_t pcr_mask, const char *pin, void **ret_secret, size_t *ret_secret_size, void **ret_blob, size_t *ret_blob_size, void **ret_pcr_hash, size_t *ret_pcr_hash_size, uint16_t *ret_pcr_bank, uint16_t *ret_primary_alg); +int tpm2_unseal(const char *device, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *pcr_hash, size_t pcr_hash_size, const char *pin, void **ret_secret, size_t *ret_secret_size); #endif @@ -45,7 +54,7 @@ int tpm2_find_device_auto(int log_level, char **ret); int tpm2_parse_pcrs(const char *s, uint32_t *ret); -int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, JsonVariant **ret); +int tpm2_make_luks2_json(int keyslot, uint32_t pcr_mask, uint16_t pcr_bank, uint16_t primary_alg, const void *blob, size_t blob_size, const void *policy_hash, size_t policy_hash_size, TPM2Flags flags, JsonVariant **ret); #define TPM2_PCRS_MAX 24 diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index 5c1b4a447..f91ee3e1b 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -13,6 +13,7 @@ #include "errno-util.h" #include "escape.h" #include "fd-util.h" +#include "id128-util.h" #include "log.h" #include "macro.h" #include "parse-util.h" @@ -338,6 +339,7 @@ bool device_for_action(sd_device *dev, sd_device_action_t a) { void log_device_uevent(sd_device *device, const char *str) { sd_device_action_t action = _SD_DEVICE_ACTION_INVALID; + sd_id128_t event_id = SD_ID128_NULL; uint64_t seqnum = 0; if (!DEBUG_LOGGING) @@ -345,9 +347,12 @@ void log_device_uevent(sd_device *device, const char *str) { (void) sd_device_get_seqnum(device, &seqnum); (void) sd_device_get_action(device, &action); - log_device_debug(device, "%s%s(SEQNUM=%"PRIu64", ACTION=%s)", + (void) sd_device_get_trigger_uuid(device, &event_id); + log_device_debug(device, "%s%s(SEQNUM=%"PRIu64", ACTION=%s%s%s)", strempty(str), isempty(str) ? "" : " ", - seqnum, strna(device_action_to_string(action))); + seqnum, strna(device_action_to_string(action)), + sd_id128_is_null(event_id) ? "" : ", UUID=", + sd_id128_is_null(event_id) ? "" : SD_ID128_TO_UUID_STRING(event_id)); } int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos) { diff --git a/src/shared/user-record-show.c b/src/shared/user-record-show.c index 7c2751f3a..95895a8e4 100644 --- a/src/shared/user-record-show.c +++ b/src/shared/user-record-show.c @@ -143,7 +143,6 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) { break; } bool has_valid_passwords = false; - char **p; STRV_FOREACH(p, hr->hashed_password) if (!hashed_password_is_locked_or_invalid(*p)) { has_valid_passwords = true; @@ -240,15 +239,12 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) { if (hr->preferred_language) printf(" Language: %s\n", hr->preferred_language); - if (!strv_isempty(hr->environment)) { - char **i; - + if (!strv_isempty(hr->environment)) STRV_FOREACH(i, hr->environment) { printf(i == hr->environment ? " Environment: %s\n" : " %s\n", *i); } - } if (hr->locked >= 0) printf(" Locked: %s\n", yes_no(hr->locked)); @@ -478,14 +474,11 @@ void user_record_show(UserRecord *hr, bool show_full_group_info) { if (!strv_isempty(hr->ssh_authorized_keys)) printf("SSH Pub. Key: %zu\n", strv_length(hr->ssh_authorized_keys)); - if (!strv_isempty(hr->pkcs11_token_uri)) { - char **i; - + if (!strv_isempty(hr->pkcs11_token_uri)) STRV_FOREACH(i, hr->pkcs11_token_uri) printf(i == hr->pkcs11_token_uri ? "PKCS11 Token: %s\n" : " %s\n", *i); - } if (hr->n_fido2_hmac_credential > 0) printf(" FIDO2 Token: %zu\n", hr->n_fido2_hmac_credential); @@ -558,7 +551,6 @@ void group_record_show(GroupRecord *gr, bool show_full_user_info) { } } else { const char *prefix = " Members:"; - char **i; STRV_FOREACH(i, gr->members) { printf("%s %s\n", prefix, *i); @@ -568,7 +560,6 @@ void group_record_show(GroupRecord *gr, bool show_full_user_info) { if (!strv_isempty(gr->administrators)) { const char *prefix = " Admins:"; - char **i; STRV_FOREACH(i, gr->administrators) { printf("%s %s\n", prefix, *i); diff --git a/src/shared/user-record.c b/src/shared/user-record.c index 5b406d1f4..7c1c2cd99 100644 --- a/src/shared/user-record.c +++ b/src/shared/user-record.c @@ -1747,7 +1747,7 @@ const char *user_record_shell(UserRecord *h) { return "/bin/sh"; if (user_record_disposition(h) == USER_REGULAR) - return "/bin/bash"; + return DEFAULT_USER_SHELL; return NOLOGIN; } diff --git a/src/shared/varlink.c b/src/shared/varlink.c index e0038dfd2..bae2031d0 100644 --- a/src/shared/varlink.c +++ b/src/shared/varlink.c @@ -1660,6 +1660,7 @@ int varlink_errorb(Varlink *v, const char *error_id, ...) { } int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters) { + int r; assert_return(v, -EINVAL); assert_return(parameters, -EINVAL); @@ -1669,13 +1670,33 @@ int varlink_error_invalid_parameter(Varlink *v, JsonVariant *parameters) { * variant in which case we'll pull out the first key. The latter mode is useful in functions that * don't expect any arguments. */ - if (json_variant_is_string(parameters)) - return varlink_error(v, VARLINK_ERROR_INVALID_PARAMETER, parameters); + /* varlink_error(...) expects a json object as the third parameter. Passing a string variant causes + * parameter sanitization to fail, and it returns -EINVAL. */ + + if (json_variant_is_string(parameters)) { + _cleanup_(json_variant_unrefp) JsonVariant *parameters_obj = NULL; + + r = json_build(¶meters_obj, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("parameter", JSON_BUILD_VARIANT(parameters)))); + if (r < 0) + return r; + + return varlink_error(v, VARLINK_ERROR_INVALID_PARAMETER, parameters_obj); + } if (json_variant_is_object(parameters) && - json_variant_elements(parameters) > 0) - return varlink_error(v, VARLINK_ERROR_INVALID_PARAMETER, - json_variant_by_index(parameters, 0)); + json_variant_elements(parameters) > 0) { + _cleanup_(json_variant_unrefp) JsonVariant *parameters_obj = NULL; + + r = json_build(¶meters_obj, + JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("parameter", JSON_BUILD_VARIANT(json_variant_by_index(parameters, 0))))); + if (r < 0) + return r; + + return varlink_error(v, VARLINK_ERROR_INVALID_PARAMETER, parameters_obj); + } return -EINVAL; } @@ -2326,7 +2347,6 @@ int varlink_server_shutdown(VarlinkServer *s) { } int varlink_server_attach_event(VarlinkServer *s, sd_event *e, int64_t priority) { - VarlinkServerSocket *ss; int r; assert_return(s, -EINVAL); @@ -2361,8 +2381,6 @@ fail: } int varlink_server_detach_event(VarlinkServer *s) { - VarlinkServerSocket *ss; - assert_return(s, -EINVAL); LIST_FOREACH(sockets, ss, s->sockets) diff --git a/src/shared/watchdog.c b/src/shared/watchdog.c index 3f4c72826..2476778c9 100644 --- a/src/shared/watchdog.c +++ b/src/shared/watchdog.c @@ -9,6 +9,7 @@ #include "errno-util.h" #include "fd-util.h" +#include "fileio.h" #include "log.h" #include "path-util.h" #include "string-util.h" @@ -18,7 +19,85 @@ static int watchdog_fd = -1; static char *watchdog_device; static usec_t watchdog_timeout; /* 0 → close device and USEC_INFINITY → don't change timeout */ +static usec_t watchdog_pretimeout; /* 0 → disable pretimeout and USEC_INFINITY → don't change pretimeout */ static usec_t watchdog_last_ping = USEC_INFINITY; +static bool watchdog_supports_pretimeout = false; /* Depends on kernel state that might change at runtime */ +static char *watchdog_pretimeout_governor = NULL; + +/* Starting from kernel version 4.5, the maximum allowable watchdog timeout is + * UINT_MAX/1000U seconds (since internal calculations are done in milliseconds + * using unsigned integers. However, the kernel's userspace API for the watchdog + * uses signed integers for its ioctl parameters (even for timeout values and + * bit flags) so this is why we must consider the maximum signed integer value + * as well. + */ +#define WATCHDOG_TIMEOUT_MAX_SEC (CONST_MIN(UINT_MAX/1000U, (unsigned)INT_MAX)) + +#define WATCHDOG_GOV_NAME_MAXLEN 20 /* From the kernel watchdog driver */ + +static int saturated_usec_to_sec(usec_t val) { + usec_t t = DIV_ROUND_UP(val, USEC_PER_SEC); + return MIN(t, (usec_t) WATCHDOG_TIMEOUT_MAX_SEC); /* Saturate to watchdog max */ +} + +static int get_watchdog_sysfs_path(const char *filename, char **ret_path) { + struct stat st; + + if (watchdog_fd < 0) + return -EBADF; + + if (fstat(watchdog_fd, &st)) + return -errno; + + if (!S_ISCHR(st.st_mode)) + return -EBADF; + + if (asprintf(ret_path, "/sys/dev/char/%d:%d/%s", major(st.st_rdev), minor(st.st_rdev), filename) < 0) + return -ENOMEM; + + return 0; +} + +static int get_pretimeout_governor(char **ret_gov) { + _cleanup_free_ char *sys_fn = NULL; + int r; + + r = get_watchdog_sysfs_path("pretimeout_governor", &sys_fn); + if (r < 0) + return r; + + log_info("Watchdog: reading from %s", sys_fn); + + r = read_virtual_file(sys_fn, WATCHDOG_GOV_NAME_MAXLEN - 1, ret_gov, NULL); + if (r < 0) + return r; + + delete_trailing_chars(*ret_gov, WHITESPACE); + + return 0; +} + +static int set_pretimeout_governor(const char *governor) { + _cleanup_free_ char *sys_fn = NULL; + int r; + + if (isempty(governor)) + return 0; /* Nothing to do */ + + r = get_watchdog_sysfs_path("pretimeout_governor", &sys_fn); + if (r < 0) + return r; + + log_info("Watchdog: setting pretimeout_governor to '%s' via '%s'", governor, sys_fn); + + r = write_string_file(sys_fn, + governor, + WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_VERIFY_IGNORE_NEWLINE); + if (r < 0) + return log_error_errno(r, "Failed to set pretimeout_governor to '%s': %m", governor); + + return r; +} static int watchdog_set_enable(bool enable) { int flags = enable ? WDIOS_ENABLECARD : WDIOS_DISABLECARD; @@ -54,35 +133,127 @@ static int watchdog_get_timeout(void) { } static int watchdog_set_timeout(void) { - usec_t t; int sec; assert(watchdog_fd >= 0); assert(timestamp_is_set(watchdog_timeout)); - t = DIV_ROUND_UP(watchdog_timeout, USEC_PER_SEC); - sec = MIN(t, (usec_t) INT_MAX); /* Saturate */ + sec = saturated_usec_to_sec(watchdog_timeout); if (ioctl(watchdog_fd, WDIOC_SETTIMEOUT, &sec) < 0) return -errno; - assert(sec > 0);/* buggy driver ? */ + assert(sec > 0); /* buggy driver ? */ watchdog_timeout = sec * USEC_PER_SEC; return 0; } +static int watchdog_get_pretimeout(void) { + int sec = 0; + + assert(watchdog_fd >= 0); + + if (ioctl(watchdog_fd, WDIOC_GETPRETIMEOUT, &sec) < 0) { + watchdog_pretimeout = 0; + return log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno, "Failed to get pretimeout value, ignoring: %m"); + } + + watchdog_pretimeout = sec * USEC_PER_SEC; + + return 0; +} + +static int watchdog_set_pretimeout(void) { + int sec; + + assert(watchdog_fd >= 0); + assert(watchdog_pretimeout != USEC_INFINITY); + + sec = saturated_usec_to_sec(watchdog_pretimeout); + + if (ioctl(watchdog_fd, WDIOC_SETPRETIMEOUT, &sec) < 0) { + watchdog_pretimeout = 0; + + if (ERRNO_IS_NOT_SUPPORTED(errno)) { + log_info("Watchdog does not support pretimeouts."); + return 0; + } + + return log_error_errno(errno, "Failed to set pretimeout to %s: %m", FORMAT_TIMESPAN(sec, USEC_PER_SEC)); + } + + /* The set ioctl does not return the actual value set so get it now. */ + (void) watchdog_get_pretimeout(); + + return 0; +} + static int watchdog_ping_now(void) { assert(watchdog_fd >= 0); if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) < 0) return log_warning_errno(errno, "Failed to ping hardware watchdog, ignoring: %m"); - watchdog_last_ping = now(clock_boottime_or_monotonic()); + watchdog_last_ping = now(CLOCK_BOOTTIME); return 0; } +static int update_pretimeout(void) { + _cleanup_free_ char *governor = NULL; + int r, t_sec, pt_sec; + + if (watchdog_fd < 0) + return 0; + + if (watchdog_timeout == USEC_INFINITY || watchdog_pretimeout == USEC_INFINITY) + return 0; + + if (!watchdog_supports_pretimeout && watchdog_pretimeout == 0) + return 0; /* Nothing to do */ + + /* The configuration changed, do not assume it can still work, as the module(s) + * might have been unloaded. */ + watchdog_supports_pretimeout = false; + + /* Update the pretimeout governor as well */ + (void) set_pretimeout_governor(watchdog_pretimeout_governor); + + r = get_pretimeout_governor(&governor); + if (r < 0) + return log_warning_errno(r, "Watchdog: failed to read pretimeout governor: %m"); + if (isempty(governor)) + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "Watchdog: no pretimeout governor detected - is the required kernel module loaded?"); + + /* If we have a pretimeout governor, then pretimeout is supported. Without a governor + * pretimeout does not work at all. + * Note that this might require a kernel module that is not autoloaded, so we don't + * cache this, but we check every time the configuration changes. */ + watchdog_supports_pretimeout = true; + + /* Determine if the pretimeout is valid for the current watchdog timeout. */ + t_sec = saturated_usec_to_sec(watchdog_timeout); + pt_sec = saturated_usec_to_sec(watchdog_pretimeout); + if (pt_sec >= t_sec) { + r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Cannot set watchdog pretimeout to %is (%s watchdog timeout of %is)", + pt_sec, pt_sec == t_sec ? "same as" : "longer than", t_sec); + (void) watchdog_get_pretimeout(); + } else + r = watchdog_set_pretimeout(); + + if (watchdog_pretimeout == 0) + log_info("Watchdog pretimeout is disabled."); + else + log_info("Watchdog running with a pretimeout of %s with governor '%s'.", + FORMAT_TIMESPAN(watchdog_pretimeout, 0), + governor); + + return r; +} + static int update_timeout(void) { int r; @@ -109,6 +280,12 @@ static int update_timeout(void) { return log_error_errno(r, "Failed to query watchdog HW timeout: %m"); } + /* If the watchdog timeout was changed, the pretimeout could have been + * changed as well by the driver or the kernel so we need to update the + * pretimeout now. Or if the watchdog is being configured for the first + * time, we want to configure the pretimeout before it is enabled. */ + (void) update_pretimeout(); + r = watchdog_set_enable(true); if (r < 0) return r; @@ -133,17 +310,21 @@ static int open_watchdog(void) { fn = !watchdog_device || path_equal(watchdog_device, "/dev/watchdog") ? "/dev/watchdog0" : watchdog_device; - watchdog_fd = open(fn, O_WRONLY|O_CLOEXEC); + r = free_and_strdup(&watchdog_device, fn); + if (r < 0) + return log_oom_debug(); + + watchdog_fd = open(watchdog_device, O_WRONLY|O_CLOEXEC); if (watchdog_fd < 0) - return log_debug_errno(errno, "Failed to open watchdog device %s, ignoring: %m", fn); + return log_debug_errno(errno, "Failed to open watchdog device %s, ignoring: %m", watchdog_device); if (ioctl(watchdog_fd, WDIOC_GETSUPPORT, &ident) < 0) - log_debug_errno(errno, "Hardware watchdog %s does not support WDIOC_GETSUPPORT ioctl, ignoring: %m", fn); + log_debug_errno(errno, "Hardware watchdog %s does not support WDIOC_GETSUPPORT ioctl, ignoring: %m", watchdog_device); else log_info("Using hardware watchdog '%s', version %x, device %s", ident.identity, ident.firmware_version, - fn); + watchdog_device); r = update_timeout(); if (r < 0) @@ -156,9 +337,6 @@ int watchdog_set_device(const char *path) { int r; r = free_and_strdup(&watchdog_device, path); - if (r < 0) - return r; - if (r > 0) /* watchdog_device changed */ watchdog_fd = safe_close(watchdog_fd); @@ -169,11 +347,9 @@ int watchdog_setup(usec_t timeout) { usec_t previous_timeout; int r; - /* timeout=0 closes the device whereas passing timeout=USEC_INFINITY - * opens it (if needed) without configuring any particular timeout and - * thus reuses the programmed value (therefore it's a nop if the device - * is already opened). - */ + /* timeout=0 closes the device whereas passing timeout=USEC_INFINITY opens it (if needed) + * without configuring any particular timeout and thus reuses the programmed value (therefore + * it's a nop if the device is already opened). */ if (timeout == 0) { watchdog_close(true); @@ -184,9 +360,8 @@ int watchdog_setup(usec_t timeout) { if (watchdog_fd >= 0 && (timeout == watchdog_timeout || timeout == USEC_INFINITY)) return 0; - /* Initialize the watchdog timeout with the caller value. This value is - * going to be updated by update_timeout() with the closest value - * supported by the driver */ + /* Initialize the watchdog timeout with the caller value. This value is going to be updated by + * update_timeout() with the closest value supported by the driver */ previous_timeout = watchdog_timeout; watchdog_timeout = timeout; @@ -200,24 +375,53 @@ int watchdog_setup(usec_t timeout) { return r; } -usec_t watchdog_runtime_wait(void) { +int watchdog_setup_pretimeout(usec_t timeout) { + /* timeout=0 disables the pretimeout whereas timeout=USEC_INFINITY is a nop. */ + if ((watchdog_fd >= 0 && timeout == watchdog_pretimeout) || timeout == USEC_INFINITY) + return 0; - if (!timestamp_is_set(watchdog_timeout)) + /* Initialize the watchdog timeout with the caller value. This value is + * going to be updated by update_pretimeout() with the running value, + * even if it fails to update the timeout. */ + watchdog_pretimeout = timeout; + + return update_pretimeout(); +} + +int watchdog_setup_pretimeout_governor(const char *governor) { + if (free_and_strdup(&watchdog_pretimeout_governor, governor) < 0) + return -ENOMEM; + + return set_pretimeout_governor(watchdog_pretimeout_governor); +} + +static usec_t calc_timeout(void) { + /* Calculate the effective timeout which accounts for the watchdog + * pretimeout if configured and supported. */ + if (watchdog_supports_pretimeout && timestamp_is_set(watchdog_pretimeout) && watchdog_timeout >= watchdog_pretimeout) + return watchdog_timeout - watchdog_pretimeout; + else + return watchdog_timeout; +} + +usec_t watchdog_runtime_wait(void) { + usec_t timeout = calc_timeout(); + if (!timestamp_is_set(timeout)) return USEC_INFINITY; /* Sleep half the watchdog timeout since the last successful ping at most */ if (timestamp_is_set(watchdog_last_ping)) { - usec_t ntime = now(clock_boottime_or_monotonic()); + usec_t ntime = now(CLOCK_BOOTTIME); assert(ntime >= watchdog_last_ping); - return usec_sub_unsigned(watchdog_last_ping + (watchdog_timeout / 2), ntime); + return usec_sub_unsigned(watchdog_last_ping + (timeout / 2), ntime); } - return watchdog_timeout / 2; + return timeout / 2; } int watchdog_ping(void) { - usec_t ntime; + usec_t ntime, timeout; if (watchdog_timeout == 0) return 0; @@ -226,13 +430,14 @@ int watchdog_ping(void) { /* open_watchdog() will automatically ping the device for us if necessary */ return open_watchdog(); - ntime = now(clock_boottime_or_monotonic()); + ntime = now(CLOCK_BOOTTIME); + timeout = calc_timeout(); /* Never ping earlier than watchdog_timeout/4 and try to ping - * by watchdog_timeout/2 plus scheduling latencies the latest */ + * by watchdog_timeout/2 plus scheduling latencies at the latest */ if (timestamp_is_set(watchdog_last_ping)) { assert(ntime >= watchdog_last_ping); - if ((ntime - watchdog_last_ping) < (watchdog_timeout / 4)) + if ((ntime - watchdog_last_ping) < (timeout / 4)) return 0; } diff --git a/src/shared/watchdog.h b/src/shared/watchdog.h index 6e99bbdf5..a537f8ae7 100644 --- a/src/shared/watchdog.h +++ b/src/shared/watchdog.h @@ -8,6 +8,8 @@ int watchdog_set_device(const char *path); int watchdog_setup(usec_t timeout); +int watchdog_setup_pretimeout(usec_t usec); +int watchdog_setup_pretimeout_governor(const char *governor); int watchdog_ping(void); void watchdog_close(bool disarm); usec_t watchdog_runtime_wait(void); diff --git a/src/shared/wifi-util.c b/src/shared/wifi-util.c index cf0f56255..d32bb5d70 100644 --- a/src/shared/wifi-util.c +++ b/src/shared/wifi-util.c @@ -153,7 +153,7 @@ static const char * const nl80211_iftype_table[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_NAN] = "nan", }; -DEFINE_STRING_TABLE_LOOKUP_TO_STRING(nl80211_iftype, enum nl80211_iftype); +DEFINE_STRING_TABLE_LOOKUP(nl80211_iftype, enum nl80211_iftype); static const char * const nl80211_cmd_table[__NL80211_CMD_AFTER_LAST] = { [NL80211_CMD_GET_WIPHY] = "get_wiphy", diff --git a/src/shared/wifi-util.h b/src/shared/wifi-util.h index 8afd1a4ec..a762fbcd4 100644 --- a/src/shared/wifi-util.h +++ b/src/shared/wifi-util.h @@ -12,4 +12,5 @@ int wifi_get_interface(sd_netlink *genl, int ifindex, enum nl80211_iftype *ret_i int wifi_get_station(sd_netlink *genl, int ifindex, struct ether_addr *ret_bssid); const char *nl80211_iftype_to_string(enum nl80211_iftype iftype) _const_; +enum nl80211_iftype nl80211_iftype_from_string(const char *s) _pure_; const char *nl80211_cmd_to_string(int cmd) _const_; diff --git a/src/shutdown/meson.build b/src/shutdown/meson.build index 186e9240f..0bd09fd7b 100644 --- a/src/shutdown/meson.build +++ b/src/shutdown/meson.build @@ -7,9 +7,9 @@ systemd_shutdown_sources = files( ) tests += [ - [['src/shutdown/test-umount.c', - 'src/shutdown/umount.c', - 'src/shutdown/umount.h'], + [files('test-umount.c', + 'umount.c', + 'umount.h'), [], [libmount]], ] diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index 7ad993067..2c3cbec02 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -531,7 +531,9 @@ int main(int argc, char *argv[]) { need_md_detach ? " MD devices," : ""); } - /* We're done with the watchdog. */ + /* We're done with the watchdog. Note that the watchdog is explicitly not + * stopped here. It remains active to guard against any issues during the + * rest of the shutdown sequence. */ watchdog_free_device(); arguments[0] = NULL; diff --git a/src/shutdown/test-umount.c b/src/shutdown/test-umount.c index 676c6dd86..c92105b62 100644 --- a/src/shutdown/test-umount.c +++ b/src/shutdown/test-umount.c @@ -9,10 +9,9 @@ #include "umount.h" #include "util.h" -static void test_mount_points_list(const char *fname) { +static void test_mount_points_list_one(const char *fname) { _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head); _cleanup_free_ char *testdata_fname = NULL; - MountPoint *m; log_info("/* %s(\"%s\") */", __func__, fname ?: "/proc/self/mountinfo"); @@ -33,10 +32,16 @@ static void test_mount_points_list(const char *fname) { major(m->devnum), minor(m->devnum)); } -static void test_swap_list(const char *fname) { +TEST(mount_points_list) { + test_mount_points_list_one(NULL); + test_mount_points_list_one("/test-umount/empty.mountinfo"); + test_mount_points_list_one("/test-umount/garbled.mountinfo"); + test_mount_points_list_one("/test-umount/rhbug-1554943.mountinfo"); +} + +static void test_swap_list_one(const char *fname) { _cleanup_(mount_points_list_free) LIST_HEAD(MountPoint, mp_list_head); _cleanup_free_ char *testdata_fname = NULL; - MountPoint *m; int r; log_info("/* %s(\"%s\") */", __func__, fname ?: "/proc/swaps"); @@ -61,14 +66,9 @@ static void test_swap_list(const char *fname) { major(m->devnum), minor(m->devnum)); } -int main(int argc, char **argv) { - test_setup_logging(LOG_DEBUG); - - test_mount_points_list(NULL); - test_mount_points_list("/test-umount/empty.mountinfo"); - test_mount_points_list("/test-umount/garbled.mountinfo"); - test_mount_points_list("/test-umount/rhbug-1554943.mountinfo"); - - test_swap_list(NULL); - test_swap_list("/test-umount/example.swaps"); +TEST(swap_list) { + test_swap_list_one(NULL); + test_swap_list_one("/test-umount/example.swaps"); } + +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/shutdown/umount.c b/src/shutdown/umount.c index f5a2cb20c..3e9e24149 100644 --- a/src/shutdown/umount.c +++ b/src/shutdown/umount.c @@ -522,7 +522,7 @@ static int remount_with_timeout(MountPoint *m, int umount_log_level) { if (r < 0) return r; if (r == 0) { - log_info("Remounting '%s' read-only with options '%s'.", m->path, strna(m->remount_options)); + log_info("Remounting '%s' read-only with options '%s'.", m->path, strempty(m->remount_options)); /* Start the mount operation here in the child */ r = mount(NULL, m->path, NULL, m->remount_flags, m->remount_options); @@ -589,7 +589,6 @@ static int umount_with_timeout(MountPoint *m, int umount_log_level) { /* This includes remounting readonly, which changes the kernel mount options. Therefore the list passed to * this function is invalidated, and should not be reused. */ static int mount_points_list_umount(MountPoint **head, bool *changed, int umount_log_level) { - MountPoint *m; int n_failed = 0; assert(head); @@ -635,13 +634,12 @@ static int mount_points_list_umount(MountPoint **head, bool *changed, int umount } static int swap_points_list_off(MountPoint **head, bool *changed) { - MountPoint *m, *n; int n_failed = 0; assert(head); assert(changed); - LIST_FOREACH_SAFE(mount_point, m, n, *head) { + LIST_FOREACH(mount_point, m, *head) { log_info("Deactivating swap %s.", m->path); if (swapoff(m->path) < 0) { log_warning_errno(errno, "Could not deactivate swap %s: %m", m->path); @@ -657,7 +655,6 @@ static int swap_points_list_off(MountPoint **head, bool *changed) { } static int loopback_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) { - MountPoint *m, *n; int n_failed = 0, r; dev_t rootdev = 0; @@ -666,7 +663,7 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed, int umo (void) get_block_device("/", &rootdev); - LIST_FOREACH_SAFE(mount_point, m, n, *head) { + LIST_FOREACH(mount_point, m, *head) { if (major(rootdev) != 0 && rootdev == m->devnum) { n_failed++; continue; @@ -689,7 +686,6 @@ static int loopback_points_list_detach(MountPoint **head, bool *changed, int umo } static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) { - MountPoint *m, *n; int n_failed = 0, r; dev_t rootdev = 0; @@ -698,7 +694,7 @@ static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_lo (void) get_block_device("/", &rootdev); - LIST_FOREACH_SAFE(mount_point, m, n, *head) { + LIST_FOREACH(mount_point, m, *head) { if (major(rootdev) != 0 && rootdev == m->devnum) { n_failed ++; continue; @@ -720,7 +716,6 @@ static int dm_points_list_detach(MountPoint **head, bool *changed, int umount_lo } static int md_points_list_detach(MountPoint **head, bool *changed, int umount_log_level) { - MountPoint *m, *n; int n_failed = 0, r; dev_t rootdev = 0; @@ -729,7 +724,7 @@ static int md_points_list_detach(MountPoint **head, bool *changed, int umount_lo (void) get_block_device("/", &rootdev); - LIST_FOREACH_SAFE(mount_point, m, n, *head) { + LIST_FOREACH(mount_point, m, *head) { if (major(rootdev) != 0 && rootdev == m->devnum) { n_failed ++; continue; diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 7064f3a90..f108529bb 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -85,7 +85,6 @@ static int write_hibernate_location_info(const HibernateLocation *hibernate_loca static int write_mode(char **modes) { int r = 0; - char **mode; STRV_FOREACH(mode, modes) { int k; @@ -103,7 +102,6 @@ static int write_mode(char **modes) { } static int write_state(FILE **f, char **states) { - char **state; int r = 0; assert(f); diff --git a/src/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c index 1a2099a8d..c851059a0 100644 --- a/src/stdio-bridge/stdio-bridge.c +++ b/src/stdio-bridge/stdio-bridge.c @@ -26,9 +26,8 @@ static BusTransport arg_transport = BUS_TRANSPORT_LOCAL; static bool arg_user = false; static int help(void) { - printf("%s [OPTIONS...]\n\n" - "Forward messages between two D-Bus busses via a pipe or socket.\n\n" + "Forward messages between a pipe or socket and a D-Bus bus.\n\n" " -h --help Show this help\n" " --version Show package version\n" " -p --bus-path=PATH Path to the bus address (default: %s)\n" @@ -41,7 +40,6 @@ static int help(void) { } static int parse_argv(int argc, char *argv[]) { - enum { ARG_VERSION = 0x100, ARG_MACHINE, @@ -64,7 +62,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hp:M:", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hp:M:", options, NULL)) >= 0) switch (c) { @@ -98,7 +96,6 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown option code %c", c); } - } return 1; } diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index 408ac3b8b..24c8baab0 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -51,8 +51,6 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Option*, option_free); DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(option_hash_ops, char, string_hash_func, string_compare_func, Option, option_free); static bool test_prefix(const char *p) { - char **i; - if (strv_isempty(arg_prefixes)) return true; @@ -131,7 +129,6 @@ static int apply_all(OrderedHashmap *sysctl_options) { if (string_is_glob(option->key)) { _cleanup_strv_free_ char **paths = NULL; _cleanup_free_ char *pattern = NULL; - char **s; pattern = path_join("/proc/sys", option->key); if (!pattern) @@ -403,7 +400,6 @@ static int run(int argc, char *argv[]) { } } else { _cleanup_strv_free_ char **files = NULL; - char **f; r = conf_files_list_strv(&files, ".conf", NULL, 0, (const char**) CONF_PATHS_STRV("sysctl.d")); if (r < 0) diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 60789e0f2..6d4df0afd 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -124,7 +124,6 @@ static int unmerge_hierarchy(const char *p) { static int unmerge(void) { int r, ret = 0; - char **p; STRV_FOREACH(p, arg_hierarchies) { _cleanup_free_ char *resolved = NULL; @@ -161,7 +160,6 @@ static int verb_unmerge(int argc, char **argv, void *userdata) { static int verb_status(int argc, char **argv, void *userdata) { _cleanup_(table_unrefp) Table *t = NULL; int r, ret = 0; - char **p; t = table_new("hierarchy", "extensions", "since"); if (!t) @@ -245,7 +243,6 @@ static int mount_overlayfs( _cleanup_free_ char *options = NULL; bool separator = false; - char **l; int r; assert(where); @@ -285,7 +282,6 @@ static int merge_hierarchy( _cleanup_free_ char *resolved_hierarchy = NULL, *f = NULL, *buf = NULL; _cleanup_strv_free_ char **layers = NULL; struct stat st; - char **p; int r; assert(hierarchy); @@ -453,7 +449,6 @@ static int merge_subprocess(Hashmap *images, const char *workspace) { size_t n_extensions = 0; unsigned n_ignored = 0; Image *img; - char **h; int r; /* Mark the whole of /run as MS_SLAVE, so that we can mount stuff below it that doesn't show up on @@ -764,7 +759,6 @@ static int image_discover_and_read_metadata(Hashmap **ret_images) { static int verb_merge(int argc, char **argv, void *userdata) { _cleanup_(hashmap_freep) Hashmap *images = NULL; - char **p; int r; if (!have_effective_cap(CAP_SYS_ADMIN)) diff --git a/src/systemctl/meson.build b/src/systemctl/meson.build index 38bf33d49..f0d405bb5 100644 --- a/src/systemctl/meson.build +++ b/src/systemctl/meson.build @@ -83,7 +83,8 @@ else endif fuzzers += [ - [['src/systemctl/fuzz-systemctl-parse-argv.c', - systemctl_sources], + [files('fuzz-systemctl-parse-argv.c') + + systemctl_sources, systemctl_link_with, - [], [], ['-DFUZZ_SYSTEMCTL_PARSE_ARGV']]] + [], [], ['-DFUZZ_SYSTEMCTL_PARSE_ARGV']] +] diff --git a/src/systemctl/systemctl-add-dependency.c b/src/systemctl/systemctl-add-dependency.c index ba385ea2a..120798b33 100644 --- a/src/systemctl/systemctl-add-dependency.c +++ b/src/systemctl/systemctl-add-dependency.c @@ -7,7 +7,7 @@ #include "systemctl-util.h" #include "systemctl.h" -int add_dependency(int argc, char *argv[], void *userdata) { +int verb_add_dependency(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; _cleanup_free_ char *target = NULL; const char *verb = argv[0]; @@ -78,7 +78,9 @@ int add_dependency(int argc, char *argv[], void *userdata) { goto finish; } - r = daemon_reload(argc, argv, userdata); + r = daemon_reload(ACTION_RELOAD, /* graceful= */ false); + if (r > 0) + r = 0; } finish: diff --git a/src/systemctl/systemctl-add-dependency.h b/src/systemctl/systemctl-add-dependency.h index deb0da4f3..11e5c82cc 100644 --- a/src/systemctl/systemctl-add-dependency.h +++ b/src/systemctl/systemctl-add-dependency.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int add_dependency(int argc, char *argv[], void *userdata); +int verb_add_dependency(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-cancel-job.c b/src/systemctl/systemctl-cancel-job.c index 4c5203c1f..3cf136946 100644 --- a/src/systemctl/systemctl-cancel-job.c +++ b/src/systemctl/systemctl-cancel-job.c @@ -8,13 +8,12 @@ #include "systemctl-util.h" #include "systemctl.h" -int cancel_job(int argc, char *argv[], void *userdata) { +int verb_cancel(int argc, char *argv[], void *userdata) { sd_bus *bus; - char **name; int r; if (argc <= 1) /* Shortcut to trivial_method() if no argument is given */ - return trivial_method(argc, argv, userdata); + return verb_trivial_method(argc, argv, userdata); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) diff --git a/src/systemctl/systemctl-cancel-job.h b/src/systemctl/systemctl-cancel-job.h index 75151d67f..397e5155f 100644 --- a/src/systemctl/systemctl-cancel-job.h +++ b/src/systemctl/systemctl-cancel-job.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int cancel_job(int argc, char *argv[], void *userdata); +int verb_cancel(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-clean-or-freeze.c b/src/systemctl/systemctl-clean-or-freeze.c index fb4d64351..5c15a9fba 100644 --- a/src/systemctl/systemctl-clean-or-freeze.c +++ b/src/systemctl/systemctl-clean-or-freeze.c @@ -7,11 +7,10 @@ #include "systemctl-util.h" #include "systemctl.h" -int clean_or_freeze_unit(int argc, char *argv[], void *userdata) { +int verb_clean_or_freeze(int argc, char *argv[], void *userdata) { _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL; _cleanup_strv_free_ char **names = NULL; int r, ret = EXIT_SUCCESS; - char **name; const char *method; sd_bus *bus; diff --git a/src/systemctl/systemctl-clean-or-freeze.h b/src/systemctl/systemctl-clean-or-freeze.h index 8e73f4efb..5f2bca4a4 100644 --- a/src/systemctl/systemctl-clean-or-freeze.h +++ b/src/systemctl/systemctl-clean-or-freeze.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int clean_or_freeze_unit(int argc, char *argv[], void *userdata); +int verb_clean_or_freeze(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-compat-halt.c b/src/systemctl/systemctl-compat-halt.c index 760758322..8a0e4e629 100644 --- a/src/systemctl/systemctl-compat-halt.c +++ b/src/systemctl/systemctl-compat-halt.c @@ -12,6 +12,7 @@ #include "systemctl-compat-halt.h" #include "systemctl-compat-telinit.h" #include "systemctl-logind.h" +#include "systemctl-start-unit.h" #include "systemctl-util.h" #include "systemctl.h" #include "terminal-util.h" @@ -75,6 +76,7 @@ int halt_parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); + /* called in sysvinit system as last command in shutdown/reboot so this is always forceful */ if (utmp_get_runlevel(&runlevel, NULL) >= 0) if (IN_SET(runlevel, '0', '6')) arg_force = 2; @@ -144,44 +146,37 @@ int halt_parse_argv(int argc, char *argv[]) { int halt_main(void) { int r; - r = logind_check_inhibitors(arg_action); - if (r < 0) - return r; + if (arg_force == 0) { + /* always try logind first */ + if (arg_when > 0) + r = logind_schedule_shutdown(); + else { + r = logind_check_inhibitors(arg_action); + if (r < 0) + return r; - /* Delayed shutdown requested, and was successful */ - if (arg_when > 0 && logind_schedule_shutdown() == 0) - return 0; - - /* No delay, or logind failed or is not at all available */ - if (geteuid() != 0) { - if (arg_dry_run || arg_force > 0) { - (void) must_be_root(); - return -EPERM; - } - - /* Try logind if we are a normal user and no special mode applies. Maybe polkit allows us to - * shutdown the machine. */ - if (IN_SET(arg_action, ACTION_POWEROFF, ACTION_REBOOT, ACTION_KEXEC, ACTION_HALT)) { r = logind_reboot(arg_action); - if (r >= 0) - return r; - if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) - /* Requested operation is not supported on the local system or already in - * progress */ - return r; - - /* on all other errors, try low-level operation */ } + if (r >= 0) + return r; + if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS)) + /* Requested operation requires auth, is not supported on the local system or already in + * progress */ + return r; + /* on all other errors, try low-level operation */ + + /* In order to minimize the difference between operation with and without logind, we explicitly + * enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */ + arg_no_block = true; + + if (!arg_dry_run) + return start_with_fallback(); } - /* In order to minimize the difference between operation with and without logind, we explicitly - * enable non-blocking mode for this, as logind's shutdown operations are always non-blocking. */ - arg_no_block = true; - - if (!arg_dry_run && !arg_force) - return start_with_fallback(); - - assert(geteuid() == 0); + if (geteuid() != 0) { + (void) must_be_root(); + return -EPERM; + } if (!arg_no_wtmp) { if (sd_booted() > 0) @@ -197,5 +192,5 @@ int halt_main(void) { return 0; r = halt_now(arg_action); - return log_error_errno(r, "Failed to reboot: %m"); + return log_error_errno(r, "Failed to %s: %m", action_table[arg_action].verb); } diff --git a/src/systemctl/systemctl-compat-telinit.c b/src/systemctl/systemctl-compat-telinit.c index c81e9bc3c..20325e5e1 100644 --- a/src/systemctl/systemctl-compat-telinit.c +++ b/src/systemctl/systemctl-compat-telinit.c @@ -125,8 +125,11 @@ int telinit_parse_argv(int argc, char *argv[]) { } int start_with_fallback(void) { + int r; + /* First, try systemd via D-Bus. */ - if (start_unit(0, NULL, NULL) == 0) + r = verb_start(0, NULL, NULL); + if (r == 0) return 0; #if HAVE_SYSV_COMPAT @@ -135,18 +138,18 @@ int start_with_fallback(void) { return 0; #endif - return log_error_errno(SYNTHETIC_ERRNO(EIO), - "Failed to talk to init daemon."); + return log_error_errno(r, "Failed to talk to init daemon: %m"); } int reload_with_fallback(void) { - /* First, try systemd via D-Bus. */ - if (daemon_reload(0, NULL, NULL) >= 0) - return 0; - /* Nothing else worked, so let's try signals */ assert(IN_SET(arg_action, ACTION_RELOAD, ACTION_REEXEC)); + /* First, try systemd via D-Bus */ + if (daemon_reload(arg_action, /* graceful= */ true) > 0) + return 0; + + /* That didn't work, so let's try signals */ if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) return log_error_errno(errno, "kill() failed: %m"); @@ -155,7 +158,7 @@ int reload_with_fallback(void) { int exec_telinit(char *argv[]) { (void) rlimit_nofile_safe(); - execv(TELINIT, argv); + (void) execv(TELINIT, argv); return log_error_errno(SYNTHETIC_ERRNO(EIO), "Couldn't find an alternative telinit implementation to spawn."); diff --git a/src/systemctl/systemctl-daemon-reload.c b/src/systemctl/systemctl-daemon-reload.c index 1c2331533..33de7d161 100644 --- a/src/systemctl/systemctl-daemon-reload.c +++ b/src/systemctl/systemctl-daemon-reload.c @@ -6,7 +6,7 @@ #include "systemctl-util.h" #include "systemctl.h" -int daemon_reload(int argc, char *argv[], void *userdata) { +int daemon_reload(enum action action, bool graceful) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; const char *method; @@ -19,7 +19,7 @@ int daemon_reload(int argc, char *argv[], void *userdata) { polkit_agent_open_maybe(); - switch (arg_action) { + switch (action) { case ACTION_RELOAD: method = "Reload"; @@ -29,13 +29,8 @@ int daemon_reload(int argc, char *argv[], void *userdata) { method = "Reexecute"; break; - case ACTION_SYSTEMCTL: - method = streq(argv[0], "daemon-reexec") ? "Reexecute" : - /* "daemon-reload" */ "Reload"; - break; - default: - assert_not_reached(); + return -EINVAL; } r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, method); @@ -50,14 +45,36 @@ int daemon_reload(int argc, char *argv[], void *userdata) { r = sd_bus_call(bus, m, DEFAULT_TIMEOUT_USEC * 2, &error, NULL); /* On reexecution, we expect a disconnect, not a reply */ - if (IN_SET(r, -ETIMEDOUT, -ECONNRESET) && streq(method, "Reexecute")) - r = 0; + if (IN_SET(r, -ETIMEDOUT, -ECONNRESET) && action == ACTION_REEXEC) + return 1; + if (r < 0) { + if (graceful) { /* If graceful mode is selected, debug log, but don't fail */ + log_debug_errno(r, "Failed to reload daemon via the bus, ignoring: %s", bus_error_message(&error, r)); + return 0; + } - if (r < 0 && arg_action == ACTION_SYSTEMCTL) return log_error_errno(r, "Failed to reload daemon: %s", bus_error_message(&error, r)); + } - /* Note that for the legacy commands (i.e. those with action != ACTION_SYSTEMCTL) we support - * fallbacks to the old ways of doing things, hence don't log any error in that case here. */ - - return r < 0 ? r : 0; + return 1; +} + +int verb_daemon_reload(int argc, char *argv[], void *userdata) { + enum action a; + int r; + + assert(argc >= 1); + + if (streq(argv[0], "daemon-reexec")) + a = ACTION_REEXEC; + else if (streq(argv[0], "daemon-reload")) + a = ACTION_RELOAD; + else + assert_not_reached(); + + r = daemon_reload(a, /* graceful= */ false); + if (r < 0) + return r; + + return 0; } diff --git a/src/systemctl/systemctl-daemon-reload.h b/src/systemctl/systemctl-daemon-reload.h index a9fc00770..ced34ce44 100644 --- a/src/systemctl/systemctl-daemon-reload.h +++ b/src/systemctl/systemctl-daemon-reload.h @@ -1,4 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int daemon_reload(int argc, char *argv[], void *userdata); +#include "systemctl.h" + +int daemon_reload(enum action, bool graceful); + +int verb_daemon_reload(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-edit.c b/src/systemctl/systemctl-edit.c index b59a67ac2..446dfd7dd 100644 --- a/src/systemctl/systemctl-edit.c +++ b/src/systemctl/systemctl-edit.c @@ -22,11 +22,10 @@ #define EDIT_MARKER_START "### Anything between here and the comment below will become the new contents of the file" #define EDIT_MARKER_END "### Lines below this comment will be discarded" -int cat(int argc, char *argv[], void *userdata) { +int verb_cat(int argc, char *argv[], void *userdata) { _cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL; _cleanup_(lookup_paths_free) LookupPaths lp = {}; _cleanup_strv_free_ char **names = NULL; - char **name; sd_bus *bus; bool first = true; int r, rc = 0; @@ -38,9 +37,9 @@ int cat(int argc, char *argv[], void *userdata) { if (arg_transport != BUS_TRANSPORT_LOCAL) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot remotely cat units."); - r = lookup_paths_init(&lp, arg_scope, 0, arg_root); + r = lookup_paths_init_or_warn(&lp, arg_scope, 0, arg_root); if (r < 0) - return log_error_errno(r, "Failed to determine unit paths: %m"); + return r; r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -100,7 +99,7 @@ int cat(int argc, char *argv[], void *userdata) { ansi_highlight_red(), ansi_highlight_red(), ansi_highlight_red(), - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", + arg_scope == LOOKUP_SCOPE_SYSTEM ? "" : " --user", ansi_normal()); r = cat_files(fragment_path, dropin_paths, 0); @@ -145,7 +144,6 @@ static int create_edit_temp_file(const char *new_path, const char *original_path } else if (original_unit_paths) { _cleanup_free_ char *new_contents = NULL; _cleanup_fclose_ FILE *f = NULL; - char **path; r = mac_selinux_create_file_prepare(new_path, S_IFREG); if (r < 0) @@ -318,9 +316,9 @@ static int run_editor(char **paths) { if (r < 0) return r; if (r == 0) { - char **editor_args = NULL, **tmp_path, **original_path; + char **editor_args = NULL; size_t n_editor_args = 0, i = 1, argc; - const char **args, *editor, *p; + const char **args, *editor; argc = strv_length(paths)/2 + 1; @@ -358,13 +356,13 @@ static int run_editor(char **paths) { if (n_editor_args > 0) execvp(args[0], (char* const*) args); - FOREACH_STRING(p, "editor", "nano", "vim", "vi") { - args[0] = p; - execvp(p, (char* const*) args); + FOREACH_STRING(name, "editor", "nano", "vim", "vi") { + args[0] = name; + execvp(name, (char* const*) args); /* We do not fail if the editor doesn't exist because we want to try each one of them * before failing. */ if (errno != ENOENT) { - log_error_errno(errno, "Failed to execute %s: %m", editor); + log_error_errno(errno, "Failed to execute %s: %m", name); _exit(EXIT_FAILURE); } } @@ -379,7 +377,6 @@ static int run_editor(char **paths) { static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { _cleanup_(hashmap_freep) Hashmap *cached_name_map = NULL, *cached_id_map = NULL; _cleanup_(lookup_paths_free) LookupPaths lp = {}; - char **name; int r; assert(names); @@ -409,8 +406,8 @@ static int find_paths_to_edit(sd_bus *bus, char **names, char ***paths) { if (!path) { if (!arg_force) { log_info("Run 'systemctl edit%s --force --full %s' to create a new unit.", - arg_scope == UNIT_FILE_GLOBAL ? " --global" : - arg_scope == UNIT_FILE_USER ? " --user" : "", + arg_scope == LOOKUP_SCOPE_GLOBAL ? " --global" : + arg_scope == LOOKUP_SCOPE_USER ? " --user" : "", *name); return -ENOENT; } @@ -497,11 +494,10 @@ static int trim_edit_markers(const char *path) { return 0; } -int edit(int argc, char *argv[], void *userdata) { +int verb_edit(int argc, char *argv[], void *userdata) { _cleanup_(lookup_paths_free) LookupPaths lp = {}; _cleanup_strv_free_ char **names = NULL; _cleanup_strv_free_ char **paths = NULL; - char **original, **tmp; sd_bus *bus; int r; @@ -511,9 +507,9 @@ int edit(int argc, char *argv[], void *userdata) { if (arg_transport != BUS_TRANSPORT_LOCAL) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units remotely."); - r = lookup_paths_init(&lp, arg_scope, 0, arg_root); + r = lookup_paths_init_or_warn(&lp, arg_scope, 0, arg_root); if (r < 0) - return log_error_errno(r, "Failed to determine unit paths: %m"); + return r; r = mac_selinux_init(); if (r < 0) @@ -569,8 +565,11 @@ int edit(int argc, char *argv[], void *userdata) { r = 0; - if (!arg_no_reload && !install_client_side()) - r = daemon_reload(argc, argv, userdata); + if (!arg_no_reload && !install_client_side()) { + r = daemon_reload(ACTION_RELOAD, /* graceful= */ false); + if (r > 0) + r = 0; + } end: STRV_FOREACH_PAIR(original, tmp, paths) { diff --git a/src/systemctl/systemctl-edit.h b/src/systemctl/systemctl-edit.h index fe7e4dc75..10dac5cb2 100644 --- a/src/systemctl/systemctl-edit.h +++ b/src/systemctl/systemctl-edit.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int cat(int argc, char *argv[], void *userdata); -int edit(int argc, char *argv[], void *userdata); +int verb_cat(int argc, char *argv[], void *userdata); +int verb_edit(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-enable.c b/src/systemctl/systemctl-enable.c index dcbe2c730..a1ca837d4 100644 --- a/src/systemctl/systemctl-enable.c +++ b/src/systemctl/systemctl-enable.c @@ -12,7 +12,6 @@ #include "systemctl.h" static int normalize_filenames(char **names) { - char **u; int r; STRV_FOREACH(u, names) @@ -39,8 +38,7 @@ static int normalize_filenames(char **names) { return 0; } -static int normalize_names(char **names, bool warn_if_path) { - char **u; +static int normalize_names(char **names) { bool was_path = false; STRV_FOREACH(u, names) { @@ -56,13 +54,13 @@ static int normalize_names(char **names, bool warn_if_path) { was_path = true; } - if (warn_if_path && was_path) + if (was_path) log_warning("Warning: Can't execute disable on the unit file path. Proceeding with the unit name."); return 0; } -int enable_unit(int argc, char *argv[], void *userdata) { +int verb_enable(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; const char *verb = argv[0]; UnitFileChange *changes = NULL; @@ -86,11 +84,13 @@ int enable_unit(int argc, char *argv[], void *userdata) { if (strv_isempty(names)) { if (arg_no_reload || install_client_side()) return 0; - return daemon_reload(argc, argv, userdata); + + r = daemon_reload(ACTION_RELOAD, /* graceful= */ false); + return r > 0 ? 0 : r; } if (streq(verb, "disable")) { - r = normalize_names(names, true); + r = normalize_names(names); if (r < 0) return r; } @@ -115,9 +115,9 @@ int enable_unit(int argc, char *argv[], void *userdata) { carries_install_info = r; } else if (streq(verb, "link")) r = unit_file_link(arg_scope, flags, arg_root, names, &changes, &n_changes); - else if (streq(verb, "preset")) { + else if (streq(verb, "preset")) r = unit_file_preset(arg_scope, flags, arg_root, names, arg_preset_mode, &changes, &n_changes); - } else if (streq(verb, "mask")) + else if (streq(verb, "mask")) r = unit_file_mask(arg_scope, flags, arg_root, names, &changes, &n_changes); else if (streq(verb, "unmask")) r = unit_file_unmask(arg_scope, flags, arg_root, names, &changes, &n_changes); @@ -139,10 +139,9 @@ int enable_unit(int argc, char *argv[], void *userdata) { sd_bus *bus; if (STR_IN_SET(verb, "mask", "unmask")) { - char **name; _cleanup_(lookup_paths_free) LookupPaths lp = {}; - r = lookup_paths_init(&lp, arg_scope, 0, arg_root); + r = lookup_paths_init_or_warn(&lp, arg_scope, 0, arg_root); if (r < 0) return r; @@ -234,9 +233,11 @@ int enable_unit(int argc, char *argv[], void *userdata) { goto finish; /* Try to reload if enabled */ - if (!arg_no_reload) - r = daemon_reload(argc, argv, userdata); - else + if (!arg_no_reload) { + r = daemon_reload(ACTION_RELOAD, /* graceful= */ false); + if (r > 0) + r = 0; + } else r = 0; } @@ -273,7 +274,7 @@ int enable_unit(int argc, char *argv[], void *userdata) { new_args[i + 1] = basename(names[i]); new_args[i + 1] = NULL; - r = start_unit(len + 1, new_args, userdata); + r = verb_start(len + 1, new_args, userdata); } } diff --git a/src/systemctl/systemctl-enable.h b/src/systemctl/systemctl-enable.h index 43f60e78e..f04bbcd62 100644 --- a/src/systemctl/systemctl-enable.h +++ b/src/systemctl/systemctl-enable.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int enable_unit(int argc, char *argv[], void *userdata); +int verb_enable(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-is-active.c b/src/systemctl/systemctl-is-active.c index d83736e94..7218e9001 100644 --- a/src/systemctl/systemctl-is-active.c +++ b/src/systemctl/systemctl-is-active.c @@ -13,7 +13,6 @@ static int check_unit_generic(int code, const UnitActiveState good_states[], int _cleanup_strv_free_ char **names = NULL; UnitActiveState active_state; sd_bus *bus; - char **name; int r; bool found = false; @@ -43,7 +42,7 @@ static int check_unit_generic(int code, const UnitActiveState good_states[], int return found ? 0 : code; } -int check_unit_active(int argc, char *argv[], void *userdata) { +int verb_is_active(int argc, char *argv[], void *userdata) { static const UnitActiveState states[] = { UNIT_ACTIVE, UNIT_RELOADING, @@ -53,7 +52,7 @@ int check_unit_active(int argc, char *argv[], void *userdata) { return check_unit_generic(EXIT_PROGRAM_NOT_RUNNING, states, ELEMENTSOF(states), strv_skip(argv, 1)); } -int check_unit_failed(int argc, char *argv[], void *userdata) { +int verb_is_failed(int argc, char *argv[], void *userdata) { static const UnitActiveState states[] = { UNIT_FAILED, }; diff --git a/src/systemctl/systemctl-is-active.h b/src/systemctl/systemctl-is-active.h index 9a5238e8c..950f29ac5 100644 --- a/src/systemctl/systemctl-is-active.h +++ b/src/systemctl/systemctl-is-active.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int check_unit_active(int argc, char *argv[], void *userdata); -int check_unit_failed(int argc, char *argv[], void *userdata); +int verb_is_active(int argc, char *argv[], void *userdata); +int verb_is_failed(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-is-enabled.c b/src/systemctl/systemctl-is-enabled.c index e33dffaf2..6f3d0b6ea 100644 --- a/src/systemctl/systemctl-is-enabled.c +++ b/src/systemctl/systemctl-is-enabled.c @@ -18,7 +18,7 @@ static int show_installation_targets_client_side(const char *name) { flags = UNIT_FILE_DRY_RUN | (arg_runtime ? UNIT_FILE_RUNTIME : 0); - r = unit_file_disable(UNIT_FILE_SYSTEM, flags, NULL, p, &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, flags, NULL, p, &changes, &n_changes); if (r < 0) return log_error_errno(r, "Failed to get file links for %s: %m", name); @@ -56,10 +56,9 @@ static int show_installation_targets(sd_bus *bus, const char *name) { return 0; } -int unit_is_enabled(int argc, char *argv[], void *userdata) { +int verb_is_enabled(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; bool enabled; - char **name; int r; r = mangle_names("to check", strv_skip(argv, 1), &names); diff --git a/src/systemctl/systemctl-is-enabled.h b/src/systemctl/systemctl-is-enabled.h index 5cb9e5c53..96dff95d6 100644 --- a/src/systemctl/systemctl-is-enabled.h +++ b/src/systemctl/systemctl-is-enabled.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int unit_is_enabled(int argc, char *argv[], void *userdata); +int verb_is_enabled(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-is-system-running.c b/src/systemctl/systemctl-is-system-running.c index ecebf0d11..ea89eb2cc 100644 --- a/src/systemctl/systemctl-is-system-running.c +++ b/src/systemctl/systemctl-is-system-running.c @@ -23,7 +23,7 @@ static int match_startup_finished(sd_bus_message *m, void *userdata, sd_bus_erro return 0; } -int is_system_running(int argc, char *argv[], void *userdata) { +int verb_is_system_running(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot_startup_finished = NULL; _cleanup_(sd_event_unrefp) sd_event* event = NULL; diff --git a/src/systemctl/systemctl-is-system-running.h b/src/systemctl/systemctl-is-system-running.h index 3d7e9fb83..de86211a9 100644 --- a/src/systemctl/systemctl-is-system-running.h +++ b/src/systemctl/systemctl-is-system-running.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int is_system_running(int argc, char *argv[], void *userdata); +int verb_is_system_running(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-kill.c b/src/systemctl/systemctl-kill.c index 489e75475..1a8b4a3ed 100644 --- a/src/systemctl/systemctl-kill.c +++ b/src/systemctl/systemctl-kill.c @@ -6,9 +6,9 @@ #include "systemctl-util.h" #include "systemctl.h" -int kill_unit(int argc, char *argv[], void *userdata) { +int verb_kill(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; - char *kill_who = NULL, **name; + char *kill_who = NULL; sd_bus *bus; int r, q; diff --git a/src/systemctl/systemctl-kill.h b/src/systemctl/systemctl-kill.h index a42d4f1f9..88b2eae4b 100644 --- a/src/systemctl/systemctl-kill.h +++ b/src/systemctl/systemctl-kill.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int kill_unit(int argc, char *argv[], void *userdata); +int verb_kill(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-list-dependencies.c b/src/systemctl/systemctl-list-dependencies.c index a536240a9..86d1c5b7c 100644 --- a/src/systemctl/systemctl-list-dependencies.c +++ b/src/systemctl/systemctl-list-dependencies.c @@ -63,7 +63,6 @@ static int list_dependencies_one( unsigned branches) { _cleanup_strv_free_ char **deps = NULL; - char **c; int r; assert(bus); @@ -136,9 +135,9 @@ static int list_dependencies_one( return 0; } -int list_dependencies(int argc, char *argv[], void *userdata) { +int verb_list_dependencies(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **units = NULL, **done = NULL; - char **u, **patterns; + char **patterns; sd_bus *bus; int r; diff --git a/src/systemctl/systemctl-list-dependencies.h b/src/systemctl/systemctl-list-dependencies.h index 724657014..1e68a5f9f 100644 --- a/src/systemctl/systemctl-list-dependencies.h +++ b/src/systemctl/systemctl-list-dependencies.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int list_dependencies(int argc, char *argv[], void *userdata); +int verb_list_dependencies(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-list-jobs.c b/src/systemctl/systemctl-list-jobs.c index 1a39416d3..f9eba2368 100644 --- a/src/systemctl/systemctl-list-jobs.c +++ b/src/systemctl/systemctl-list-jobs.c @@ -125,7 +125,7 @@ static bool output_show_job(struct job_info *job, char **patterns) { return strv_fnmatch_or_empty(patterns, job->name, FNM_NOESCAPE); } -int list_jobs(int argc, char *argv[], void *userdata) { +int verb_list_jobs(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ struct job_info *jobs = NULL; diff --git a/src/systemctl/systemctl-list-jobs.h b/src/systemctl/systemctl-list-jobs.h index aa4969639..b10ec79b3 100644 --- a/src/systemctl/systemctl-list-jobs.h +++ b/src/systemctl/systemctl-list-jobs.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int list_jobs(int argc, char *argv[], void *userdata); +int verb_list_jobs(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-list-machines.c b/src/systemctl/systemctl-list-machines.c index b4eb0bd4b..0abafa7bf 100644 --- a/src/systemctl/systemctl-list-machines.c +++ b/src/systemctl/systemctl-list-machines.c @@ -93,7 +93,6 @@ static int get_machine_list( struct machine_info *machine_infos = NULL; _cleanup_strv_free_ char **m = NULL; _cleanup_free_ char *hn = NULL; - char **i; int c = 0, r; hn = gethostname_malloc(); @@ -219,7 +218,7 @@ static int output_machines_list(struct machine_info *machine_infos, unsigned n) return 0; } -int list_machines(int argc, char *argv[], void *userdata) { +int verb_list_machines(int argc, char *argv[], void *userdata) { struct machine_info *machine_infos = NULL; sd_bus *bus; int r, rc; diff --git a/src/systemctl/systemctl-list-machines.h b/src/systemctl/systemctl-list-machines.h index 4a33e2b27..9dff0d172 100644 --- a/src/systemctl/systemctl-list-machines.h +++ b/src/systemctl/systemctl-list-machines.h @@ -7,7 +7,7 @@ #include "bus-map-properties.h" #include "time-util.h" -int list_machines(int argc, char *argv[], void *userdata); +int verb_list_machines(int argc, char *argv[], void *userdata); struct machine_info { bool is_host; diff --git a/src/systemctl/systemctl-list-unit-files.c b/src/systemctl/systemctl-list-unit-files.c index a729171de..552e85a06 100644 --- a/src/systemctl/systemctl-list-unit-files.c +++ b/src/systemctl/systemctl-list-unit-files.c @@ -38,12 +38,12 @@ static bool output_show_unit_file(const UnitFileList *u, char **states, char **p if (!dot) return false; - if (!strv_find(arg_types, dot+1)) + if (!strv_contains(arg_types, dot+1)) return false; } if (!strv_isempty(states) && - !strv_find(states, unit_file_state_to_string(u->state))) + !strv_contains(states, unit_file_state_to_string(u->state))) return false; return true; @@ -133,7 +133,7 @@ static int output_unit_file_list(const UnitFileList *units, unsigned c) { return 0; } -int list_unit_files(int argc, char *argv[], void *userdata) { +int verb_list_unit_files(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; _cleanup_free_ UnitFileList *units = NULL; unsigned c = 0; diff --git a/src/systemctl/systemctl-list-unit-files.h b/src/systemctl/systemctl-list-unit-files.h index 387233e01..4819fbd82 100644 --- a/src/systemctl/systemctl-list-unit-files.h +++ b/src/systemctl/systemctl-list-unit-files.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int list_unit_files(int argc, char *argv[], void *userdata); +int verb_list_unit_files(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-list-units.c b/src/systemctl/systemctl-list-units.c index 0c405fb7e..4f3d534bc 100644 --- a/src/systemctl/systemctl-list-units.c +++ b/src/systemctl/systemctl-list-units.c @@ -49,7 +49,6 @@ static int get_unit_list_recursive( if (arg_recursive) { _cleanup_strv_free_ char **machines = NULL; - char **i; r = sd_get_machine_names(&machines); if (r < 0) @@ -210,7 +209,7 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) { return 0; } -int list_units(int argc, char *argv[], void *userdata) { +int verb_list_units(int argc, char *argv[], void *userdata) { _cleanup_free_ UnitInfo *unit_infos = NULL; _cleanup_(message_set_freep) Set *replies = NULL; _cleanup_strv_free_ char **machines = NULL; @@ -425,7 +424,7 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { return 0; } -int list_sockets(int argc, char *argv[], void *userdata) { +int verb_list_sockets(int argc, char *argv[], void *userdata) { _cleanup_(message_set_freep) Set *replies = NULL; _cleanup_strv_free_ char **machines = NULL; _cleanup_strv_free_ char **sockets_with_suffix = NULL; @@ -688,7 +687,7 @@ usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) { return next_elapse; } -int list_timers(int argc, char *argv[], void *userdata) { +int verb_list_timers(int argc, char *argv[], void *userdata) { _cleanup_(message_set_freep) Set *replies = NULL; _cleanup_strv_free_ char **machines = NULL; _cleanup_strv_free_ char **timers_with_suffix = NULL; diff --git a/src/systemctl/systemctl-list-units.h b/src/systemctl/systemctl-list-units.h index ef2762771..7f4cee0d1 100644 --- a/src/systemctl/systemctl-list-units.h +++ b/src/systemctl/systemctl-list-units.h @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int list_units(int argc, char *argv[], void *userdata); -int list_sockets(int argc, char *argv[], void *userdata); -int list_timers(int argc, char *argv[], void *userdata); +int verb_list_units(int argc, char *argv[], void *userdata); +int verb_list_sockets(int argc, char *argv[], void *userdata); +int verb_list_timers(int argc, char *argv[], void *userdata); usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next); diff --git a/src/systemctl/systemctl-log-setting.c b/src/systemctl/systemctl-log-setting.c index e3e957647..88b2e49b1 100644 --- a/src/systemctl/systemctl-log-setting.c +++ b/src/systemctl/systemctl-log-setting.c @@ -21,7 +21,7 @@ static void give_log_control1_hint(const char *name) { " See the %s for details.", link ?: "org.freedesktop.LogControl1(5) man page"); } -int log_setting(int argc, char *argv[], void *userdata) { +int verb_log_setting(int argc, char *argv[], void *userdata) { sd_bus *bus; int r; @@ -66,7 +66,7 @@ static int service_name_to_dbus(sd_bus *bus, const char *name, char **ret_dbus_n return 0; } -int service_log_setting(int argc, char *argv[], void *userdata) { +int verb_service_log_setting(int argc, char *argv[], void *userdata) { sd_bus *bus; _cleanup_free_ char *unit = NULL, *dbus_name = NULL; int r; diff --git a/src/systemctl/systemctl-log-setting.h b/src/systemctl/systemctl-log-setting.h index 9a2e793f0..910d6c8af 100644 --- a/src/systemctl/systemctl-log-setting.h +++ b/src/systemctl/systemctl-log-setting.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int log_setting(int argc, char *argv[], void *userdata); -int service_log_setting(int argc, char *argv[], void *userdata); +int verb_log_setting(int argc, char *argv[], void *userdata); +int verb_service_log_setting(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-logind.c b/src/systemctl/systemctl-logind.c index 114641cdc..40521b859 100644 --- a/src/systemctl/systemctl-logind.c +++ b/src/systemctl/systemctl-logind.c @@ -112,7 +112,6 @@ int logind_check_inhibitors(enum action a) { uint32_t uid, pid; sd_bus *bus; unsigned c = 0; - char **s; int r; if (arg_check_inhibitors == 0 || arg_force > 0) @@ -297,7 +296,6 @@ int logind_schedule_shutdown(void) { #if ENABLE_LOGIND _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; const char *action; - const char *log_action; sd_bus *bus; int r; @@ -305,29 +303,9 @@ int logind_schedule_shutdown(void) { if (r < 0) return r; - switch (arg_action) { - case ACTION_HALT: - action = "halt"; - log_action = "Shutdown"; - break; - case ACTION_POWEROFF: - action = "poweroff"; - log_action = "Shutdown"; - break; - case ACTION_KEXEC: - action = "kexec"; - log_action = "Reboot via kexec"; - break; - case ACTION_EXIT: - action = "exit"; - log_action = "Shutdown"; - break; - case ACTION_REBOOT: - default: - action = "reboot"; - log_action = "Reboot"; - break; - } + action = action_table[arg_action].verb; + if (!action) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Scheduling not supported for this action."); if (arg_dry_run) action = strjoina("dry-", action); @@ -336,12 +314,11 @@ int logind_schedule_shutdown(void) { r = bus_call_method(bus, bus_login_mgr, "ScheduleShutdown", &error, NULL, "st", action, arg_when); if (r < 0) - return log_warning_errno(r, "Failed to call ScheduleShutdown in logind, proceeding with immediate shutdown: %s", bus_error_message(&error, r)); + return log_warning_errno(r, "Failed to schedule shutdown: %s", bus_error_message(&error, r)); if (!arg_quiet) - log_info("%s scheduled for %s, use 'shutdown -c' to cancel.", - log_action, - FORMAT_TIMESTAMP_STYLE(arg_when, arg_timestamp_style)); + logind_show_shutdown(); + return 0; #else return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), @@ -396,6 +373,15 @@ int logind_show_shutdown(void) { if (isempty(action)) return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "No scheduled shutdown."); + if (STR_IN_SET(action, "halt", "poweroff", "exit")) + action = "Shutdown"; + else if (streq(action, "kexec")) + action = "Reboot via kexec"; + else if (streq(action, "reboot")) + action = "Reboot"; + + /* If we don't recognize the action string, we'll show it as-is */ + log_info("%s scheduled for %s, use 'shutdown -c' to cancel.", action, FORMAT_TIMESTAMP_STYLE(elapse, arg_timestamp_style)); @@ -412,7 +398,6 @@ int help_boot_loader_entry(void) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_strv_free_ char **l = NULL; sd_bus *bus; - char **i; int r; r = acquire_bus(BUS_FULL, &bus); diff --git a/src/systemctl/systemctl-mount.c b/src/systemctl/systemctl-mount.c index 04e626550..d9ad332b2 100644 --- a/src/systemctl/systemctl-mount.c +++ b/src/systemctl/systemctl-mount.c @@ -7,7 +7,7 @@ #include "systemctl-util.h" #include "systemctl.h" -int mount_bind(int argc, char *argv[], void *userdata) { +int verb_bind(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *n = NULL; sd_bus *bus; @@ -41,7 +41,7 @@ int mount_bind(int argc, char *argv[], void *userdata) { return 0; } -int mount_image(int argc, char *argv[], void *userdata) { +int verb_mount_image(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; const char *unit = argv[1], *src = argv[2], *dest = argv[3]; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; diff --git a/src/systemctl/systemctl-mount.h b/src/systemctl/systemctl-mount.h index 60d6875d8..b2d075001 100644 --- a/src/systemctl/systemctl-mount.h +++ b/src/systemctl/systemctl-mount.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int mount_bind(int argc, char *argv[], void *userdata); -int mount_image(int argc, char *argv[], void *userdata); +int verb_bind(int argc, char *argv[], void *userdata); +int verb_mount_image(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-preset-all.c b/src/systemctl/systemctl-preset-all.c index b5eb199f4..8e36ddc0c 100644 --- a/src/systemctl/systemctl-preset-all.c +++ b/src/systemctl/systemctl-preset-all.c @@ -7,7 +7,7 @@ #include "systemctl-util.h" #include "systemctl.h" -int preset_all(int argc, char *argv[], void *userdata) { +int verb_preset_all(int argc, char *argv[], void *userdata) { UnitFileChange *changes = NULL; size_t n_changes = 0; int r; @@ -51,7 +51,9 @@ int preset_all(int argc, char *argv[], void *userdata) { goto finish; } - r = daemon_reload(argc, argv, userdata); + r = daemon_reload(ACTION_RELOAD, /* graceful= */ false); + if (r > 0) + r = 0; } finish: diff --git a/src/systemctl/systemctl-preset-all.h b/src/systemctl/systemctl-preset-all.h index f4f679040..4631e7ea3 100644 --- a/src/systemctl/systemctl-preset-all.h +++ b/src/systemctl/systemctl-preset-all.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int preset_all(int argc, char *argv[], void *userdata); +int verb_preset_all(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-reset-failed.c b/src/systemctl/systemctl-reset-failed.c index eee758646..1ca05339d 100644 --- a/src/systemctl/systemctl-reset-failed.c +++ b/src/systemctl/systemctl-reset-failed.c @@ -7,14 +7,13 @@ #include "systemctl-util.h" #include "systemctl.h" -int reset_failed(int argc, char *argv[], void *userdata) { +int verb_reset_failed(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; sd_bus *bus; - char **name; int r, q; if (argc <= 1) /* Shortcut to trivial_method() if no argument is given */ - return trivial_method(argc, argv, userdata); + return verb_trivial_method(argc, argv, userdata); r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) diff --git a/src/systemctl/systemctl-reset-failed.h b/src/systemctl/systemctl-reset-failed.h index 956bb469d..5da0659d6 100644 --- a/src/systemctl/systemctl-reset-failed.h +++ b/src/systemctl/systemctl-reset-failed.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int reset_failed(int argc, char *argv[], void *userdata); +int verb_reset_failed(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-service-watchdogs.c b/src/systemctl/systemctl-service-watchdogs.c index e57985109..620f46aaf 100644 --- a/src/systemctl/systemctl-service-watchdogs.c +++ b/src/systemctl/systemctl-service-watchdogs.c @@ -7,7 +7,7 @@ #include "systemctl-util.h" #include "systemctl.h" -int service_watchdogs(int argc, char *argv[], void *userdata) { +int verb_service_watchdogs(int argc, char *argv[], void *userdata) { sd_bus *bus; _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int b, r; diff --git a/src/systemctl/systemctl-service-watchdogs.h b/src/systemctl/systemctl-service-watchdogs.h index 11a53dbbf..2f59f5a3f 100644 --- a/src/systemctl/systemctl-service-watchdogs.h +++ b/src/systemctl/systemctl-service-watchdogs.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int service_watchdogs(int argc, char *argv[], void *userdata); +int verb_service_watchdogs(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-set-default.c b/src/systemctl/systemctl-set-default.c index 05c1894b1..5f9186aa3 100644 --- a/src/systemctl/systemctl-set-default.c +++ b/src/systemctl/systemctl-set-default.c @@ -76,7 +76,7 @@ static int determine_default(char **ret_name) { } } -int get_default(int argc, char *argv[], void *userdata) { +int verb_get_default(int argc, char *argv[], void *userdata) { _cleanup_free_ char *name = NULL; int r; @@ -91,7 +91,7 @@ int get_default(int argc, char *argv[], void *userdata) { return 0; } -int set_default(int argc, char *argv[], void *userdata) { +int verb_set_default(int argc, char *argv[], void *userdata) { _cleanup_free_ char *unit = NULL; UnitFileChange *changes = NULL; size_t n_changes = 0; @@ -132,9 +132,11 @@ int set_default(int argc, char *argv[], void *userdata) { goto finish; /* Try to reload if enabled */ - if (!arg_no_reload) - r = daemon_reload(argc, argv, userdata); - else + if (!arg_no_reload) { + r = daemon_reload(ACTION_RELOAD, /* graceful= */ false); + if (r > 0) + r = 0; + } else r = 0; } diff --git a/src/systemctl/systemctl-set-default.h b/src/systemctl/systemctl-set-default.h index 839b2c9b9..7873e1267 100644 --- a/src/systemctl/systemctl-set-default.h +++ b/src/systemctl/systemctl-set-default.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int get_default(int argc, char *argv[], void *userdata); -int set_default(int argc, char *argv[], void *userdata); +int verb_get_default(int argc, char *argv[], void *userdata); +int verb_set_default(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-set-environment.c b/src/systemctl/systemctl-set-environment.c index aab0fe5fd..55d116051 100644 --- a/src/systemctl/systemctl-set-environment.c +++ b/src/systemctl/systemctl-set-environment.c @@ -10,36 +10,36 @@ static int json_transform_message(sd_bus_message *m, JsonVariant **ret) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; - char *text; + const char *text; int r; assert(m); assert(ret); while ((r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &text)) > 0) { - _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; + _cleanup_free_ char *n = NULL; + const char *sep; - char *sep = strchr(text, '='); + sep = strchr(text, '='); if (!sep) return log_error_errno(SYNTHETIC_ERRNO(EUCLEAN), "Invalid environment block"); - *sep++ = '\0'; + n = strndup(text, sep - text); + if (!n) + return log_oom(); - r = json_build(&w, JSON_BUILD_OBJECT(JSON_BUILD_PAIR(text, JSON_BUILD_STRING(sep)))); - if (r < 0) - return r; + sep++; - r = json_variant_merge(&v, w); + r = json_variant_set_field_string(&v, n, sep); if (r < 0) - return r; + return log_error_errno(r, "Failed to set JSON field '%s' to '%s': %m", n, sep); } if (r < 0) return bus_log_parse_error(r); *ret = TAKE_PTR(v); - - return r; + return 0; } static int print_variable(const char *s) { @@ -59,7 +59,7 @@ static int print_variable(const char *s) { return 0; } -int show_environment(int argc, char *argv[], void *userdata) { +int verb_show_environment(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; const char *text; @@ -82,13 +82,12 @@ int show_environment(int argc, char *argv[], void *userdata) { if (OUTPUT_MODE_IS_JSON(arg_output)) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; - JsonFormatFlags flags = output_mode_to_json_format_flags(arg_output); r = json_transform_message(reply, &v); if (r < 0) return r; - json_variant_dump(v, flags, stdout, NULL); + json_variant_dump(v, output_mode_to_json_format_flags(arg_output), stdout, NULL); } else { while ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &text)) > 0) { r = print_variable(text); @@ -112,7 +111,7 @@ static void invalid_callback(const char *p, void *userdata) { log_debug("Ignoring invalid environment assignment \"%s\".", strnull(t)); } -int set_environment(int argc, char *argv[], void *userdata) { +int verb_set_environment(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; const char *method; @@ -147,7 +146,7 @@ int set_environment(int argc, char *argv[], void *userdata) { return 0; } -int import_environment(int argc, char *argv[], void *userdata) { +int verb_import_environment(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; sd_bus *bus; @@ -172,7 +171,6 @@ int import_environment(int argc, char *argv[], void *userdata) { strv_env_clean_with_callback(copy, invalid_callback, NULL); - char **e; STRV_FOREACH(e, copy) if (string_has_cc(*e, NULL)) log_notice("Environment variable $%.*s contains control characters, importing anyway.", @@ -181,8 +179,6 @@ int import_environment(int argc, char *argv[], void *userdata) { r = sd_bus_message_append_strv(m, copy); } else { - char **a, **b; - r = sd_bus_message_open_container(m, 'a', "s"); if (r < 0) return bus_log_create_error(r); diff --git a/src/systemctl/systemctl-set-environment.h b/src/systemctl/systemctl-set-environment.h index bd05e318a..404258aa4 100644 --- a/src/systemctl/systemctl-set-environment.h +++ b/src/systemctl/systemctl-set-environment.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int show_environment(int argc, char *argv[], void *userdata); -int set_environment(int argc, char *argv[], void *userdata); -int import_environment(int argc, char *argv[], void *userdata); +int verb_show_environment(int argc, char *argv[], void *userdata); +int verb_set_environment(int argc, char *argv[], void *userdata); +int verb_import_environment(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-set-property.c b/src/systemctl/systemctl-set-property.c index 5739bac07..b8d702ac0 100644 --- a/src/systemctl/systemctl-set-property.c +++ b/src/systemctl/systemctl-set-property.c @@ -43,10 +43,9 @@ static int set_property_one(sd_bus *bus, const char *name, char **properties) { return 0; } -int set_property(int argc, char *argv[], void *userdata) { +int verb_set_property(int argc, char *argv[], void *userdata) { sd_bus *bus; _cleanup_strv_free_ char **names = NULL; - char **name; int r, k; r = acquire_bus(BUS_MANAGER, &bus); diff --git a/src/systemctl/systemctl-set-property.h b/src/systemctl/systemctl-set-property.h index 74990e7cb..0892291d5 100644 --- a/src/systemctl/systemctl-set-property.h +++ b/src/systemctl/systemctl-set-property.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int set_property(int argc, char *argv[], void *userdata); +int verb_set_property(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c index 9181a22eb..24e43321c 100644 --- a/src/systemctl/systemctl-show.c +++ b/src/systemctl/systemctl-show.c @@ -305,10 +305,8 @@ static void print_status_info( const char *active_on, *active_off, *on, *off, *ss, *fs; _cleanup_free_ char *formatted_path = NULL; - ExecStatusInfo *p; usec_t timestamp; const char *path; - char **t, **t2; int r; assert(i); @@ -367,7 +365,6 @@ static void print_status_info( if (!strv_isempty(i->dropin_paths)) { _cleanup_free_ char *dir = NULL; bool last = false; - char ** dropin; STRV_FOREACH(dropin, i->dropin_paths) { _cleanup_free_ char *dropin_formatted = NULL; @@ -421,7 +418,7 @@ static void print_status_info( STRPTR_IN_SET(i->active_state, "activating") ? i->inactive_exit_timestamp : i->active_exit_timestamp; - if (timestamp > 0 && timestamp < USEC_INFINITY) { + if (timestamp_is_set(timestamp)) { printf(" since %s; %s\n", FORMAT_TIMESTAMP_STYLE(timestamp, arg_timestamp_style), FORMAT_TIMESTAMP_RELATIVE(timestamp)); @@ -433,6 +430,18 @@ static void print_status_info( FORMAT_TIMESTAMP_STYLE(until_timestamp, arg_timestamp_style), FORMAT_TIMESTAMP_RELATIVE(until_timestamp)); } + + if (!endswith(i->id, ".target") && + STRPTR_IN_SET(i->active_state, "inactive", "failed") && + timestamp_is_set(i->active_enter_timestamp) && + timestamp_is_set(i->active_exit_timestamp) && + i->active_exit_timestamp >= i->active_enter_timestamp) { + + usec_t duration; + + duration = i->active_exit_timestamp - i->active_enter_timestamp; + printf(" Duration: %s\n", FORMAT_TIMESPAN(duration, MSEC_PER_SEC)); + } } else printf("\n"); @@ -455,7 +464,7 @@ static void print_status_info( dual_timestamp_get(&nw); next_elapse = calc_next_elapse(&nw, &next); - if (next_elapse > 0 && next_elapse < USEC_INFINITY) + if (timestamp_is_set(next_elapse)) printf(" Trigger: %s; %s\n", FORMAT_TIMESTAMP_STYLE(next_elapse, arg_timestamp_style), FORMAT_TIMESTAMP_RELATIVE(next_elapse)); @@ -476,7 +485,6 @@ static void print_status_info( } if (!i->condition_result && i->condition_timestamp > 0) { - UnitCondition *c; int n = 0; printf(" Condition: start %scondition failed%s at %s; %s\n", @@ -754,7 +762,7 @@ static void print_status_info( getuid(), get_output_flags() | OUTPUT_BEGIN_NEWLINE, SD_JOURNAL_LOCAL_ONLY, - arg_scope == UNIT_FILE_SYSTEM, + arg_scope == LOOKUP_SCOPE_SYSTEM, ellipsized); if (i->need_daemon_reload) @@ -762,8 +770,6 @@ static void print_status_info( } static void show_unit_help(UnitStatusInfo *i) { - char **p; - assert(i); if (!i->documentation) { @@ -1066,7 +1072,6 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m if (FLAGS_SET(flags, BUS_PRINT_PROPERTY_SHOW_EMPTY) || allow_list || !strv_isempty(l)) { bool first = true; - char **i; if (!FLAGS_SET(flags, BUS_PRINT_PROPERTY_ONLY_VALUE)) { fputs(name, stdout); @@ -1958,7 +1963,6 @@ static int show_one( .io_read_bytes = UINT64_MAX, .io_write_bytes = UINT64_MAX, }; - char **pp; int r; assert(path); @@ -2146,7 +2150,7 @@ static int show_system_status(sd_bus *bus) { return 0; } -int show(int argc, char *argv[], void *userdata) { +int verb_show(int argc, char *argv[], void *userdata) { bool new_line = false, ellipsized = false; SystemctlShowMode show_mode; int r, ret = 0; @@ -2156,7 +2160,7 @@ int show(int argc, char *argv[], void *userdata) { show_mode = systemctl_show_mode_from_string(argv[0]); if (show_mode < 0) - return log_error_errno(show_mode, "Invalid argument."); + return log_error_errno(show_mode, "Invalid argument '%s'.", argv[0]); if (show_mode == SYSTEMCTL_SHOW_HELP && argc <= 1) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), @@ -2182,7 +2186,6 @@ int show(int argc, char *argv[], void *userdata) { ret = show_all(bus, &new_line, &ellipsized); } else { _cleanup_free_ char **patterns = NULL; - char **name; STRV_FOREACH(name, strv_skip(argv, 1)) { _cleanup_free_ char *path = NULL, *unit = NULL; diff --git a/src/systemctl/systemctl-show.h b/src/systemctl/systemctl-show.h index d778bebb4..5aeed51e5 100644 --- a/src/systemctl/systemctl-show.h +++ b/src/systemctl/systemctl-show.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int show(int argc, char *argv[], void *userdata); +int verb_show(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-start-special.c b/src/systemctl/systemctl-start-special.c index 6ece700a9..cfd007896 100644 --- a/src/systemctl/systemctl-start-special.c +++ b/src/systemctl/systemctl-start-special.c @@ -18,7 +18,7 @@ #include "systemctl.h" static int load_kexec_kernel(void) { - _cleanup_(boot_config_free) BootConfig config = {}; + _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL; _cleanup_free_ char *kernel = NULL, *initrd = NULL, *options = NULL; const BootEntry *e; pid_t pid; @@ -32,7 +32,7 @@ static int load_kexec_kernel(void) { if (access(KEXEC, X_OK) < 0) return log_error_errno(errno, KEXEC" is not available: %m"); - r = boot_entries_load_config_auto(NULL, NULL, &config); + r = boot_config_load_auto(&config, NULL, NULL); if (r == -ENOKEY) /* The call doesn't log about ENOKEY, let's do so here. */ return log_error_errno(r, @@ -43,6 +43,10 @@ static int load_kexec_kernel(void) { if (r < 0) return r; + r = boot_config_select_special_entries(&config); + if (r < 0) + return r; + e = boot_config_default_entry(&config); if (!e) return log_error_errno(SYNTHETIC_ERRNO(ENOENT), @@ -116,7 +120,7 @@ static int set_exit_code(uint8_t code) { return 0; } -int start_special(int argc, char *argv[], void *userdata) { +int verb_start_special(int argc, char *argv[], void *userdata) { bool termination_action; /* An action that terminates the manager, can be performed also by * signal. */ enum action a; @@ -197,7 +201,7 @@ int start_special(int argc, char *argv[], void *userdata) { if (arg_force >= 1 && (termination_action || IN_SET(a, ACTION_KEXEC, ACTION_EXIT))) - r = trivial_method(argc, argv, userdata); + r = verb_trivial_method(argc, argv, userdata); else { /* First try logind, to allow authentication with polkit */ if (IN_SET(a, @@ -213,8 +217,8 @@ int start_special(int argc, char *argv[], void *userdata) { r = logind_reboot(a); if (r >= 0) return r; - if (IN_SET(r, -EOPNOTSUPP, -EINPROGRESS)) - /* Requested operation is not supported or already in progress */ + if (IN_SET(r, -EACCES, -EOPNOTSUPP, -EINPROGRESS)) + /* Requested operation requires auth, is not supported or already in progress */ return r; /* On all other errors, try low-level operation. In order to minimize the difference @@ -229,7 +233,7 @@ int start_special(int argc, char *argv[], void *userdata) { * behaviour. */ arg_no_block = true; - r = start_unit(argc, argv, userdata); + r = verb_start(argc, argv, userdata); } if (termination_action && arg_force < 2 && @@ -239,13 +243,13 @@ int start_special(int argc, char *argv[], void *userdata) { return r; } -int start_system_special(int argc, char *argv[], void *userdata) { +int verb_start_system_special(int argc, char *argv[], void *userdata) { /* Like start_special above, but raises an error when running in user mode */ - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Bad action for %s mode.", - arg_scope == UNIT_FILE_GLOBAL ? "--global" : "--user"); + arg_scope == LOOKUP_SCOPE_GLOBAL ? "--global" : "--user"); - return start_special(argc, argv, userdata); + return verb_start_special(argc, argv, userdata); } diff --git a/src/systemctl/systemctl-start-special.h b/src/systemctl/systemctl-start-special.h index 06875e9c6..9396321d7 100644 --- a/src/systemctl/systemctl-start-special.h +++ b/src/systemctl/systemctl-start-special.h @@ -1,5 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int start_special(int argc, char *argv[], void *userdata); -int start_system_special(int argc, char *argv[], void *userdata); +int verb_start_special(int argc, char *argv[], void *userdata); +int verb_start_system_special(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-start-unit.c b/src/systemctl/systemctl-start-unit.c index 274b278d2..42a5b086c 100644 --- a/src/systemctl/systemctl-start-unit.c +++ b/src/systemctl/systemctl-start-unit.c @@ -168,8 +168,8 @@ fail: BUS_ERROR_UNIT_MASKED, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE)) log_error("See %s logs and 'systemctl%s status%s %s' for details.", - arg_scope == UNIT_FILE_SYSTEM ? "system" : "user", - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", + arg_scope == LOOKUP_SCOPE_SYSTEM ? "system" : "user", + arg_scope == LOOKUP_SCOPE_SYSTEM ? "" : " --user", name[0] == '-' ? " --" : "", name); @@ -199,16 +199,13 @@ static int enqueue_marked_jobs( if (r < 0) return bus_log_parse_error(r); - if (w) { - char **path; - + if (w) STRV_FOREACH(path, paths) { log_debug("Adding %s to the set", *path); r = bus_wait_for_jobs_add(w, *path); if (r < 0) return log_error_errno(r, "Failed to watch job %s: %m", *path); } - } return 0; } @@ -245,7 +242,7 @@ static const char** make_extra_args(const char *extra_args[static 4]) { assert(extra_args); - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) extra_args[n++] = "--user"; if (arg_transport == BUS_TRANSPORT_REMOTE) { @@ -261,7 +258,7 @@ static const char** make_extra_args(const char *extra_args[static 4]) { return extra_args; } -int start_unit(int argc, char *argv[], void *userdata) { +int verb_start(int argc, char *argv[], void *userdata) { _cleanup_(bus_wait_for_units_freep) BusWaitForUnits *wu = NULL; _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL; const char *method, *job_type, *mode, *one_name, *suffix = NULL; @@ -269,7 +266,6 @@ int start_unit(int argc, char *argv[], void *userdata) { _cleanup_strv_free_ char **names = NULL; int r, ret = EXIT_SUCCESS; sd_bus *bus; - char **name; if (arg_wait && !STR_IN_SET(argv[0], "start", "restart")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), diff --git a/src/systemctl/systemctl-start-unit.h b/src/systemctl/systemctl-start-unit.h index 915c6fa7f..286501677 100644 --- a/src/systemctl/systemctl-start-unit.h +++ b/src/systemctl/systemctl-start-unit.h @@ -3,7 +3,7 @@ #include "systemctl.h" -int start_unit(int argc, char *argv[], void *userdata); +int verb_start(int argc, char *argv[], void *userdata); struct action_metadata { const char *target; diff --git a/src/systemctl/systemctl-switch-root.c b/src/systemctl/systemctl-switch-root.c index b80126797..669fa60df 100644 --- a/src/systemctl/systemctl-switch-root.c +++ b/src/systemctl/systemctl-switch-root.c @@ -11,7 +11,7 @@ #include "systemctl-util.h" #include "systemctl.h" -int switch_root(int argc, char *argv[], void *userdata) { +int verb_switch_root(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *cmdline_init = NULL; const char *root, *init; diff --git a/src/systemctl/systemctl-switch-root.h b/src/systemctl/systemctl-switch-root.h index 6e13961ab..e9ba12baf 100644 --- a/src/systemctl/systemctl-switch-root.h +++ b/src/systemctl/systemctl-switch-root.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int switch_root(int argc, char *argv[], void *userdata); +int verb_switch_root(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-sysv-compat.c b/src/systemctl/systemctl-sysv-compat.c index a78fa1e04..f6889993e 100644 --- a/src/systemctl/systemctl-sysv-compat.c +++ b/src/systemctl/systemctl-sysv-compat.c @@ -18,9 +18,8 @@ int talk_initctl(char rl) { #if HAVE_SYSV_COMPAT - struct init_request request; _cleanup_close_ int fd = -1; - const char *p; + const char *path; int r; /* Try to switch to the specified SysV runlevel. Returns == 0 if the operation does not apply on this @@ -29,19 +28,19 @@ int talk_initctl(char rl) { if (rl == 0) return 0; - FOREACH_STRING(p, "/run/initctl", "/dev/initctl") { - fd = open(p, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); - if (fd >= 0 || errno != ENOENT) + FOREACH_STRING(_path, "/run/initctl", "/dev/initctl") { + path = _path; + + fd = open(path, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); + if (fd < 0 && errno != ENOENT) + return log_error_errno(errno, "Failed to open %s: %m", path); + if (fd >= 0) break; } - if (fd < 0) { - if (errno == ENOENT) - return 0; + if (fd < 0) + return 0; - return log_error_errno(errno, "Failed to open initctl fifo: %m"); - } - - request = (struct init_request) { + struct init_request request = { .magic = INIT_MAGIC, .sleeptime = 0, .cmd = INIT_CMD_RUNLVL, @@ -50,7 +49,7 @@ int talk_initctl(char rl) { r = loop_write(fd, &request, sizeof(request), false); if (r < 0) - return log_error_errno(r, "Failed to write to %s: %m", p); + return log_error_errno(r, "Failed to write to %s: %m", path); return 1; #else @@ -117,7 +116,7 @@ int enable_sysv_units(const char *verb, char **args) { /* Processes all SysV units, and reshuffles the array so that afterwards only the native units remain */ - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) return 0; if (getenv_bool("SYSTEMCTL_SKIP_SYSV") > 0) @@ -129,7 +128,7 @@ int enable_sysv_units(const char *verb, char **args) { "is-enabled")) return 0; - r = lookup_paths_init(&paths, arg_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root); + r = lookup_paths_init_or_warn(&paths, arg_scope, LOOKUP_PATHS_EXCLUDE_GENERATED, arg_root); if (r < 0) return r; diff --git a/src/systemctl/systemctl-trivial-method.c b/src/systemctl/systemctl-trivial-method.c index c0b4d489a..5e530f3a1 100644 --- a/src/systemctl/systemctl-trivial-method.c +++ b/src/systemctl/systemctl-trivial-method.c @@ -8,7 +8,7 @@ /* A generic implementation for cases we just need to invoke a simple method call on the Manager object. */ -int trivial_method(int argc, char *argv[], void *userdata) { +int verb_trivial_method(int argc, char *argv[], void *userdata) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; const char *method; sd_bus *bus; diff --git a/src/systemctl/systemctl-trivial-method.h b/src/systemctl/systemctl-trivial-method.h index 6dcd15284..d36b4803d 100644 --- a/src/systemctl/systemctl-trivial-method.h +++ b/src/systemctl/systemctl-trivial-method.h @@ -1,4 +1,4 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #pragma once -int trivial_method(int argc, char *argv[], void *userdata); +int verb_trivial_method(int argc, char *argv[], void *userdata); diff --git a/src/systemctl/systemctl-util.c b/src/systemctl/systemctl-util.c index ae02af280..6a71bce5f 100644 --- a/src/systemctl/systemctl-util.c +++ b/src/systemctl/systemctl-util.c @@ -46,7 +46,7 @@ int acquire_bus(BusFocus focus, sd_bus **ret) { if (!buses[focus]) { bool user; - user = arg_scope != UNIT_FILE_SYSTEM; + user = arg_scope != LOOKUP_SCOPE_SYSTEM; if (focus == BUS_MANAGER) r = bus_connect_transport_systemd(arg_transport, arg_host, user, &buses[focus]); @@ -73,7 +73,7 @@ void ask_password_agent_open_maybe(void) { if (arg_dry_run) return; - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) return; ask_password_agent_open_if_enabled(arg_transport, arg_ask_password); @@ -82,7 +82,7 @@ void ask_password_agent_open_maybe(void) { void polkit_agent_open_maybe(void) { /* Open the polkit agent as a child process if necessary */ - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) return; polkit_agent_open_if_enabled(arg_transport, arg_ask_password); @@ -234,7 +234,6 @@ int get_unit_list( int expand_unit_names(sd_bus *bus, char **names, const char* suffix, char ***ret, bool *ret_expanded) { _cleanup_strv_free_ char **mangled = NULL, **globs = NULL; - char **name; int r; assert(bus); @@ -294,7 +293,6 @@ int check_triggering_units(sd_bus *bus, const char *unit) { _cleanup_strv_free_ char **triggered_by = NULL; bool print_warning_label = true; UnitActiveState active_state; - char **i; int r; r = unit_name_mangle(unit, 0, &n); @@ -382,12 +380,10 @@ void warn_unit_file_changed(const char *unit) { ansi_highlight_red(), ansi_normal(), unit, - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user"); + arg_scope == LOOKUP_SCOPE_SYSTEM ? "" : " --user"); } int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **ret_unit_path) { - char **p; - assert(lp); assert(unit_name); @@ -666,7 +662,6 @@ int unit_exists(LookupPaths *lp, const char *unit) { int append_unit_dependencies(sd_bus *bus, char **names, char ***ret) { _cleanup_strv_free_ char **with_deps = NULL; - char **name; assert(bus); assert(ret); @@ -783,7 +778,7 @@ bool output_show_unit(const UnitInfo *u, char **patterns) { if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE)) return false; - if (arg_types && !strv_find(arg_types, unit_type_suffix(u->id))) + if (arg_types && !strv_contains(arg_types, unit_type_suffix(u->id))) return false; if (arg_all) @@ -819,7 +814,7 @@ bool install_client_side(void) { if (!isempty(arg_root)) return true; - if (arg_scope == UNIT_FILE_GLOBAL) + if (arg_scope == LOOKUP_SCOPE_GLOBAL) return true; /* Unsupported environment variable, mostly for debugging purposes */ @@ -860,7 +855,7 @@ UnitFileFlags unit_file_flags_from_args(void) { int mangle_names(const char *operation, char **original_names, char ***ret_mangled_names) { _cleanup_strv_free_ char **l = NULL; - char **i, **name; + char **i; int r; assert(ret_mangled_names); @@ -873,18 +868,15 @@ int mangle_names(const char *operation, char **original_names, char ***ret_mangl /* When enabling units qualified path names are OK, too, hence allow them explicitly. */ - if (is_path(*name)) { - *i = strdup(*name); - if (!*i) - return log_oom(); - } else { + if (is_path(*name)) + r = path_make_absolute_cwd(*name, i); + else r = unit_name_mangle_with_suffix(*name, operation, arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN, ".service", i); - if (r < 0) { - *i = NULL; - return log_error_errno(r, "Failed to mangle unit name: %m"); - } + if (r < 0) { + *i = NULL; + return log_error_errno(r, "Failed to mangle unit name or path '%s': %m", *name); } i++; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 9031e685e..97ec51b7d 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -66,7 +66,7 @@ char **arg_properties = NULL; bool arg_all = false; enum dependency arg_dependency = DEPENDENCY_FORWARD; const char *_arg_job_mode = NULL; -UnitFileScope arg_scope = UNIT_FILE_SYSTEM; +LookupScope arg_scope = LOOKUP_SCOPE_SYSTEM; bool arg_wait = false; bool arg_no_block = false; int arg_legend = -1; /* -1: true, unless --quiet is passed, 1: true */ @@ -296,11 +296,8 @@ static int systemctl_help(void) { " --boot-loader-entry=NAME\n" " Boot into a specific boot loader entry on next boot\n" " --plain Print unit dependencies as a list instead of a tree\n" - " --timestamp=FORMAT Change format of printed timestamps.\n" - " 'pretty' (default): 'Day YYYY-MM-DD HH:MM:SS TZ\n" - " 'us': 'Day YYYY-MM-DD HH:MM:SS.UUUUUU TZ\n" - " 'utc': 'Day YYYY-MM-DD HH:MM:SS UTC\n" - " 'us+utc': 'Day YYYY-MM-DD HH:MM:SS.UUUUUU UTC\n" + " --timestamp=FORMAT Change format of printed timestamps (pretty, unix,\n" + " us, utc, us+utc)\n" " --read-only Create read-only bind mount\n" " --mkdir Create directory before mounting, if missing\n" " --marked Restart/reload previously marked units\n" @@ -619,15 +616,15 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; case ARG_USER: - arg_scope = UNIT_FILE_USER; + arg_scope = LOOKUP_SCOPE_USER; break; case ARG_SYSTEM: - arg_scope = UNIT_FILE_SYSTEM; + arg_scope = LOOKUP_SCOPE_SYSTEM; break; case ARG_GLOBAL: - arg_scope = UNIT_FILE_GLOBAL; + arg_scope = LOOKUP_SCOPE_GLOBAL; break; case ARG_WAIT: @@ -927,10 +924,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { /* If we are in --user mode, there's no point in talking to PolicyKit or the infra to query system * passwords */ - if (arg_scope != UNIT_FILE_SYSTEM) + if (arg_scope != LOOKUP_SCOPE_SYSTEM) arg_ask_password = false; - if (arg_transport == BUS_TRANSPORT_REMOTE && arg_scope != UNIT_FILE_SYSTEM) + if (arg_transport == BUS_TRANSPORT_REMOTE && arg_scope != LOOKUP_SCOPE_SYSTEM) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot access user instance remotely."); @@ -1022,83 +1019,83 @@ int systemctl_dispatch_parse_argv(int argc, char *argv[]) { #ifndef FUZZ_SYSTEMCTL_PARSE_ARGV static int systemctl_main(int argc, char *argv[]) { static const Verb verbs[] = { - { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, list_units }, - { "list-unit-files", VERB_ANY, VERB_ANY, 0, list_unit_files }, - { "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, 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 }, - { "stop", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, - { "condstop", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with ALTLinux */ - { "reload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, - { "restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, - { "try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, - { "reload-or-restart", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, - { "reload-or-try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with old systemctl <= 228 */ - { "try-reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, - { "force-reload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with SysV */ - { "condreload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with ALTLinux */ - { "condrestart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with RH */ - { "isolate", 2, 2, VERB_ONLINE_ONLY, start_unit }, - { "kill", 2, VERB_ANY, VERB_ONLINE_ONLY, kill_unit }, - { "clean", 2, VERB_ANY, VERB_ONLINE_ONLY, clean_or_freeze_unit }, - { "freeze", 2, VERB_ANY, VERB_ONLINE_ONLY, clean_or_freeze_unit }, - { "thaw", 2, VERB_ANY, VERB_ONLINE_ONLY, clean_or_freeze_unit }, - { "is-active", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active }, - { "check", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active }, /* deprecated alias of is-active */ - { "is-failed", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_failed }, - { "show", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, show }, - { "cat", 2, VERB_ANY, VERB_ONLINE_ONLY, cat }, - { "status", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, show }, - { "help", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, show }, - { "daemon-reload", VERB_ANY, 1, VERB_ONLINE_ONLY, daemon_reload }, - { "daemon-reexec", VERB_ANY, 1, VERB_ONLINE_ONLY, daemon_reload }, - { "log-level", VERB_ANY, 2, VERB_ONLINE_ONLY, log_setting }, - { "log-target", VERB_ANY, 2, VERB_ONLINE_ONLY, log_setting }, - { "service-log-level", 2, 3, VERB_ONLINE_ONLY, service_log_setting }, - { "service-log-target", 2, 3, VERB_ONLINE_ONLY, service_log_setting }, - { "service-watchdogs", VERB_ANY, 2, VERB_ONLINE_ONLY, service_watchdogs }, - { "show-environment", VERB_ANY, 1, VERB_ONLINE_ONLY, show_environment }, - { "set-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, set_environment }, - { "unset-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, set_environment }, - { "import-environment", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, import_environment }, - { "halt", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, - { "poweroff", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, - { "reboot", VERB_ANY, 2, VERB_ONLINE_ONLY, start_system_special }, - { "kexec", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, - { "suspend", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, - { "hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, - { "hybrid-sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, - { "suspend-then-hibernate",VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, - { "default", VERB_ANY, 1, VERB_ONLINE_ONLY, start_special }, - { "rescue", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, - { "emergency", VERB_ANY, 1, VERB_ONLINE_ONLY, start_system_special }, - { "exit", VERB_ANY, 2, VERB_ONLINE_ONLY, start_special }, - { "reset-failed", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, reset_failed }, - { "enable", 2, VERB_ANY, 0, enable_unit }, - { "disable", 2, VERB_ANY, 0, enable_unit }, - { "is-enabled", 2, VERB_ANY, 0, unit_is_enabled }, - { "reenable", 2, VERB_ANY, 0, enable_unit }, - { "preset", 2, VERB_ANY, 0, enable_unit }, - { "preset-all", VERB_ANY, 1, 0, preset_all }, - { "mask", 2, VERB_ANY, 0, enable_unit }, - { "unmask", 2, VERB_ANY, 0, enable_unit }, - { "link", 2, VERB_ANY, 0, enable_unit }, - { "revert", 2, VERB_ANY, 0, enable_unit }, - { "switch-root", 2, VERB_ANY, VERB_ONLINE_ONLY, switch_root }, - { "list-dependencies", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, list_dependencies }, - { "set-default", 2, 2, 0, set_default }, - { "get-default", VERB_ANY, 1, 0, get_default }, - { "set-property", 3, VERB_ANY, VERB_ONLINE_ONLY, set_property }, - { "is-system-running", VERB_ANY, 1, 0, is_system_running }, - { "add-wants", 3, VERB_ANY, 0, add_dependency }, - { "add-requires", 3, VERB_ANY, 0, add_dependency }, - { "edit", 2, VERB_ANY, VERB_ONLINE_ONLY, edit }, - { "bind", 3, 4, VERB_ONLINE_ONLY, mount_bind }, - { "mount-image", 4, 5, VERB_ONLINE_ONLY, mount_image }, + { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, verb_list_units }, + { "list-unit-files", VERB_ANY, VERB_ANY, 0, verb_list_unit_files }, + { "list-sockets", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_sockets }, + { "list-timers", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_timers }, + { "list-jobs", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_jobs }, + { "list-machines", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_machines }, + { "clear-jobs", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_trivial_method }, + { "cancel", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_cancel }, + { "start", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, + { "stop", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, + { "condstop", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with ALTLinux */ + { "reload", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, + { "restart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, + { "try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, + { "reload-or-restart", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, + { "reload-or-try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with old systemctl <= 228 */ + { "try-reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, + { "force-reload", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with SysV */ + { "condreload", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with ALTLinux */ + { "condrestart", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_start }, /* For compatibility with RH */ + { "isolate", 2, 2, VERB_ONLINE_ONLY, verb_start }, + { "kill", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_kill }, + { "clean", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_clean_or_freeze }, + { "freeze", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_clean_or_freeze }, + { "thaw", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_clean_or_freeze }, + { "is-active", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_is_active }, + { "check", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_is_active }, /* deprecated alias of is-active */ + { "is-failed", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_is_failed }, + { "show", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_show }, + { "cat", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_cat }, + { "status", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_show }, + { "help", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_show }, + { "daemon-reload", 1, 1, VERB_ONLINE_ONLY, verb_daemon_reload }, + { "daemon-reexec", 1, 1, VERB_ONLINE_ONLY, verb_daemon_reload }, + { "log-level", VERB_ANY, 2, VERB_ONLINE_ONLY, verb_log_setting }, + { "log-target", VERB_ANY, 2, VERB_ONLINE_ONLY, verb_log_setting }, + { "service-log-level", 2, 3, VERB_ONLINE_ONLY, verb_service_log_setting }, + { "service-log-target", 2, 3, VERB_ONLINE_ONLY, verb_service_log_setting }, + { "service-watchdogs", VERB_ANY, 2, VERB_ONLINE_ONLY, verb_service_watchdogs }, + { "show-environment", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_show_environment }, + { "set-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_set_environment }, + { "unset-environment", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_set_environment }, + { "import-environment", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_import_environment }, + { "halt", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, + { "poweroff", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, + { "reboot", VERB_ANY, 2, VERB_ONLINE_ONLY, verb_start_system_special }, + { "kexec", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, + { "suspend", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, + { "hibernate", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, + { "hybrid-sleep", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, + { "suspend-then-hibernate",VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, + { "default", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_special }, + { "rescue", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, + { "emergency", VERB_ANY, 1, VERB_ONLINE_ONLY, verb_start_system_special }, + { "exit", VERB_ANY, 2, VERB_ONLINE_ONLY, verb_start_special }, + { "reset-failed", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_reset_failed }, + { "enable", 2, VERB_ANY, 0, verb_enable }, + { "disable", 2, VERB_ANY, 0, verb_enable }, + { "is-enabled", 2, VERB_ANY, 0, verb_is_enabled }, + { "reenable", 2, VERB_ANY, 0, verb_enable }, + { "preset", 2, VERB_ANY, 0, verb_enable }, + { "preset-all", VERB_ANY, 1, 0, verb_preset_all }, + { "mask", 2, VERB_ANY, 0, verb_enable }, + { "unmask", 2, VERB_ANY, 0, verb_enable }, + { "link", 2, VERB_ANY, 0, verb_enable }, + { "revert", 2, VERB_ANY, 0, verb_enable }, + { "switch-root", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_switch_root }, + { "list-dependencies", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, verb_list_dependencies }, + { "set-default", 2, 2, 0, verb_set_default }, + { "get-default", VERB_ANY, 1, 0, verb_get_default }, + { "set-property", 3, VERB_ANY, VERB_ONLINE_ONLY, verb_set_property }, + { "is-system-running", VERB_ANY, 1, 0, verb_is_system_running }, + { "add-wants", 3, VERB_ANY, 0, verb_add_dependency }, + { "add-requires", 3, VERB_ANY, 0, verb_add_dependency }, + { "edit", 2, VERB_ANY, VERB_ONLINE_ONLY, verb_edit }, + { "bind", 3, 4, VERB_ONLINE_ONLY, verb_bind }, + { "mount-image", 4, 5, VERB_ONLINE_ONLY, verb_mount_image }, {} }; diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h index d6b9d7495..7507398c4 100644 --- a/src/systemctl/systemctl.h +++ b/src/systemctl/systemctl.h @@ -51,7 +51,7 @@ extern char **arg_properties; extern bool arg_all; extern enum dependency arg_dependency; extern const char *_arg_job_mode; -extern UnitFileScope arg_scope; +extern LookupScope arg_scope; extern bool arg_wait; extern bool arg_no_block; extern int arg_legend; diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index 4af4b45f2..834f80b42 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -44,67 +44,175 @@ enum { * The client may want to start acquiring link-local addresses. */ }; +/* https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options */ enum { - SD_DHCP_OPTION_PAD = 0, - SD_DHCP_OPTION_SUBNET_MASK = 1, - SD_DHCP_OPTION_TIME_OFFSET = 2, - SD_DHCP_OPTION_ROUTER = 3, - SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6, - SD_DHCP_OPTION_LPR_SERVER = 9, - SD_DHCP_OPTION_HOST_NAME = 12, - SD_DHCP_OPTION_BOOT_FILE_SIZE = 13, - SD_DHCP_OPTION_DOMAIN_NAME = 15, - SD_DHCP_OPTION_ROOT_PATH = 17, - SD_DHCP_OPTION_ENABLE_IP_FORWARDING = 19, - SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL = 20, - SD_DHCP_OPTION_POLICY_FILTER = 21, - SD_DHCP_OPTION_INTERFACE_MDR = 22, - SD_DHCP_OPTION_INTERFACE_TTL = 23, - SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24, - SD_DHCP_OPTION_INTERFACE_MTU = 26, - SD_DHCP_OPTION_BROADCAST = 28, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_ROUTER_DISCOVER = 31, - SD_DHCP_OPTION_STATIC_ROUTE = 33, - SD_DHCP_OPTION_NTP_SERVER = 42, - SD_DHCP_OPTION_VENDOR_SPECIFIC = 43, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_NETBIOS_NODETYPE = 46, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_NETBIOS_SCOPE = 47, - SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, - SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, - SD_DHCP_OPTION_OVERLOAD = 52, - SD_DHCP_OPTION_MESSAGE_TYPE = 53, - SD_DHCP_OPTION_SERVER_IDENTIFIER = 54, - SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, - SD_DHCP_OPTION_ERROR_MESSAGE = 56, - SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, - SD_DHCP_OPTION_RENEWAL_T1_TIME = 58, - SD_DHCP_OPTION_REBINDING_T2_TIME = 59, - SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, - SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, - SD_DHCP_OPTION_SMTP_SERVER = 69, - SD_DHCP_OPTION_POP3_SERVER = 70, - SD_DHCP_OPTION_USER_CLASS = 77, - SD_DHCP_OPTION_FQDN = 81, - SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82, - SD_DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, - SD_DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, - SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119, - SD_DHCP_OPTION_SIP_SERVER = 120, - SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, - SD_DHCP_OPTION_MUD_URL = 161, - SD_DHCP_OPTION_6RD = 212, + SD_DHCP_OPTION_PAD = 0, /* [RFC2132] */ + SD_DHCP_OPTION_SUBNET_MASK = 1, /* [RFC2132] */ + SD_DHCP_OPTION_TIME_OFFSET = 2, /* [RFC2132], deprecated by 100 and 101 */ + SD_DHCP_OPTION_ROUTER = 3, /* [RFC2132] */ + SD_DHCP_OPTION_TIME_SERVER = 4, /* [RFC2132] */ + SD_DHCP_OPTION_NAME_SERVER = 5, /* [RFC2132] */ + SD_DHCP_OPTION_DOMAIN_NAME_SERVER = 6, /* [RFC2132] */ + SD_DHCP_OPTION_LOG_SERVER = 7, /* [RFC2132] */ + SD_DHCP_OPTION_QUOTES_SERVER = 8, /* [RFC2132] */ + SD_DHCP_OPTION_LPR_SERVER = 9, /* [RFC2132] */ + SD_DHCP_OPTION_IMPRESS_SERVER = 10, /* [RFC2132] */ + SD_DHCP_OPTION_RLP_SERVER = 11, /* [RFC2132] */ + SD_DHCP_OPTION_HOST_NAME = 12, /* [RFC2132] */ + SD_DHCP_OPTION_BOOT_FILE_SIZE = 13, /* [RFC2132] */ + SD_DHCP_OPTION_MERIT_DUMP_FILE = 14, /* [RFC2132] */ + SD_DHCP_OPTION_DOMAIN_NAME = 15, /* [RFC2132] */ + SD_DHCP_OPTION_SWAP_SERVER = 16, /* [RFC2132] */ + SD_DHCP_OPTION_ROOT_PATH = 17, /* [RFC2132] */ + SD_DHCP_OPTION_EXTENSION_FILE = 18, /* [RFC2132] */ + SD_DHCP_OPTION_FORWARD = 19, /* [RFC2132] */ + SD_DHCP_OPTION_SOURCE_ROUTE = 20, /* [RFC2132] */ + SD_DHCP_OPTION_POLICY_FILTER = 21, /* [RFC2132] */ + SD_DHCP_OPTION_MAX_DATAGRAM_ASSEMBLY = 22, /* [RFC2132] */ + SD_DHCP_OPTION_DEFAULT_IP_TTL = 23, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_TIMEOUT = 24, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_PLATEAU = 25, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_INTERFACE = 26, /* [RFC2132] */ + SD_DHCP_OPTION_MTU_SUBNET = 27, /* [RFC2132] */ + SD_DHCP_OPTION_BROADCAST = 28, /* [RFC2132] */ + SD_DHCP_OPTION_MASK_DISCOVERY = 29, /* [RFC2132] */ + SD_DHCP_OPTION_MASK_SUPPLIER = 30, /* [RFC2132] */ + SD_DHCP_OPTION_ROUTER_DISCOVERY = 31, /* [RFC2132] */ + SD_DHCP_OPTION_ROUTER_REQUEST = 32, /* [RFC2132] */ + SD_DHCP_OPTION_STATIC_ROUTE = 33, /* [RFC2132] */ + SD_DHCP_OPTION_TRAILERS = 34, /* [RFC2132] */ + SD_DHCP_OPTION_ARP_TIMEOUT = 35, /* [RFC2132] */ + SD_DHCP_OPTION_ETHERNET = 36, /* [RFC2132] */ + SD_DHCP_OPTION_DEFAULT_TCP_TTL = 37, /* [RFC2132] */ + SD_DHCP_OPTION_KEEPALIVE_TIME = 38, /* [RFC2132] */ + SD_DHCP_OPTION_KEEPALIVE_DATA = 39, /* [RFC2132] */ + SD_DHCP_OPTION_NIS_DOMAIN = 40, /* [RFC2132] */ + SD_DHCP_OPTION_NIS_SERVER = 41, /* [RFC2132] */ + SD_DHCP_OPTION_NTP_SERVER = 42, /* [RFC2132] */ + SD_DHCP_OPTION_VENDOR_SPECIFIC = 43, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_NAME_SERVER = 44, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_DIST_SERVER = 45, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_NODE_TYPE = 46, /* [RFC2132] */ + SD_DHCP_OPTION_NETBIOS_SCOPE = 47, /* [RFC2132] */ + SD_DHCP_OPTION_X_WINDOW_FONT = 48, /* [RFC2132] */ + SD_DHCP_OPTION_X_WINDOW_MANAGER = 49, /* [RFC2132] */ + SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50, /* [RFC2132] */ + SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51, /* [RFC2132] */ + SD_DHCP_OPTION_OVERLOAD = 52, /* [RFC2132] */ + SD_DHCP_OPTION_MESSAGE_TYPE = 53, /* [RFC2132] */ + SD_DHCP_OPTION_SERVER_IDENTIFIER = 54, /* [RFC2132] */ + SD_DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, /* [RFC2132] */ + SD_DHCP_OPTION_ERROR_MESSAGE = 56, /* [RFC2132] */ + SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, /* [RFC2132] */ + SD_DHCP_OPTION_RENEWAL_TIME = 58, /* [RFC2132] */ + SD_DHCP_OPTION_REBINDING_TIME = 59, /* [RFC2132] */ + SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, /* [RFC2132] */ + SD_DHCP_OPTION_CLIENT_IDENTIFIER = 61, /* [RFC2132] */ + SD_DHCP_OPTION_NETWARE_IP_DOMAIN = 62, /* [RFC2242] */ + SD_DHCP_OPTION_NETWARE_IP_OPTION = 63, /* [RFC2242] */ + SD_DHCP_OPTION_NIS_DOMAIN_NAME = 64, /* [RFC2132] */ + SD_DHCP_OPTION_NIS_SERVER_ADDR = 65, /* [RFC2132] */ + SD_DHCP_OPTION_BOOT_SERVER_NAME = 66, /* [RFC2132] */ + SD_DHCP_OPTION_BOOT_FILENAME = 67, /* [RFC2132] */ + SD_DHCP_OPTION_HOME_AGENT_ADDRESSES = 68, /* [RFC2132] */ + SD_DHCP_OPTION_SMTP_SERVER = 69, /* [RFC2132] */ + SD_DHCP_OPTION_POP3_SERVER = 70, /* [RFC2132] */ + SD_DHCP_OPTION_NNTP_SERVER = 71, /* [RFC2132] */ + SD_DHCP_OPTION_WWW_SERVER = 72, /* [RFC2132] */ + SD_DHCP_OPTION_FINGER_SERVER = 73, /* [RFC2132] */ + SD_DHCP_OPTION_IRC_SERVER = 74, /* [RFC2132] */ + SD_DHCP_OPTION_STREETTALK_SERVER = 75, /* [RFC2132] */ + SD_DHCP_OPTION_STDA_SERVER = 76, /* [RFC2132] */ + SD_DHCP_OPTION_USER_CLASS = 77, /* [RFC3004] */ + SD_DHCP_OPTION_DIRECTORY_AGENT = 78, /* [RFC2610] */ + SD_DHCP_OPTION_SERVICE_SCOPE = 79, /* [RFC2610] */ + SD_DHCP_OPTION_RAPID_COMMIT = 80, /* [RFC4039] */ + SD_DHCP_OPTION_FQDN = 81, /* [RFC4702] */ + SD_DHCP_OPTION_RELAY_AGENT_INFORMATION = 82, /* [RFC3046] */ + SD_DHCP_OPTION_ISNS = 83, /* [RFC4174] */ + /* option code 84 is unassigned [RFC3679] */ + SD_DHCP_OPTION_NDS_SERVER = 85, /* [RFC2241] */ + SD_DHCP_OPTION_NDS_TREE_NAME = 86, /* [RFC2241] */ + SD_DHCP_OPTION_NDS_CONTEXT = 87, /* [RFC2241] */ + SD_DHCP_OPTION_BCMCS_CONTROLLER_DOMAIN_NAM = 88, /* [RFC4280] */ + SD_DHCP_OPTION_BCMCS_CONTROLLER_ADDRESS = 89, /* [RFC4280] */ + SD_DHCP_OPTION_AUTHENTICATION = 90, /* [RFC3118] */ + SD_DHCP_OPTION_CLIENT_LAST_TRANSACTION_TIME = 91, /* [RFC4388] */ + SD_DHCP_OPTION_ASSOCIATED_IP = 92, /* [RFC4388] */ + SD_DHCP_OPTION_CLIENT_SYSTEM = 93, /* [RFC4578] */ + SD_DHCP_OPTION_CLIENT_NDI = 94, /* [RFC4578] */ + SD_DHCP_OPTION_LDAP = 95, /* [RFC3679] */ + /* option code 96 is unassigned [RFC3679] */ + SD_DHCP_OPTION_UUID = 97, /* [RFC4578] */ + SD_DHCP_OPTION_USER_AUTHENTICATION = 98, /* [RFC2485] */ + SD_DHCP_OPTION_GEOCONF_CIVIC = 99, /* [RFC4776] */ + SD_DHCP_OPTION_POSIX_TIMEZONE = 100, /* [RFC4833] */ + SD_DHCP_OPTION_TZDB_TIMEZONE = 101, /* [RFC4833] */ + /* option codes 102-107 are unassigned [RFC3679] */ + SD_DHCP_OPTION_IPV6_ONLY_PREFERRED = 108, /* [RFC8925] */ + SD_DHCP_OPTION_DHCP4O6_SOURCE_ADDRESS = 109, /* [RFC8539] */ + /* option codes 110-111 are unassigned [RFC3679] */ + SD_DHCP_OPTION_NETINFO_ADDRESS = 112, /* [RFC3679] */ + SD_DHCP_OPTION_NETINFO_TAG = 113, /* [RFC3679] */ + SD_DHCP_OPTION_DHCP_CAPTIVE_PORTAL = 114, /* [RFC8910] */ + /* option code 115 is unassigned [RFC3679] */ + SD_DHCP_OPTION_AUTO_CONFIG = 116, /* [RFC2563] */ + SD_DHCP_OPTION_NAME_SERVICE_SEARCH = 117, /* [RFC2937] */ + SD_DHCP_OPTION_SUBNET_SELECTION = 118, /* [RFC3011] */ + SD_DHCP_OPTION_DOMAIN_SEARCH = 119, /* [RFC3397] */ + SD_DHCP_OPTION_SIP_SERVER = 120, /* [RFC3361] */ + SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, /* [RFC3442] */ + SD_DHCP_OPTION_CABLELABS_CLIENT_CONFIGURATION = 122, /* [RFC3495] */ + SD_DHCP_OPTION_GEOCONF = 123, /* [RFC6225] */ + SD_DHCP_OPTION_VENDOR_CLASS = 124, /* [RFC3925] */ + SD_DHCP_OPTION_VENDOR_SPECIFIC_INFORMATION = 125, /* [RFC3925] */ + /* option codes 126-127 are unassigned [RFC3679] */ + /* option codes 128-135 are assigned to use by PXE, but they are vendor specific [RFC4578] */ + SD_DHCP_OPTION_PANA_AGENT = 136, /* [RFC5192] */ + SD_DHCP_OPTION_LOST_SERVER_FQDN = 137, /* [RFC5223] */ + SD_DHCP_OPTION_CAPWAP_AC_ADDRESS = 138, /* [RFC5417] */ + SD_DHCP_OPTION_MOS_ADDRESS = 139, /* [RFC5678] */ + SD_DHCP_OPTION_MOS_FQDN = 140, /* [RFC5678] */ + SD_DHCP_OPTION_SIP_SERVICE_DOMAINS = 141, /* [RFC6011] */ + SD_DHCP_OPTION_ANDSF_ADDRESS = 142, /* [RFC6153] */ + SD_DHCP_OPTION_SZTP_REDIRECT = 143, /* [RFC8572] */ + SD_DHCP_OPTION_GEOLOC = 144, /* [RFC6225] */ + SD_DHCP_OPTION_FORCERENEW_NONCE_CAPABLE = 145, /* [RFC6704] */ + SD_DHCP_OPTION_RDNSS_SELECTION = 146, /* [RFC6731] */ + SD_DHCP_OPTION_DOTS_RI = 147, /* [RFC8973] */ + SD_DHCP_OPTION_DOTS_ADDRESS = 148, /* [RFC8973] */ + /* option code 149 is unassigned [RFC3942] */ + SD_DHCP_OPTION_TFTP_SERVER_ADDRESS = 150, /* [RFC5859] */ + SD_DHCP_OPTION_STATUS_CODE = 151, /* [RFC6926] */ + SD_DHCP_OPTION_BASE_TIME = 152, /* [RFC6926] */ + SD_DHCP_OPTION_START_TIME_OF_STATE = 153, /* [RFC6926] */ + SD_DHCP_OPTION_QUERY_START_TIME = 154, /* [RFC6926] */ + SD_DHCP_OPTION_QUERY_END_TIME = 155, /* [RFC6926] */ + SD_DHCP_OPTION_DHCP_STATE = 156, /* [RFC6926] */ + SD_DHCP_OPTION_DATA_SOURCE = 157, /* [RFC6926] */ + SD_DHCP_OPTION_PCP_SERVER = 158, /* [RFC7291] */ + SD_DHCP_OPTION_PORT_PARAMS = 159, /* [RFC7618] */ + /* option code 160 is unassigned [RFC7710][RFC8910] */ + SD_DHCP_OPTION_MUD_URL = 161, /* [RFC8520] */ + /* option codes 162-174 are unassigned [RFC3942] */ + /* option codes 175-177 are temporary assigned. */ + /* option codes 178-207 are unassigned [RFC3942] */ + SD_DHCP_OPTION_PXELINUX_MAGIC = 208, /* [RFC5071] Deprecated */ + SD_DHCP_OPTION_CONFIGURATION_FILE = 209, /* [RFC5071] */ + SD_DHCP_OPTION_PATH_PREFIX = 210, /* [RFC5071] */ + SD_DHCP_OPTION_REBOOT_TIME = 211, /* [RFC5071] */ + SD_DHCP_OPTION_6RD = 212, /* [RFC5969] */ + SD_DHCP_OPTION_ACCESS_DOMAIN = 213, /* [RFC5986] */ + /* option codes 214-219 are unassigned */ + SD_DHCP_OPTION_SUBNET_ALLOCATION = 220, /* [RFC6656] */ + SD_DHCP_OPTION_VIRTUAL_SUBNET_SELECTION = 221, /* [RFC6607] */ + /* option codes 222-223 are unassigned [RFC3942] */ + /* option codes 224-254 are reserved for private use */ SD_DHCP_OPTION_PRIVATE_BASE = 224, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, - /* Windows 10 option to send when Anonymize=true */ - SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252, + SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249, /* [RFC7844] */ + SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252, /* [RFC7844] */ SD_DHCP_OPTION_PRIVATE_LAST = 254, - SD_DHCP_OPTION_END = 255, + SD_DHCP_OPTION_END = 255, /* [RFC2132] */ }; /* Suboptions for SD_DHCP_OPTION_RELAY_AGENT_INFORMATION option */ diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h index 478bbfd7a..578ac6d4d 100644 --- a/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/sd-dhcp-lease.h @@ -67,7 +67,8 @@ int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname); int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains); int sd_dhcp_lease_get_hostname(sd_dhcp_lease *lease, const char **hostname); int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path); -int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes); +int sd_dhcp_lease_get_static_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret); +int sd_dhcp_lease_get_classless_routes(sd_dhcp_lease *lease, sd_dhcp_route ***ret); int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len); int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len); int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); @@ -82,7 +83,6 @@ int sd_dhcp_lease_get_6rd( int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination); int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length); int sd_dhcp_route_get_gateway(sd_dhcp_route *route, struct in_addr *gateway); -int sd_dhcp_route_get_option(sd_dhcp_route *route); _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp_lease, sd_dhcp_lease_unref); diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index 59ef27e1d..371834dd8 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -58,6 +58,9 @@ int sd_dhcp_server_stop(sd_dhcp_server *server); int sd_dhcp_server_configure_pool(sd_dhcp_server *server, const struct in_addr *address, unsigned char prefixlen, uint32_t offset, uint32_t size); +int sd_dhcp_server_set_boot_server_address(sd_dhcp_server *server, const struct in_addr *address); +int sd_dhcp_server_set_boot_server_name(sd_dhcp_server *server, const char *name); +int sd_dhcp_server_set_boot_filename(sd_dhcp_server *server, const char *filename); int sd_dhcp_server_set_bind_to_interface(sd_dhcp_server *server, int enabled); int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone); int sd_dhcp_server_set_router(sd_dhcp_server *server, const struct in_addr *address); diff --git a/src/systemd/sd-dhcp6-client.h b/src/systemd/sd-dhcp6-client.h index 0e23c84e6..d89b7d1c8 100644 --- a/src/systemd/sd-dhcp6-client.h +++ b/src/systemd/sd-dhcp6-client.h @@ -81,8 +81,8 @@ enum { SD_DHCP6_OPTION_SUBSCRIBER_ID = 38, /* RFC 4580 */ SD_DHCP6_OPTION_CLIENT_FQDN = 39, /* RFC 4704 */ SD_DHCP6_OPTION_PANA_AGENT = 40, /* RFC 5192 */ - SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE = 41, /* RFC 4833 */ - SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE = 42, /* RFC 4833 */ + SD_DHCP6_OPTION_POSIX_TIMEZONE = 41, /* RFC 4833 */ + SD_DHCP6_OPTION_TZDB_TIMEZONE = 42, /* RFC 4833 */ SD_DHCP6_OPTION_ERO = 43, /* RFC 4994 */ SD_DHCP6_OPTION_LQ_QUERY = 44, /* RFC 5007 */ SD_DHCP6_OPTION_CLIENT_DATA = 45, /* RFC 5007 */ @@ -251,7 +251,7 @@ int sd_dhcp6_client_set_request_vendor_class( int sd_dhcp6_client_set_prefix_delegation_hint( sd_dhcp6_client *client, uint8_t prefixlen, - const struct in6_addr *pd_address); + const struct in6_addr *pd_prefix); int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation); int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, @@ -260,8 +260,6 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request); int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request); -int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, - uint32_t transaction_id); int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v); diff --git a/src/systemd/sd-id128.h b/src/systemd/sd-id128.h index e64f3200a..f7d3244bb 100644 --- a/src/systemd/sd-id128.h +++ b/src/systemd/sd-id128.h @@ -35,11 +35,14 @@ union sd_id128 { }; #define SD_ID128_STRING_MAX 33U +#define SD_ID128_UUID_STRING_MAX 37U char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]); +char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]); int sd_id128_from_string(const char *s, sd_id128_t *ret); #define SD_ID128_TO_STRING(id) sd_id128_to_string((id), (char[SD_ID128_STRING_MAX]) {}) +#define SD_ID128_TO_UUID_STRING(id) sd_id128_to_uuid_string((id), (char[SD_ID128_UUID_STRING_MAX]) {}) int sd_id128_randomize(sd_id128_t *ret); diff --git a/src/systemd/sd-messages.h b/src/systemd/sd-messages.h index b9445cf0a..f3cca758a 100644 --- a/src/systemd/sd-messages.h +++ b/src/systemd/sd-messages.h @@ -200,6 +200,9 @@ _SD_BEGIN_DECLARATIONS; #define SD_MESSAGE_SYSTEMD_UDEV_SETTLE_DEPRECATED_STR \ SD_ID128_MAKE_STR(1c,04,54,c1,bd,22,41,e0,ac,6f,ef,b4,bc,63,14,33) +#define SD_MESSAGE_TIME_SYNC SD_ID128_MAKE(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37) +#define SD_MESSAGE_TIME_SYNC_STR SD_ID128_MAKE_STR(7c,8a,41,f3,7b,76,49,41,a0,e1,78,0b,1b,e2,f0,37) + _SD_END_DECLARATIONS; #endif diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index c166cc167..6030d9f9e 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -202,13 +202,11 @@ int sd_rtnl_message_routing_policy_rule_get_fib_type(sd_netlink_message *m, uint int sd_rtnl_message_routing_policy_rule_set_flags(sd_netlink_message *m, uint32_t flags); int sd_rtnl_message_routing_policy_rule_get_flags(sd_netlink_message *m, uint32_t *flags); -int sd_rtnl_message_new_qdisc(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex); -int sd_rtnl_message_set_qdisc_parent(sd_netlink_message *m, uint32_t parent); -int sd_rtnl_message_set_qdisc_handle(sd_netlink_message *m, uint32_t handle); - -int sd_rtnl_message_new_tclass(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int tcm_family, int tcm_ifindex); -int sd_rtnl_message_set_tclass_parent(sd_netlink_message *m, uint32_t parent); -int sd_rtnl_message_set_tclass_handle(sd_netlink_message *m, uint32_t handle); +int sd_rtnl_message_new_traffic_control(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, + int ifindex, uint32_t handle, uint32_t parent); +int sd_rtnl_message_traffic_control_get_ifindex(sd_netlink_message *m, int *ret); +int sd_rtnl_message_traffic_control_get_handle(sd_netlink_message *m, uint32_t *ret); +int sd_rtnl_message_traffic_control_get_parent(sd_netlink_message *m, uint32_t *ret); int sd_rtnl_message_new_mdb(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t nlmsg_type, int mdb_ifindex); diff --git a/src/systemd/sd-radv.h b/src/systemd/sd-radv.h index e604df137..6eeb92c48 100644 --- a/src/systemd/sd-radv.h +++ b/src/systemd/sd-radv.h @@ -59,8 +59,7 @@ int sd_radv_set_other_information(sd_radv *ra, int other); int sd_radv_set_preference(sd_radv *ra, unsigned preference); int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p); int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p); -sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, const struct in6_addr *prefix, - unsigned char prefixlen); +void sd_radv_remove_prefix(sd_radv *ra, const struct in6_addr *prefix, unsigned char prefixlen); int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, const struct in6_addr *dns, size_t n_dns); int sd_radv_set_dnssl(sd_radv *ra, uint32_t lifetime, char **search_list); diff --git a/src/sysupdate/meson.build b/src/sysupdate/meson.build new file mode 100644 index 000000000..2b1a25602 --- /dev/null +++ b/src/sysupdate/meson.build @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +systemd_sysupdate_sources = files(''' + sysupdate-instance.c + sysupdate-instance.h + sysupdate-partition.c + sysupdate-partition.h + sysupdate-pattern.c + sysupdate-pattern.h + sysupdate-resource.c + sysupdate-resource.h + sysupdate-transfer.c + sysupdate-transfer.h + sysupdate-update-set.c + sysupdate-update-set.h + sysupdate-util.c + sysupdate-util.h + sysupdate-cache.c + sysupdate-cache.h + sysupdate.c + sysupdate.h +'''.split()) diff --git a/src/sysupdate/sysupdate-cache.c b/src/sysupdate/sysupdate-cache.c new file mode 100644 index 000000000..8dad3ee47 --- /dev/null +++ b/src/sysupdate/sysupdate-cache.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "memory-util.h" +#include "sysupdate-cache.h" + +#define WEB_CACHE_ENTRIES_MAX 64U +#define WEB_CACHE_ITEM_SIZE_MAX (64U*1024U*1024U) + +static WebCacheItem* web_cache_item_free(WebCacheItem *i) { + if (!i) + return NULL; + + free(i->url); + return mfree(i); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(WebCacheItem*, web_cache_item_free); + +DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(web_cache_hash_ops, char, string_hash_func, string_compare_func, WebCacheItem, web_cache_item_free); + +int web_cache_add_item( + Hashmap **web_cache, + const char *url, + bool verified, + const void *data, + size_t size) { + + _cleanup_(web_cache_item_freep) WebCacheItem *item = NULL; + _cleanup_free_ char *u = NULL; + int r; + + assert(web_cache); + assert(url); + assert(data || size == 0); + + if (size > WEB_CACHE_ITEM_SIZE_MAX) + return -E2BIG; + + item = web_cache_get_item(*web_cache, url, verified); + if (item && memcmp_nn(item->data, item->size, data, size) == 0) + return 0; + + if (hashmap_size(*web_cache) >= (size_t) (WEB_CACHE_ENTRIES_MAX + !!hashmap_get(*web_cache, url))) + return -ENOSPC; + + r = hashmap_ensure_allocated(web_cache, &web_cache_hash_ops); + if (r < 0) + return r; + + u = strdup(url); + if (!u) + return -ENOMEM; + + item = malloc(offsetof(WebCacheItem, data) + size + 1); + if (!item) + return -ENOMEM; + + *item = (WebCacheItem) { + .url = TAKE_PTR(u), + .size = size, + .verified = verified, + }; + + /* Just to be extra paranoid, let's NUL terminate the downloaded buffer */ + *(uint8_t*) mempcpy(item->data, data, size) = 0; + + web_cache_item_free(hashmap_remove(*web_cache, url)); + + r = hashmap_put(*web_cache, item->url, item); + if (r < 0) + return r; + + TAKE_PTR(item); + return 1; +} + +WebCacheItem* web_cache_get_item(Hashmap *web_cache, const char *url, bool verified) { + WebCacheItem *i; + + i = hashmap_get(web_cache, url); + if (!i) + return NULL; + + if (i->verified != verified) + return NULL; + + return i; +} diff --git a/src/sysupdate/sysupdate-cache.h b/src/sysupdate/sysupdate-cache.h new file mode 100644 index 000000000..d6a789739 --- /dev/null +++ b/src/sysupdate/sysupdate-cache.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "hashmap.h" + +typedef struct WebCacheItem { + char *url; + bool verified; + size_t size; + uint8_t data[]; +} WebCacheItem; + +/* A simple in-memory cache for downloaded manifests. Very likely multiple transfers will use the same + * manifest URLs, hence let's make sure we only download them once within each sysupdate invocation. */ + +int web_cache_add_item(Hashmap **cache, const char *url, bool verified, const void *data, size_t size); + +WebCacheItem* web_cache_get_item(Hashmap *cache, const char *url, bool verified); diff --git a/src/sysupdate/sysupdate-instance.c b/src/sysupdate/sysupdate-instance.c new file mode 100644 index 000000000..16bfab912 --- /dev/null +++ b/src/sysupdate/sysupdate-instance.c @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "sysupdate-instance.h" + +void instance_metadata_destroy(InstanceMetadata *m) { + assert(m); + free(m->version); +} + +int instance_new( + Resource *rr, + const char *path, + const InstanceMetadata *f, + Instance **ret) { + + _cleanup_(instance_freep) Instance *i = NULL; + _cleanup_free_ char *p = NULL, *v = NULL; + + assert(rr); + assert(path); + assert(f); + assert(f->version); + assert(ret); + + p = strdup(path); + if (!p) + return log_oom(); + + v = strdup(f->version); + if (!v) + return log_oom(); + + i = new(Instance, 1); + if (!i) + return log_oom(); + + *i = (Instance) { + .resource = rr, + .metadata = *f, + .path = TAKE_PTR(p), + .partition_info = PARTITION_INFO_NULL, + }; + + i->metadata.version = TAKE_PTR(v); + + *ret = TAKE_PTR(i); + return 0; +} + +Instance *instance_free(Instance *i) { + if (!i) + return NULL; + + instance_metadata_destroy(&i->metadata); + + free(i->path); + partition_info_destroy(&i->partition_info); + + return mfree(i); +} diff --git a/src/sysupdate/sysupdate-instance.h b/src/sysupdate/sysupdate-instance.h new file mode 100644 index 000000000..2860d295d --- /dev/null +++ b/src/sysupdate/sysupdate-instance.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include +#include + +#include "sd-id128.h" + +#include "fs-util.h" +#include "time-util.h" + +typedef struct InstanceMetadata InstanceMetadata; +typedef struct Instance Instance; + +#include "sysupdate-resource.h" +#include "sysupdate-partition.h" + +struct InstanceMetadata { + /* Various bits of metadata for each instance, that is either derived from the filename/GPT label or + * from metadata of the file/partition itself */ + char *version; + sd_id128_t partition_uuid; + bool partition_uuid_set; + uint64_t partition_flags; /* GPT partition flags */ + bool partition_flags_set; + usec_t mtime; + mode_t mode; + uint64_t size; /* uncompressed size of the file */ + uint64_t tries_done, tries_left; /* for boot assessment counters */ + int no_auto; + int read_only; + int growfs; + uint8_t sha256sum[32]; /* SHA256 sum of the download (i.e. compressed) file */ + bool sha256sum_set; +}; + +#define INSTANCE_METADATA_NULL \ + { \ + .mtime = USEC_INFINITY, \ + .mode = MODE_INVALID, \ + .size = UINT64_MAX, \ + .tries_done = UINT64_MAX, \ + .tries_left = UINT64_MAX, \ + .no_auto = -1, \ + .read_only = -1, \ + .growfs = -1, \ + } + +struct Instance { + /* A pointer back to the resource this belongs to */ + Resource *resource; + + /* Metadata of this version */ + InstanceMetadata metadata; + + /* Where we found the instance */ + char *path; + PartitionInfo partition_info; +}; + +void instance_metadata_destroy(InstanceMetadata *m); + +int instance_new(Resource *rr, const char *path, const InstanceMetadata *f, Instance **ret); +Instance *instance_free(Instance *i); + +DEFINE_TRIVIAL_CLEANUP_FUNC(Instance*, instance_free); diff --git a/src/sysupdate/sysupdate-partition.c b/src/sysupdate/sysupdate-partition.c new file mode 100644 index 000000000..f3e21001e --- /dev/null +++ b/src/sysupdate/sysupdate-partition.c @@ -0,0 +1,379 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "alloc-util.h" +#include "extract-word.h" +#include "gpt.h" +#include "id128-util.h" +#include "parse-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "sysupdate-partition.h" +#include "util.h" + +void partition_info_destroy(PartitionInfo *p) { + assert(p); + + p->label = mfree(p->label); + p->device = mfree(p->device); +} + +static int fdisk_partition_get_attrs_as_uint64( + struct fdisk_partition *pa, + uint64_t *ret) { + + uint64_t flags = 0; + const char *a; + int r; + + assert(pa); + assert(ret); + + /* Retrieve current flags as uint64_t mask */ + + a = fdisk_partition_get_attrs(pa); + if (!a) { + *ret = 0; + return 0; + } + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&a, &word, ",", EXTRACT_DONT_COALESCE_SEPARATORS); + if (r < 0) + return r; + if (r == 0) + break; + + if (streq(word, "RequiredPartition")) + flags |= GPT_FLAG_REQUIRED_PARTITION; + else if (streq(word, "NoBlockIOProtocol")) + flags |= GPT_FLAG_NO_BLOCK_IO_PROTOCOL; + else if (streq(word, "LegacyBIOSBootable")) + flags |= GPT_FLAG_LEGACY_BIOS_BOOTABLE; + else { + const char *e; + unsigned u; + + /* Drop "GUID" prefix if specified */ + e = startswith(word, "GUID:") ?: word; + + if (safe_atou(e, &u) < 0) { + log_debug("Unknown partition flag '%s', ignoring.", word); + continue; + } + + if (u >= sizeof(flags)*8) { /* partition flags on GPT are 64bit. Let's ignore any further + bits should libfdisk report them */ + log_debug("Partition flag above bit 63 (%s), ignoring.", word); + continue; + } + + flags |= UINT64_C(1) << u; + } + } + + *ret = flags; + return 0; +} + +static int fdisk_partition_set_attrs_as_uint64( + struct fdisk_partition *pa, + uint64_t flags) { + + _cleanup_free_ char *attrs = NULL; + int r; + + assert(pa); + + for (unsigned i = 0; i < sizeof(flags) * 8; i++) { + if (!FLAGS_SET(flags, UINT64_C(1) << i)) + continue; + + r = strextendf_with_separator(&attrs, ",", "%u", i); + if (r < 0) + return r; + } + + return fdisk_partition_set_attrs(pa, strempty(attrs)); +} + +int read_partition_info( + struct fdisk_context *c, + struct fdisk_table *t, + size_t i, + PartitionInfo *ret) { + + _cleanup_free_ char *label_copy = NULL, *device = NULL; + const char *pts, *ids, *label; + struct fdisk_partition *p; + struct fdisk_parttype *pt; + uint64_t start, size, flags; + sd_id128_t ptid, id; + size_t partno; + int r; + + assert(c); + assert(t); + assert(ret); + + p = fdisk_table_get_partition(t, i); + if (!p) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to read partition metadata: %m"); + + if (fdisk_partition_is_used(p) <= 0) { + *ret = (PartitionInfo) PARTITION_INFO_NULL; + return 0; /* not found! */ + } + + if (fdisk_partition_has_partno(p) <= 0 || + fdisk_partition_has_start(p) <= 0 || + fdisk_partition_has_size(p) <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a number, position or size."); + + partno = fdisk_partition_get_partno(p); + + start = fdisk_partition_get_start(p); + assert(start <= UINT64_MAX / 512U); + start *= 512U; + + size = fdisk_partition_get_size(p); + assert(size <= UINT64_MAX / 512U); + size *= 512U; + + label = fdisk_partition_get_name(p); + if (!label) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a label."); + + pt = fdisk_partition_get_type(p); + if (!pt) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition: %m"); + + pts = fdisk_parttype_get_string(pt); + if (!pts) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to acquire type of partition as string: %m"); + + r = sd_id128_from_string(pts, &ptid); + if (r < 0) + return log_error_errno(r, "Failed to parse partition type UUID %s: %m", pts); + + ids = fdisk_partition_get_uuid(p); + if (!ids) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Found a partition without a UUID."); + + r = sd_id128_from_string(ids, &id); + if (r < 0) + return log_error_errno(r, "Failed to parse partition UUID %s: %m", ids); + + r = fdisk_partition_get_attrs_as_uint64(p, &flags); + if (r < 0) + return log_error_errno(r, "Failed to get partition flags: %m"); + + r = fdisk_partition_to_string(p, c, FDISK_FIELD_DEVICE, &device); + if (r != 0) + return log_error_errno(r, "Failed to get partition device name: %m"); + + label_copy = strdup(label); + if (!label_copy) + return log_oom(); + + *ret = (PartitionInfo) { + .partno = partno, + .start = start, + .size = size, + .flags = flags, + .type = ptid, + .uuid = id, + .label = TAKE_PTR(label_copy), + .device = TAKE_PTR(device), + .no_auto = FLAGS_SET(flags, GPT_FLAG_NO_AUTO) && gpt_partition_type_knows_no_auto(ptid), + .read_only = FLAGS_SET(flags, GPT_FLAG_READ_ONLY) && gpt_partition_type_knows_read_only(ptid), + .growfs = FLAGS_SET(flags, GPT_FLAG_GROWFS) && gpt_partition_type_knows_growfs(ptid), + }; + + return 1; /* found! */ +} + +int find_suitable_partition( + const char *device, + uint64_t space, + sd_id128_t *partition_type, + PartitionInfo *ret) { + + _cleanup_(partition_info_destroy) PartitionInfo smallest = PARTITION_INFO_NULL; + _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL; + _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL; + size_t n_partitions; + int r; + + assert(device); + assert(ret); + + c = fdisk_new_context(); + if (!c) + return log_oom(); + + r = fdisk_assign_device(c, device, /* readonly= */ true); + if (r < 0) + return log_error_errno(r, "Failed to open device '%s': %m", device); + + if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT)) + return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not suitable.", device); + + r = fdisk_get_partitions(c, &t); + if (r < 0) + return log_error_errno(r, "Failed to acquire partition table: %m"); + + n_partitions = fdisk_table_get_nents(t); + for (size_t i = 0; i < n_partitions; i++) { + _cleanup_(partition_info_destroy) PartitionInfo pinfo = PARTITION_INFO_NULL; + + r = read_partition_info(c, t, i, &pinfo); + if (r < 0) + return r; + if (r == 0) /* not assigned */ + continue; + + /* Filter out non-matching partition types */ + if (partition_type && !sd_id128_equal(pinfo.type, *partition_type)) + continue; + + if (!streq_ptr(pinfo.label, "_empty")) /* used */ + continue; + + if (space != UINT64_MAX && pinfo.size < space) /* too small */ + continue; + + if (smallest.partno != SIZE_MAX && smallest.size <= pinfo.size) /* already found smaller */ + continue; + + smallest = pinfo; + pinfo = (PartitionInfo) PARTITION_INFO_NULL; + } + + if (smallest.partno == SIZE_MAX) + return log_error_errno(SYNTHETIC_ERRNO(ENOSPC), "No available partition of a suitable size found."); + + *ret = smallest; + smallest = (PartitionInfo) PARTITION_INFO_NULL; + + return 0; +} + +int patch_partition( + const char *device, + const PartitionInfo *info, + PartitionChange change) { + + _cleanup_(fdisk_unref_partitionp) struct fdisk_partition *pa = NULL; + _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL; + bool tweak_no_auto, tweak_read_only, tweak_growfs; + int r, fd; + + assert(device); + assert(info); + assert(change <= _PARTITION_CHANGE_MAX); + + if (change == 0) /* Nothing to do */ + return 0; + + c = fdisk_new_context(); + if (!c) + return log_oom(); + + r = fdisk_assign_device(c, device, /* readonly= */ false); + if (r < 0) + return log_error_errno(r, "Failed to open device '%s': %m", device); + + assert_se((fd = fdisk_get_devfd(c)) >= 0); + + /* Make sure udev doesn't read the device while we make changes (this lock is released automatically + * by the kernel when the fd is closed, i.e. when the fdisk context is freed, hence no explicit + * unlock by us here anywhere.) */ + if (flock(fd, LOCK_EX) < 0) + return log_error_errno(errno, "Failed to lock block device '%s': %m", device); + + if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT)) + return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not suitable.", device); + + r = fdisk_get_partition(c, info->partno, &pa); + if (r < 0) + return log_error_errno(r, "Failed to read partition %zu of GPT label of '%s': %m", info->partno, device); + + if (change & PARTITION_LABEL) { + r = fdisk_partition_set_name(pa, info->label); + if (r < 0) + return log_error_errno(r, "Failed to update partition label: %m"); + } + + if (change & PARTITION_UUID) { + r = fdisk_partition_set_uuid(pa, SD_ID128_TO_UUID_STRING(info->uuid)); + if (r < 0) + return log_error_errno(r, "Failed to update partition UUID: %m"); + } + + /* Tweak the read-only flag, but only if supported by the partition type */ + tweak_no_auto = + FLAGS_SET(change, PARTITION_NO_AUTO) && + gpt_partition_type_knows_no_auto(info->type); + tweak_read_only = + FLAGS_SET(change, PARTITION_READ_ONLY) && + gpt_partition_type_knows_read_only(info->type); + tweak_growfs = + FLAGS_SET(change, PARTITION_GROWFS) && + gpt_partition_type_knows_growfs(info->type); + + if (change & PARTITION_FLAGS) { + uint64_t flags; + + /* Update the full flags parameter, and import the read-only flag into it */ + + flags = info->flags; + if (tweak_no_auto) + SET_FLAG(flags, GPT_FLAG_NO_AUTO, info->no_auto); + if (tweak_read_only) + SET_FLAG(flags, GPT_FLAG_READ_ONLY, info->read_only); + if (tweak_growfs) + SET_FLAG(flags, GPT_FLAG_GROWFS, info->growfs); + + r = fdisk_partition_set_attrs_as_uint64(pa, flags); + if (r < 0) + return log_error_errno(r, "Failed to update partition flags: %m"); + + } else if (tweak_no_auto || tweak_read_only || tweak_growfs) { + uint64_t old_flags, new_flags; + + /* So we aren't supposed to update the full flags parameter, but we are supposed to update + * the RO flag of it. */ + + r = fdisk_partition_get_attrs_as_uint64(pa, &old_flags); + if (r < 0) + return log_error_errno(r, "Failed to get old partition flags: %m"); + + new_flags = old_flags; + if (tweak_no_auto) + SET_FLAG(new_flags, GPT_FLAG_NO_AUTO, info->no_auto); + if (tweak_read_only) + SET_FLAG(new_flags, GPT_FLAG_READ_ONLY, info->read_only); + if (tweak_growfs) + SET_FLAG(new_flags, GPT_FLAG_GROWFS, info->growfs); + + if (new_flags != old_flags) { + r = fdisk_partition_set_attrs_as_uint64(pa, new_flags); + if (r < 0) + return log_error_errno(r, "Failed to update partition flags: %m"); + } + } + + r = fdisk_set_partition(c, info->partno, pa); + if (r < 0) + return log_error_errno(r, "Failed to update partition: %m"); + + r = fdisk_write_disklabel(c); + if (r < 0) + return log_error_errno(r, "Failed to write updated partition table: %m"); + + return 0; +} diff --git a/src/sysupdate/sysupdate-partition.h b/src/sysupdate/sysupdate-partition.h new file mode 100644 index 000000000..672eb93e9 --- /dev/null +++ b/src/sysupdate/sysupdate-partition.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include + +#include "sd-id128.h" + +#include "fdisk-util.h" +#include "macro.h" + +typedef struct PartitionInfo PartitionInfo; + +typedef enum PartitionChange { + PARTITION_FLAGS = 1 << 0, + PARTITION_NO_AUTO = 1 << 1, + PARTITION_READ_ONLY = 1 << 2, + PARTITION_GROWFS = 1 << 3, + PARTITION_UUID = 1 << 4, + PARTITION_LABEL = 1 << 5, + _PARTITION_CHANGE_MAX = (1 << 6) - 1, /* all of the above */ + _PARTITION_CHANGE_INVALID = -EINVAL, +} PartitionChange; + +struct PartitionInfo { + size_t partno; + uint64_t start, size; + uint64_t flags; + sd_id128_t type, uuid; + char *label; + char *device; /* Note that this might point to some non-existing path in case we operate on a loopback file */ + bool no_auto:1; + bool read_only:1; + bool growfs:1; +}; + +#define PARTITION_INFO_NULL \ + { \ + .partno = SIZE_MAX, \ + .start = UINT64_MAX, \ + .size = UINT64_MAX, \ + } + +void partition_info_destroy(PartitionInfo *p); + +int read_partition_info(struct fdisk_context *c, struct fdisk_table *t, size_t i, PartitionInfo *ret); + +int find_suitable_partition(const char *device, uint64_t space, sd_id128_t *partition_type, PartitionInfo *ret); +int patch_partition(const char *device, const PartitionInfo *info, PartitionChange change); diff --git a/src/sysupdate/sysupdate-pattern.c b/src/sysupdate/sysupdate-pattern.c new file mode 100644 index 000000000..c9228687f --- /dev/null +++ b/src/sysupdate/sysupdate-pattern.c @@ -0,0 +1,602 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "alloc-util.h" +#include "hexdecoct.h" +#include "list.h" +#include "parse-util.h" +#include "path-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "sysupdate-pattern.h" +#include "sysupdate-util.h" + +typedef enum PatternElementType { + PATTERN_LITERAL, + PATTERN_VERSION, + PATTERN_PARTITION_UUID, + PATTERN_PARTITION_FLAGS, + PATTERN_MTIME, + PATTERN_MODE, + PATTERN_SIZE, + PATTERN_TRIES_DONE, + PATTERN_TRIES_LEFT, + PATTERN_NO_AUTO, + PATTERN_READ_ONLY, + PATTERN_GROWFS, + PATTERN_SHA256SUM, + _PATTERN_ELEMENT_TYPE_MAX, + _PATTERN_ELEMENT_TYPE_INVALID = -EINVAL, +} PatternElementType; + +typedef struct PatternElement PatternElement; + +struct PatternElement { + PatternElementType type; + LIST_FIELDS(PatternElement, elements); + char literal[]; +}; + +static PatternElement *pattern_element_free_all(PatternElement *e) { + PatternElement *p; + + while ((p = LIST_POP(elements, e))) + free(p); + + return NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(PatternElement*, pattern_element_free_all); + +static PatternElementType pattern_element_type_from_char(char c) { + switch (c) { + case 'v': + return PATTERN_VERSION; + case 'u': + return PATTERN_PARTITION_UUID; + case 'f': + return PATTERN_PARTITION_FLAGS; + case 't': + return PATTERN_MTIME; + case 'm': + return PATTERN_MODE; + case 's': + return PATTERN_SIZE; + case 'd': + return PATTERN_TRIES_DONE; + case 'l': + return PATTERN_TRIES_LEFT; + case 'a': + return PATTERN_NO_AUTO; + case 'r': + return PATTERN_READ_ONLY; + case 'g': + return PATTERN_GROWFS; + case 'h': + return PATTERN_SHA256SUM; + default: + return _PATTERN_ELEMENT_TYPE_INVALID; + } +} + +static bool valid_char(char x) { + + /* Let's refuse control characters here, and let's reserve some characters typically used in pattern + * languages so that we can use them later, possibly. */ + + if ((unsigned) x < ' ' || x >= 127) + return false; + + return !IN_SET(x, '$', '*', '?', '[', ']', '!', '\\', '/', '|'); +} + +static int pattern_split( + const char *pattern, + PatternElement **ret) { + + _cleanup_(pattern_element_free_allp) PatternElement *first = NULL; + bool at = false, last_literal = true; + PatternElement *last = NULL; + uint64_t mask_found = 0; + size_t l, k = 0; + + assert(pattern); + + l = strlen(pattern); + + for (const char *e = pattern; *e != 0; e++) { + if (*e == '@') { + if (!at) { + at = true; + continue; + } + + /* Two at signs in a sequence, write out one */ + at = false; + + } else if (at) { + PatternElementType t; + uint64_t bit; + + t = pattern_element_type_from_char(*e); + if (t < 0) + return log_debug_errno(t, "Unknown pattern field marker '@%c'.", *e); + + bit = UINT64_C(1) << t; + if (mask_found & bit) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Pattern field marker '@%c' appears twice in pattern.", *e); + + /* We insist that two pattern field markers are separated by some literal string that + * we can use to separate the fields when parsing. */ + if (!last_literal) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Found two pattern field markers without separating literal."); + + if (ret) { + PatternElement *z; + + z = malloc(offsetof(PatternElement, literal)); + if (!z) + return -ENOMEM; + + z->type = t; + LIST_INSERT_AFTER(elements, first, last, z); + last = z; + } + + mask_found |= bit; + last_literal = at = false; + continue; + } + + if (!valid_char(*e)) + return log_debug_errno(SYNTHETIC_ERRNO(EBADRQC), "Invalid character 0x%0x in pattern, refusing.", *e); + + last_literal = true; + + if (!ret) + continue; + + if (!last || last->type != PATTERN_LITERAL) { + PatternElement *z; + + z = malloc0(offsetof(PatternElement, literal) + l + 1); /* l is an upper bound to all literal elements */ + if (!z) + return -ENOMEM; + + z->type = PATTERN_LITERAL; + k = 0; + + LIST_INSERT_AFTER(elements, first, last, z); + last = z; + } + + assert(last); + assert(last->type == PATTERN_LITERAL); + + last->literal[k++] = *e; + } + + if (at) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Trailing @ character found, refusing."); + if (!(mask_found & (UINT64_C(1) << PATTERN_VERSION))) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Version field marker '@v' not specified in pattern, refusing."); + + if (ret) + *ret = TAKE_PTR(first); + + return 0; +} + +int pattern_match(const char *pattern, const char *s, InstanceMetadata *ret) { + _cleanup_(instance_metadata_destroy) InstanceMetadata found = INSTANCE_METADATA_NULL; + _cleanup_(pattern_element_free_allp) PatternElement *elements = NULL; + const char *p; + int r; + + assert(pattern); + assert(s); + + r = pattern_split(pattern, &elements); + if (r < 0) + return r; + + p = s; + LIST_FOREACH(elements, e, elements) { + _cleanup_free_ char *t = NULL; + const char *n; + + if (e->type == PATTERN_LITERAL) { + const char *k; + + /* Skip literal fields */ + k = startswith(p, e->literal); + if (!k) + goto nope; + + p = k; + continue; + } + + if (e->elements_next) { + /* The next element must be literal, as we use it to determine where to split */ + assert(e->elements_next->type == PATTERN_LITERAL); + + n = strstr(p, e->elements_next->literal); + if (!n) + goto nope; + + } else + /* End of the string */ + assert_se(n = strchr(p, 0)); + t = strndup(p, n - p); + if (!t) + return -ENOMEM; + + switch (e->type) { + + case PATTERN_VERSION: + if (!version_is_valid(t)) { + log_debug("Version string is not valid, refusing: %s", t); + goto nope; + } + + assert(!found.version); + found.version = TAKE_PTR(t); + break; + + case PATTERN_PARTITION_UUID: { + sd_id128_t id; + + if (sd_id128_from_string(t, &id) < 0) + goto nope; + + assert(!found.partition_uuid_set); + found.partition_uuid = id; + found.partition_uuid_set = true; + break; + } + + case PATTERN_PARTITION_FLAGS: { + uint64_t f; + + if (safe_atoux64(t, &f) < 0) + goto nope; + + if (found.partition_flags_set && found.partition_flags != f) + goto nope; + + assert(!found.partition_flags_set); + found.partition_flags = f; + found.partition_flags_set = true; + break; + } + + case PATTERN_MTIME: { + uint64_t v; + + if (safe_atou64(t, &v) < 0) + goto nope; + if (v == USEC_INFINITY) /* Don't permit our internal special infinity value */ + goto nope; + if (v / 1000000U > TIME_T_MAX) /* Make sure this fits in a timespec structure */ + goto nope; + + assert(found.mtime == USEC_INFINITY); + found.mtime = v; + break; + } + + case PATTERN_MODE: { + mode_t m; + + r = parse_mode(t, &m); + if (r < 0) + goto nope; + if (m & ~0775) /* Don't allow world-writable files or suid files to be generated this way */ + goto nope; + + assert(found.mode == MODE_INVALID); + found.mode = m; + break; + } + + case PATTERN_SIZE: { + uint64_t u; + + r = safe_atou64(t, &u); + if (r < 0) + goto nope; + if (u == UINT64_MAX) + goto nope; + + assert(found.size == UINT64_MAX); + found.size = u; + break; + } + + case PATTERN_TRIES_DONE: { + uint64_t u; + + r = safe_atou64(t, &u); + if (r < 0) + goto nope; + if (u == UINT64_MAX) + goto nope; + + assert(found.tries_done == UINT64_MAX); + found.tries_done = u; + break; + } + + case PATTERN_TRIES_LEFT: { + uint64_t u; + + r = safe_atou64(t, &u); + if (r < 0) + goto nope; + if (u == UINT64_MAX) + goto nope; + + assert(found.tries_left == UINT64_MAX); + found.tries_left = u; + break; + } + + case PATTERN_NO_AUTO: + r = parse_boolean(t); + if (r < 0) + goto nope; + + assert(found.no_auto < 0); + found.no_auto = r; + break; + + case PATTERN_READ_ONLY: + r = parse_boolean(t); + if (r < 0) + goto nope; + + assert(found.read_only < 0); + found.read_only = r; + break; + + case PATTERN_GROWFS: + r = parse_boolean(t); + if (r < 0) + goto nope; + + assert(found.growfs < 0); + found.growfs = r; + break; + + case PATTERN_SHA256SUM: { + _cleanup_free_ void *d = NULL; + size_t l; + + if (strlen(t) != sizeof(found.sha256sum) * 2) + goto nope; + + r = unhexmem(t, sizeof(found.sha256sum) * 2, &d, &l); + if (r == -ENOMEM) + return r; + if (r < 0) + goto nope; + + assert(!found.sha256sum_set); + assert(l == sizeof(found.sha256sum)); + memcpy(found.sha256sum, d, l); + found.sha256sum_set = true; + break; + } + + default: + assert_se("unexpected pattern element"); + } + + p = n; + } + + if (ret) { + *ret = found; + found = (InstanceMetadata) INSTANCE_METADATA_NULL; + } + + return true; + +nope: + if (ret) + *ret = (InstanceMetadata) INSTANCE_METADATA_NULL; + + return false; +} + +int pattern_match_many(char **patterns, const char *s, InstanceMetadata *ret) { + _cleanup_(instance_metadata_destroy) InstanceMetadata found = INSTANCE_METADATA_NULL; + int r; + + STRV_FOREACH(p, patterns) { + r = pattern_match(*p, s, &found); + if (r < 0) + return r; + if (r > 0) { + if (ret) { + *ret = found; + found = (InstanceMetadata) INSTANCE_METADATA_NULL; + } + + return true; + } + } + + if (ret) + *ret = (InstanceMetadata) INSTANCE_METADATA_NULL; + + return false; +} + +int pattern_valid(const char *pattern) { + int r; + + r = pattern_split(pattern, NULL); + if (r == -EINVAL) + return false; + if (r < 0) + return r; + + return true; +} + +int pattern_format( + const char *pattern, + const InstanceMetadata *fields, + char **ret) { + + _cleanup_(pattern_element_free_allp) PatternElement *elements = NULL; + _cleanup_free_ char *j = NULL; + int r; + + assert(pattern); + assert(fields); + assert(ret); + + r = pattern_split(pattern, &elements); + if (r < 0) + return r; + + LIST_FOREACH(elements, e, elements) { + + switch (e->type) { + + case PATTERN_LITERAL: + if (!strextend(&j, e->literal)) + return -ENOMEM; + + break; + + case PATTERN_VERSION: + if (!fields->version) + return -ENXIO; + + if (!strextend(&j, fields->version)) + return -ENOMEM; + break; + + case PATTERN_PARTITION_UUID: { + char formatted[SD_ID128_STRING_MAX]; + + if (!fields->partition_uuid_set) + return -ENXIO; + + if (!strextend(&j, sd_id128_to_string(fields->partition_uuid, formatted))) + return -ENOMEM; + + break; + } + + case PATTERN_PARTITION_FLAGS: + if (!fields->partition_flags_set) + return -ENXIO; + + r = strextendf(&j, "%" PRIx64, fields->partition_flags); + if (r < 0) + return r; + + break; + + case PATTERN_MTIME: + if (fields->mtime == USEC_INFINITY) + return -ENXIO; + + r = strextendf(&j, "%" PRIu64, fields->mtime); + if (r < 0) + return r; + + break; + + case PATTERN_MODE: + if (fields->mode == MODE_INVALID) + return -ENXIO; + + r = strextendf(&j, "%03o", fields->mode); + if (r < 0) + return r; + + break; + + case PATTERN_SIZE: + if (fields->size == UINT64_MAX) + return -ENXIO; + + r = strextendf(&j, "%" PRIu64, fields->size); + if (r < 0) + return r; + break; + + case PATTERN_TRIES_DONE: + if (fields->tries_done == UINT64_MAX) + return -ENXIO; + + r = strextendf(&j, "%" PRIu64, fields->tries_done); + if (r < 0) + return r; + break; + + case PATTERN_TRIES_LEFT: + if (fields->tries_left == UINT64_MAX) + return -ENXIO; + + r = strextendf(&j, "%" PRIu64, fields->tries_left); + if (r < 0) + return r; + break; + + case PATTERN_NO_AUTO: + if (fields->no_auto < 0) + return -ENXIO; + + if (!strextend(&j, one_zero(fields->no_auto))) + return -ENOMEM; + + break; + + case PATTERN_READ_ONLY: + if (fields->read_only < 0) + return -ENXIO; + + if (!strextend(&j, one_zero(fields->read_only))) + return -ENOMEM; + + break; + + case PATTERN_GROWFS: + if (fields->growfs < 0) + return -ENXIO; + + if (!strextend(&j, one_zero(fields->growfs))) + return -ENOMEM; + + break; + + case PATTERN_SHA256SUM: { + _cleanup_free_ char *h = NULL; + + if (!fields->sha256sum_set) + return -ENXIO; + + h = hexmem(fields->sha256sum, sizeof(fields->sha256sum)); + if (!h) + return -ENOMEM; + + if (!strextend(&j, h)) + return -ENOMEM; + + break; + } + + default: + assert_not_reached(); + } + } + + *ret = TAKE_PTR(j); + return 0; +} diff --git a/src/sysupdate/sysupdate-pattern.h b/src/sysupdate/sysupdate-pattern.h new file mode 100644 index 000000000..1c60fa025 --- /dev/null +++ b/src/sysupdate/sysupdate-pattern.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +#include "sysupdate-instance.h" +#include "time-util.h" + +int pattern_match(const char *pattern, const char *s, InstanceMetadata *ret); +int pattern_match_many(char **patterns, const char *s, InstanceMetadata *ret); +int pattern_valid(const char *pattern); +int pattern_format(const char *pattern, const InstanceMetadata *fields, char **ret); diff --git a/src/sysupdate/sysupdate-resource.c b/src/sysupdate/sysupdate-resource.c new file mode 100644 index 000000000..3df34cf7f --- /dev/null +++ b/src/sysupdate/sysupdate-resource.c @@ -0,0 +1,633 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#include + +#include "alloc-util.h" +#include "blockdev-util.h" +#include "chase-symlinks.h" +#include "dirent-util.h" +#include "env-util.h" +#include "fd-util.h" +#include "fileio.h" +#include "glyph-util.h" +#include "gpt.h" +#include "hexdecoct.h" +#include "import-util.h" +#include "macro.h" +#include "process-util.h" +#include "sort-util.h" +#include "stat-util.h" +#include "string-table.h" +#include "sysupdate-cache.h" +#include "sysupdate-instance.h" +#include "sysupdate-pattern.h" +#include "sysupdate-resource.h" +#include "sysupdate.h" +#include "utf8.h" + +void resource_destroy(Resource *rr) { + assert(rr); + + free(rr->path); + strv_free(rr->patterns); + + for (size_t i = 0; i < rr->n_instances; i++) + instance_free(rr->instances[i]); + free(rr->instances); +} + +static int resource_add_instance( + Resource *rr, + const char *path, + const InstanceMetadata *f, + Instance **ret) { + + Instance *i; + int r; + + assert(rr); + assert(path); + assert(f); + assert(f->version); + + if (!GREEDY_REALLOC(rr->instances, rr->n_instances + 1)) + return log_oom(); + + r = instance_new(rr, path, f, &i); + if (r < 0) + return r; + + rr->instances[rr->n_instances++] = i; + + if (ret) + *ret = i; + + return 0; +} + +static int resource_load_from_directory( + Resource *rr, + mode_t m) { + + _cleanup_(closedirp) DIR *d = NULL; + int r; + + assert(rr); + assert(IN_SET(rr->type, RESOURCE_TAR, RESOURCE_REGULAR_FILE, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME)); + assert(IN_SET(m, S_IFREG, S_IFDIR)); + + d = opendir(rr->path); + if (!d) { + if (errno == ENOENT) { + log_debug("Directory %s does not exist, not loading any resources.", rr->path); + return 0; + } + + return log_error_errno(errno, "Failed to open directory '%s': %m", rr->path); + } + + for (;;) { + _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL; + _cleanup_free_ char *joined = NULL; + Instance *instance; + struct dirent *de; + struct stat st; + + errno = 0; + de = readdir_no_dot(d); + if (!de) { + if (errno != 0) + return log_error_errno(errno, "Failed to read directory '%s': %m", rr->path); + break; + } + + switch (de->d_type) { + + case DT_UNKNOWN: + break; + + case DT_DIR: + if (m != S_IFDIR) + continue; + + break; + + case DT_REG: + if (m != S_IFREG) + continue; + break; + + default: + continue; + } + + if (fstatat(dirfd(d), de->d_name, &st, AT_NO_AUTOMOUNT) < 0) { + if (errno == ENOENT) /* Gone by now? */ + continue; + + return log_error_errno(errno, "Failed to stat %s/%s: %m", rr->path, de->d_name); + } + + if ((st.st_mode & S_IFMT) != m) + continue; + + r = pattern_match_many(rr->patterns, de->d_name, &extracted_fields); + if (r < 0) + return log_error_errno(r, "Failed to match pattern: %m"); + if (r == 0) + continue; + + joined = path_join(rr->path, de->d_name); + if (!joined) + return log_oom(); + + r = resource_add_instance(rr, joined, &extracted_fields, &instance); + if (r < 0) + return r; + + /* Inherit these from the source, if not explicitly overwritten */ + if (instance->metadata.mtime == USEC_INFINITY) + instance->metadata.mtime = timespec_load(&st.st_mtim) ?: USEC_INFINITY; + + if (instance->metadata.mode == MODE_INVALID) + instance->metadata.mode = st.st_mode & 0775; /* mask out world-writability and suid and stuff, for safety */ + } + + return 0; +} + +static int resource_load_from_blockdev(Resource *rr) { + _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL; + _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL; + size_t n_partitions; + int r; + + assert(rr); + + c = fdisk_new_context(); + if (!c) + return log_oom(); + + r = fdisk_assign_device(c, rr->path, /* readonly= */ true); + if (r < 0) + return log_error_errno(r, "Failed to open device '%s': %m", rr->path); + + if (!fdisk_is_labeltype(c, FDISK_DISKLABEL_GPT)) + return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON), "Disk %s has no GPT disk label, not suitable.", rr->path); + + r = fdisk_get_partitions(c, &t); + if (r < 0) + return log_error_errno(r, "Failed to acquire partition table: %m"); + + n_partitions = fdisk_table_get_nents(t); + for (size_t i = 0; i < n_partitions; i++) { + _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL; + _cleanup_(partition_info_destroy) PartitionInfo pinfo = PARTITION_INFO_NULL; + Instance *instance; + + r = read_partition_info(c, t, i, &pinfo); + if (r < 0) + return r; + if (r == 0) /* not assigned */ + continue; + + /* Check if partition type matches */ + if (rr->partition_type_set && !sd_id128_equal(pinfo.type, rr->partition_type)) + continue; + + /* A label of "_empty" means "not used so far" for us */ + if (streq_ptr(pinfo.label, "_empty")) { + rr->n_empty++; + continue; + } + + r = pattern_match_many(rr->patterns, pinfo.label, &extracted_fields); + if (r < 0) + return log_error_errno(r, "Failed to match pattern: %m"); + if (r == 0) + continue; + + r = resource_add_instance(rr, pinfo.device, &extracted_fields, &instance); + if (r < 0) + return r; + + instance->partition_info = pinfo; + pinfo = (PartitionInfo) PARTITION_INFO_NULL; + + /* Inherit data from source if not configured explicitly */ + if (!instance->metadata.partition_uuid_set) { + instance->metadata.partition_uuid = instance->partition_info.uuid; + instance->metadata.partition_uuid_set = true; + } + + if (!instance->metadata.partition_flags_set) { + instance->metadata.partition_flags = instance->partition_info.flags; + instance->metadata.partition_flags_set = true; + } + + if (instance->metadata.read_only < 0) + instance->metadata.read_only = instance->partition_info.read_only; + } + + return 0; +} + +static int download_manifest( + const char *url, + bool verify_signature, + char **ret_buffer, + size_t *ret_size) { + + _cleanup_free_ char *buffer = NULL, *suffixed_url = NULL; + _cleanup_(close_pairp) int pfd[2] = { -1, -1 }; + _cleanup_fclose_ FILE *manifest = NULL; + size_t size = 0; + pid_t pid; + int r; + + assert(url); + assert(ret_buffer); + assert(ret_size); + + /* Download a SHA256SUMS file as manifest */ + + r = import_url_append_component(url, "SHA256SUMS", &suffixed_url); + if (r < 0) + return log_error_errno(r, "Failed to append SHA256SUMS to URL: %m"); + + if (pipe2(pfd, O_CLOEXEC) < 0) + return log_error_errno(errno, "Failed to allocate pipe: %m"); + + log_info("%s Acquiring manifest file %s…", special_glyph(SPECIAL_GLYPH_DOWNLOAD), suffixed_url); + + r = safe_fork("(sd-pull)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); + if (r < 0) + return r; + if (r == 0) { + /* Child */ + + const char *cmdline[] = { + "systemd-pull", + "raw", + "--direct", /* just download the specified URL, don't download anything else */ + "--verify", verify_signature ? "signature" : "no", /* verify the manifest file */ + suffixed_url, + "-", /* write to stdout */ + NULL + }; + + pfd[0] = safe_close(pfd[0]); + + r = rearrange_stdio(-1, pfd[1], STDERR_FILENO); + if (r < 0) { + log_error_errno(r, "Failed to rearrange stdin/stdout: %m"); + _exit(EXIT_FAILURE); + } + + (void) unsetenv("NOTIFY_SOCKET"); + execv(pull_binary_path(), (char *const*) cmdline); + log_error_errno(errno, "Failed to execute %s tool: %m", pull_binary_path()); + _exit(EXIT_FAILURE); + }; + + pfd[1] = safe_close(pfd[1]); + + /* We'll first load the entire manifest into memory before parsing it. That's because the + * systemd-pull tool can validate the download only after its completion, but still pass the data to + * us as it runs. We thus need to check the return value of the process *before* parsing, to be + * reasonably safe. */ + + manifest = fdopen(pfd[0], "r"); + if (!manifest) + return log_error_errno(errno, "Failed allocate FILE object for manifest file: %m"); + + TAKE_FD(pfd[0]); + + r = read_full_stream(manifest, &buffer, &size); + if (r < 0) + return log_error_errno(r, "Failed to read manifest file from child: %m"); + + manifest = safe_fclose(manifest); + + r = wait_for_terminate_and_check("(sd-pull)", pid, WAIT_LOG); + if (r < 0) + return r; + if (r != 0) + return -EPROTO; + + *ret_buffer = TAKE_PTR(buffer); + *ret_size = size; + + return 0; +} + +static int resource_load_from_web( + Resource *rr, + bool verify, + Hashmap **web_cache) { + + size_t manifest_size = 0, left = 0; + _cleanup_free_ char *buf = NULL; + const char *manifest, *p; + size_t line_nr = 1; + WebCacheItem *ci; + int r; + + assert(rr); + + ci = web_cache ? web_cache_get_item(*web_cache, rr->path, verify) : NULL; + if (ci) { + log_debug("Manifest web cache hit for %s.", rr->path); + + manifest = (char*) ci->data; + manifest_size = ci->size; + } else { + log_debug("Manifest web cache miss for %s.", rr->path); + + r = download_manifest(rr->path, verify, &buf, &manifest_size); + if (r < 0) + return r; + + manifest = buf; + } + + if (memchr(manifest, 0, manifest_size)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Manifest file has embedded NUL byte, refusing."); + if (!utf8_is_valid_n(manifest, manifest_size)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Manifest file is not valid UTF-8, refusing."); + + p = manifest; + left = manifest_size; + + while (left > 0) { + _cleanup_(instance_metadata_destroy) InstanceMetadata extracted_fields = INSTANCE_METADATA_NULL; + _cleanup_free_ char *fn = NULL; + _cleanup_free_ void *h = NULL; + Instance *instance; + const char *e; + size_t hlen; + + /* 64 character hash + separator + filename + newline */ + if (left < 67) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Corrupt manifest at line %zu, refusing.", line_nr); + + if (p[0] == '\\') + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "File names with escapes not supported in manifest at line %zu, refusing.", line_nr); + + r = unhexmem(p, 64, &h, &hlen); + if (r < 0) + return log_error_errno(r, "Failed to parse digest at manifest line %zu, refusing.", line_nr); + + p += 64, left -= 64; + + if (*p != ' ') + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing space separator at manifest line %zu, refusing.", line_nr); + p++, left--; + + if (!IN_SET(*p, '*', ' ')) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Missing binary/text input marker at manifest line %zu, refusing.", line_nr); + p++, left--; + + e = memchr(p, '\n', left); + if (!e) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Truncated manifest file at line %zu, refusing.", line_nr); + if (e == p) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty filename specified at manifest line %zu, refusing.", line_nr); + + fn = strndup(p, e - p); + if (!fn) + return log_oom(); + + if (!filename_is_valid(fn)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid filename specified at manifest line %zu, refusing.", line_nr); + if (string_has_cc(fn, NULL)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Filename contains control characters at manifest line %zu, refusing.", line_nr); + + r = pattern_match_many(rr->patterns, fn, &extracted_fields); + if (r < 0) + return log_error_errno(r, "Failed to match pattern: %m"); + if (r > 0) { + _cleanup_free_ char *path = NULL; + + r = import_url_append_component(rr->path, fn, &path); + if (r < 0) + return log_error_errno(r, "Failed to build instance URL: %m"); + + r = resource_add_instance(rr, path, &extracted_fields, &instance); + if (r < 0) + return r; + + assert(hlen == sizeof(instance->metadata.sha256sum)); + + if (instance->metadata.sha256sum_set) { + if (memcmp(instance->metadata.sha256sum, h, hlen) != 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "SHA256 sum parsed from filename and manifest don't match at line %zu, refusing.", line_nr); + } else { + memcpy(instance->metadata.sha256sum, h, hlen); + instance->metadata.sha256sum_set = true; + } + } + + left -= (e - p) + 1; + p = e + 1; + + line_nr++; + } + + if (!ci && web_cache) { + r = web_cache_add_item(web_cache, rr->path, verify, manifest, manifest_size); + if (r < 0) + log_debug_errno(r, "Failed to add manifest '%s' to cache, ignoring: %m", rr->path); + else + log_debug("Added manifest '%s' to cache.", rr->path); + } + + return 0; +} + +static int instance_cmp(Instance *const*a, Instance *const*b) { + int r; + + assert(a); + assert(b); + assert(*a); + assert(*b); + assert((*a)->metadata.version); + assert((*b)->metadata.version); + + /* Newest version at the beginning */ + r = strverscmp_improved((*a)->metadata.version, (*b)->metadata.version); + if (r != 0) + return -r; + + /* Instances don't have to be uniquely named (uniqueness on partition tables is not enforced at all, + * and since we allow multiple matching patterns not even in directories they are unique). Hence + * let's order by path as secondary ordering key. */ + return path_compare((*a)->path, (*b)->path); +} + +int resource_load_instances(Resource *rr, bool verify, Hashmap **web_cache) { + int r; + + assert(rr); + + switch (rr->type) { + + case RESOURCE_TAR: + case RESOURCE_REGULAR_FILE: + r = resource_load_from_directory(rr, S_IFREG); + break; + + case RESOURCE_DIRECTORY: + case RESOURCE_SUBVOLUME: + r = resource_load_from_directory(rr, S_IFDIR); + break; + + case RESOURCE_PARTITION: + r = resource_load_from_blockdev(rr); + break; + + case RESOURCE_URL_FILE: + case RESOURCE_URL_TAR: + r = resource_load_from_web(rr, verify, web_cache); + break; + + default: + assert_not_reached(); + } + if (r < 0) + return r; + + typesafe_qsort(rr->instances, rr->n_instances, instance_cmp); + return 0; +} + +Instance* resource_find_instance(Resource *rr, const char *version) { + Instance key = { + .metadata.version = (char*) version, + }, *k = &key; + + return typesafe_bsearch(&k, rr->instances, rr->n_instances, instance_cmp); +} + +int resource_resolve_path( + Resource *rr, + const char *root, + const char *node) { + + _cleanup_free_ char *p = NULL; + dev_t d; + int r; + + assert(rr); + + if (rr->path_auto) { + + /* NB: we don't actually check the backing device of the root fs "/", but of "/usr", in order + * to support environments where the root fs is a tmpfs, and the OS itself placed exclusively + * in /usr/. */ + + if (rr->type != RESOURCE_PARTITION) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Automatic root path discovery only supported for partition resources."); + + if (node) { /* If --image= is specified, directly use the loopback device */ + r = free_and_strdup_warn(&rr->path, node); + if (r < 0) + return r; + + return 0; + } + + if (root) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), + "Block device is not allowed when using --root= mode."); + + r = get_block_device_harder("/usr/", &d); + + } else if (rr->type == RESOURCE_PARTITION) { + _cleanup_close_ int fd = -1, real_fd = -1; + _cleanup_free_ char *resolved = NULL; + struct stat st; + + r = chase_symlinks(rr->path, root, CHASE_PREFIX_ROOT, &resolved, &fd); + if (r < 0) + return log_error_errno(r, "Failed to resolve '%s': %m", rr->path); + + if (fstat(fd, &st) < 0) + return log_error_errno(errno, "Failed to stat '%s': %m", resolved); + + if (S_ISBLK(st.st_mode) && root) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), "When using --root= or --image= access to device nodes is prohibited."); + + if (S_ISREG(st.st_mode) || S_ISBLK(st.st_mode)) { + /* Not a directory, hence no need to find backing block device for the path */ + free_and_replace(rr->path, resolved); + return 0; + } + + if (!S_ISDIR(st.st_mode)) + return log_error_errno(SYNTHETIC_ERRNO(ENOTDIR), "Target path '%s' does not refer to regular file, directory or block device, refusing.", rr->path); + + if (node) { /* If --image= is specified all file systems are backed by the same loopback device, hence shortcut things. */ + r = free_and_strdup_warn(&rr->path, node); + if (r < 0) + return r; + + return 0; + } + + real_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + if (real_fd < 0) + return log_error_errno(real_fd, "Failed to convert O_PATH file descriptor for %s to regular file descriptor: %m", rr->path); + + r = get_block_device_harder_fd(fd, &d); + + } else if (RESOURCE_IS_FILESYSTEM(rr->type) && root) { + _cleanup_free_ char *resolved = NULL; + + r = chase_symlinks(rr->path, root, CHASE_PREFIX_ROOT, &resolved, NULL); + if (r < 0) + return log_error_errno(r, "Failed to resolve '%s': %m", rr->path); + + free_and_replace(rr->path, resolved); + return 0; + } else + return 0; /* Otherwise assume there's nothing to resolve */ + + if (r < 0) + return log_error_errno(r, "Failed to determine block device of file system: %m"); + + r = block_get_whole_disk(d, &d); + if (r < 0) + return log_error_errno(r, "Failed to find whole disk device for partition backing file system: %m"); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "File system is not placed on a partition block device, cannot determine whole block device backing root file system."); + + r = device_path_make_canonical(S_IFBLK, d, &p); + if (r < 0) + return r; + + if (rr->path) + log_info("Automatically discovered block device '%s' from '%s'.", p, rr->path); + else + log_info("Automatically discovered root block device '%s'.", p); + + free_and_replace(rr->path, p); + return 1; +} + +static const char *resource_type_table[_RESOURCE_TYPE_MAX] = { + [RESOURCE_URL_FILE] = "url-file", + [RESOURCE_URL_TAR] = "url-tar", + [RESOURCE_TAR] = "tar", + [RESOURCE_PARTITION] = "partition", + [RESOURCE_REGULAR_FILE] = "regular-file", + [RESOURCE_DIRECTORY] = "directory", + [RESOURCE_SUBVOLUME] = "subvolume", +}; + +DEFINE_STRING_TABLE_LOOKUP(resource_type, ResourceType); diff --git a/src/sysupdate/sysupdate-resource.h b/src/sysupdate/sysupdate-resource.h new file mode 100644 index 000000000..86be0d338 --- /dev/null +++ b/src/sysupdate/sysupdate-resource.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include +#include + +#include "sd-id128.h" + +#include "hashmap.h" +#include "macro.h" + +/* Forward declare this type so that the headers below can use it */ +typedef struct Resource Resource; + +#include "sysupdate-instance.h" + +typedef enum ResourceType { + RESOURCE_URL_FILE, + RESOURCE_URL_TAR, + RESOURCE_TAR, + RESOURCE_PARTITION, + RESOURCE_REGULAR_FILE, + RESOURCE_DIRECTORY, + RESOURCE_SUBVOLUME, + _RESOURCE_TYPE_MAX, + _RESOURCE_TYPE_INVALID = -EINVAL, +} ResourceType; + +static inline bool RESOURCE_IS_SOURCE(ResourceType t) { + return IN_SET(t, + RESOURCE_URL_FILE, + RESOURCE_URL_TAR, + RESOURCE_TAR, + RESOURCE_REGULAR_FILE, + RESOURCE_DIRECTORY, + RESOURCE_SUBVOLUME); +} + +static inline bool RESOURCE_IS_TARGET(ResourceType t) { + return IN_SET(t, + RESOURCE_PARTITION, + RESOURCE_REGULAR_FILE, + RESOURCE_DIRECTORY, + RESOURCE_SUBVOLUME); +} + +/* Returns true for all resources that deal with file system objects, i.e. where we operate on top of the + * file system layer, instead of below. */ +static inline bool RESOURCE_IS_FILESYSTEM(ResourceType t) { + return IN_SET(t, + RESOURCE_TAR, + RESOURCE_REGULAR_FILE, + RESOURCE_DIRECTORY, + RESOURCE_SUBVOLUME); +} + +static inline bool RESOURCE_IS_TAR(ResourceType t) { + return IN_SET(t, + RESOURCE_TAR, + RESOURCE_URL_TAR); +} + +static inline bool RESOURCE_IS_URL(ResourceType t) { + return IN_SET(t, + RESOURCE_URL_TAR, + RESOURCE_URL_FILE); +} + +struct Resource { + ResourceType type; + + /* Where to look for instances, and what to match precisely */ + char *path; + bool path_auto; /* automatically find root path (only available if target resource, not source resource) */ + char **patterns; + sd_id128_t partition_type; + bool partition_type_set; + + /* All instances of this resource we found */ + Instance **instances; + size_t n_instances; + + /* If this is a partition resource (RESOURCE_PARTITION), then how many partition slots are currently unassigned, that we can use */ + size_t n_empty; +}; + +void resource_destroy(Resource *rr); + +int resource_load_instances(Resource *rr, bool verify, Hashmap **web_cache); + +Instance* resource_find_instance(Resource *rr, const char *version); + +int resource_resolve_path(Resource *rr, const char *root, const char *node); + +ResourceType resource_type_from_string(const char *s) _pure_; +const char *resource_type_to_string(ResourceType t) _const_; diff --git a/src/sysupdate/sysupdate-transfer.c b/src/sysupdate/sysupdate-transfer.c new file mode 100644 index 000000000..a9fceed60 --- /dev/null +++ b/src/sysupdate/sysupdate-transfer.c @@ -0,0 +1,1247 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "sd-id128.h" + +#include "alloc-util.h" +#include "blockdev-util.h" +#include "chase-symlinks.h" +#include "conf-parser.h" +#include "dirent-util.h" +#include "fd-util.h" +#include "glyph-util.h" +#include "gpt.h" +#include "hexdecoct.h" +#include "install-file.h" +#include "parse-util.h" +#include "path-util.h" +#include "process-util.h" +#include "rm-rf.h" +#include "specifier.h" +#include "stat-util.h" +#include "stdio-util.h" +#include "strv.h" +#include "sync-util.h" +#include "sysupdate-pattern.h" +#include "sysupdate-resource.h" +#include "sysupdate-transfer.h" +#include "sysupdate-util.h" +#include "sysupdate.h" +#include "tmpfile-util.h" +#include "web-util.h" + +/* Default value for InstancesMax= for fs object targets */ +#define DEFAULT_FILE_INSTANCES_MAX 3 + +Transfer *transfer_free(Transfer *t) { + if (!t) + return NULL; + + t->temporary_path = rm_rf_subvolume_and_free(t->temporary_path); + + free(t->definition_path); + free(t->min_version); + strv_free(t->protected_versions); + free(t->current_symlink); + free(t->final_path); + + partition_info_destroy(&t->partition_info); + + resource_destroy(&t->source); + resource_destroy(&t->target); + + return mfree(t); +} + +Transfer *transfer_new(void) { + Transfer *t; + + t = new(Transfer, 1); + if (!t) + return NULL; + + *t = (Transfer) { + .source.type = _RESOURCE_TYPE_INVALID, + .target.type = _RESOURCE_TYPE_INVALID, + .remove_temporary = true, + .mode = MODE_INVALID, + .tries_left = UINT64_MAX, + .tries_done = UINT64_MAX, + .verify = true, + + /* the three flags, as configured by the user */ + .no_auto = -1, + .read_only = -1, + .growfs = -1, + + /* the read only flag, as ultimately determined */ + .install_read_only = -1, + + .partition_info = PARTITION_INFO_NULL, + }; + + return t; +} + +static const Specifier specifier_table[] = { + COMMON_SYSTEM_SPECIFIERS, + COMMON_TMP_SPECIFIERS, + {} +}; + +static int config_parse_protect_version( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_free_ char *resolved = NULL; + char ***protected_versions = data; + int r; + + assert(rvalue); + assert(data); + + r = specifier_printf(rvalue, NAME_MAX, specifier_table, arg_root, NULL, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to expand specifiers in ProtectVersion=, ignoring: %s", rvalue); + return 0; + } + + if (!version_is_valid(resolved)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "ProtectVersion= string is not valid, ignoring: %s", resolved); + return 0; + } + + r = strv_extend(protected_versions, resolved); + if (r < 0) + return log_oom(); + + return 0; +} + +static int config_parse_min_version( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_free_ char *resolved = NULL; + char **version = data; + int r; + + assert(rvalue); + assert(data); + + r = specifier_printf(rvalue, NAME_MAX, specifier_table, arg_root, NULL, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to expand specifiers in MinVersion=, ignoring: %s", rvalue); + return 0; + } + + if (!version_is_valid(rvalue)) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "MinVersion= string is not valid, ignoring: %s", resolved); + return 0; + } + + return free_and_replace(*version, resolved); +} + +static int config_parse_current_symlink( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_free_ char *resolved = NULL; + char **current_symlink = data; + int r; + + assert(rvalue); + assert(data); + + r = specifier_printf(rvalue, NAME_MAX, specifier_table, arg_root, NULL, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to expand specifiers in CurrentSymlink=, ignoring: %s", rvalue); + return 0; + } + + r = path_simplify_and_warn(resolved, 0, unit, filename, line, lvalue); + if (r < 0) + return 0; + + return free_and_replace(*current_symlink, resolved); +} + +static int config_parse_instances_max( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint64_t *instances_max = data, i; + int r; + + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *instances_max = 0; /* Revert to default logic, see transfer_read_definition() */ + return 0; + } + + r = safe_atou64(rvalue, &i); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to parse InstancesMax= value, ignoring: %s", rvalue); + return 0; + } + + if (i < 2) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "InstancesMax= value must be at least 2, bumping: %s", rvalue); + *instances_max = 2; + } else + *instances_max = i; + + return 0; +} + +static int config_parse_resource_pattern( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char ***patterns = data; + int r; + + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + *patterns = strv_free(*patterns); + return 0; + } + + for (;;) { + _cleanup_free_ char *word = NULL, *resolved = NULL; + + r = extract_first_word(&rvalue, &word, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to extract first pattern from MatchPattern=, ignoring: %s", rvalue); + return 0; + } + if (r == 0) + break; + + r = specifier_printf(word, NAME_MAX, specifier_table, arg_root, NULL, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to expand specifiers in MatchPattern=, ignoring: %s", rvalue); + return 0; + } + + if (!pattern_valid(resolved)) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), + "MatchPattern= string is not valid, refusing: %s", resolved); + + r = strv_consume(patterns, TAKE_PTR(resolved)); + if (r < 0) + return log_oom(); + } + + strv_uniq(*patterns); + return 0; +} + +static int config_parse_resource_path( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_free_ char *resolved = NULL; + Resource *rr = data; + int r; + + assert(rvalue); + assert(data); + + if (streq(rvalue, "auto")) { + rr->path_auto = true; + rr->path = mfree(rr->path); + return 0; + } + + r = specifier_printf(rvalue, PATH_MAX-1, specifier_table, arg_root, NULL, &resolved); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed to expand specifiers in Path=, ignoring: %s", rvalue); + return 0; + } + + /* Note that we don't validate the path as being absolute or normalized. We'll do that in + * transfer_read_definition() as we might not know yet whether Path refers to an URL or a file system + * path. */ + + rr->path_auto = false; + return free_and_replace(rr->path, resolved); +} + +static DEFINE_CONFIG_PARSE_ENUM(config_parse_resource_type, resource_type, ResourceType, "Invalid resource type"); + +static int config_parse_resource_ptype( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Resource *rr = data; + int r; + + assert(rvalue); + assert(data); + + r = gpt_partition_type_uuid_from_string(rvalue, &rr->partition_type); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed parse partition type, ignoring: %s", rvalue); + return 0; + } + + rr->partition_type_set = true; + return 0; +} + +static int config_parse_partition_uuid( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Transfer *t = data; + int r; + + assert(rvalue); + assert(data); + + r = sd_id128_from_string(rvalue, &t->partition_uuid); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed parse partition UUID, ignoring: %s", rvalue); + return 0; + } + + t->partition_uuid_set = true; + return 0; +} + +static int config_parse_partition_flags( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Transfer *t = data; + int r; + + assert(rvalue); + assert(data); + + r = safe_atou64(rvalue, &t->partition_flags); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "Failed parse partition flags, ignoring: %s", rvalue); + return 0; + } + + t->partition_flags_set = true; + return 0; +} + +int transfer_read_definition(Transfer *t, const char *path) { + int r; + + assert(t); + assert(path); + + ConfigTableItem table[] = { + { "Transfer", "MinVersion", config_parse_min_version, 0, &t->min_version }, + { "Transfer", "ProtectVersion", config_parse_protect_version, 0, &t->protected_versions }, + { "Transfer", "Verify", config_parse_bool, 0, &t->verify }, + { "Source", "Type", config_parse_resource_type, 0, &t->source.type }, + { "Source", "Path", config_parse_resource_path, 0, &t->source }, + { "Source", "MatchPattern", config_parse_resource_pattern, 0, &t->source.patterns }, + { "Target", "Type", config_parse_resource_type, 0, &t->target.type }, + { "Target", "Path", config_parse_resource_path, 0, &t->target }, + { "Target", "MatchPattern", config_parse_resource_pattern, 0, &t->target.patterns }, + { "Target", "MatchPartitionType", config_parse_resource_ptype, 0, &t->target }, + { "Target", "PartitionUUID", config_parse_partition_uuid, 0, t }, + { "Target", "PartitionFlags", config_parse_partition_flags, 0, t }, + { "Target", "PartitionNoAuto", config_parse_tristate, 0, &t->no_auto }, + { "Target", "PartitionGrowFileSystem", config_parse_tristate, 0, &t->growfs }, + { "Target", "ReadOnly", config_parse_tristate, 0, &t->read_only }, + { "Target", "Mode", config_parse_mode, 0, &t->mode }, + { "Target", "TriesLeft", config_parse_uint64, 0, &t->tries_left }, + { "Target", "TriesDone", config_parse_uint64, 0, &t->tries_done }, + { "Target", "InstancesMax", config_parse_instances_max, 0, &t->instances_max }, + { "Target", "RemoveTemporary", config_parse_bool, 0, &t->remove_temporary }, + { "Target", "CurrentSymlink", config_parse_current_symlink, 0, &t->current_symlink }, + {} + }; + + r = config_parse(NULL, path, NULL, + "Transfer\0" + "Source\0" + "Target\0", + config_item_table_lookup, table, + CONFIG_PARSE_WARN, + t, + NULL); + if (r < 0) + return r; + + if (!RESOURCE_IS_SOURCE(t->source.type)) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Source Type= must be one of url-file, url-tar, tar, regular-file, directory, subvolume."); + + if (t->target.type < 0) { + switch (t->source.type) { + + case RESOURCE_URL_FILE: + case RESOURCE_REGULAR_FILE: + t->target.type = + t->target.path && path_startswith(t->target.path, "/dev/") ? + RESOURCE_PARTITION : RESOURCE_REGULAR_FILE; + break; + + case RESOURCE_URL_TAR: + case RESOURCE_TAR: + case RESOURCE_DIRECTORY: + t->target.type = RESOURCE_DIRECTORY; + break; + + case RESOURCE_SUBVOLUME: + t->target.type = RESOURCE_SUBVOLUME; + break; + + default: + assert_not_reached(); + } + } + + if (!RESOURCE_IS_TARGET(t->target.type)) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Target Type= must be one of partition, regular-file, directory, subvolume."); + + if ((IN_SET(t->source.type, RESOURCE_URL_FILE, RESOURCE_PARTITION, RESOURCE_REGULAR_FILE) && + !IN_SET(t->target.type, RESOURCE_PARTITION, RESOURCE_REGULAR_FILE)) || + (IN_SET(t->source.type, RESOURCE_URL_TAR, RESOURCE_TAR, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME) && + !IN_SET(t->target.type, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME))) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Target type '%s' is incompatible with source type '%s', refusing.", + resource_type_to_string(t->source.type), resource_type_to_string(t->target.type)); + + if (!t->source.path && !t->source.path_auto) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Source specification lacks Path=."); + + if (t->source.path) { + if (RESOURCE_IS_FILESYSTEM(t->source.type) || t->source.type == RESOURCE_PARTITION) + if (!path_is_absolute(t->source.path) || !path_is_normalized(t->source.path)) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Source path is not a normalized, absolute path: %s", t->source.path); + + /* We unofficially support file:// in addition to http:// and https:// for url + * sources. That's mostly for testing, since it relieves us from having to set up a HTTP + * server, and CURL abstracts this away from us thankfully. */ + if (RESOURCE_IS_URL(t->source.type)) + if (!http_url_is_valid(t->source.path) && !file_url_is_valid(t->source.path)) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Source path is not a valid HTTP or HTTPS URL: %s", t->source.path); + } + + if (strv_isempty(t->source.patterns)) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Source specification lacks MatchPattern=."); + + if (!t->target.path && !t->target.path_auto) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Target specification lacks Path= field."); + + if (t->target.path && + (!path_is_absolute(t->target.path) || !path_is_normalized(t->target.path))) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Target path is not a normalized, absolute path: %s", t->target.path); + + if (strv_isempty(t->target.patterns)) { + strv_free(t->target.patterns); + t->target.patterns = strv_copy(t->source.patterns); + if (!t->target.patterns) + return log_oom(); + } + + if (t->current_symlink && !RESOURCE_IS_FILESYSTEM(t->target.type) && !path_is_absolute(t->current_symlink)) + return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), + "Current symlink must be absolute path if target is partition: %s", t->current_symlink); + + /* When no instance limit is set, use all available partition slots in case of partitions, or 3 in case of fs objects */ + if (t->instances_max == 0) + t->instances_max = t->target.type == RESOURCE_PARTITION ? UINT64_MAX : DEFAULT_FILE_INSTANCES_MAX; + + return 0; +} + +int transfer_resolve_paths( + Transfer *t, + const char *root, + const char *node) { + + int r; + + /* If Path=auto is used in [Source] or [Target] sections, let's automatically detect the path of the + * block device to use. Moreover, if this path points to a directory but we need a block device, + * automatically determine the backing block device, so that users can reference block devices by + * mount point. */ + + assert(t); + + r = resource_resolve_path(&t->source, root, node); + if (r < 0) + return r; + + r = resource_resolve_path(&t->target, root, node); + if (r < 0) + return r; + + return 0; +} + +static void transfer_remove_temporary(Transfer *t) { + _cleanup_(closedirp) DIR *d = NULL; + int r; + + assert(t); + + if (!t->remove_temporary) + return; + + if (!IN_SET(t->target.type, RESOURCE_REGULAR_FILE, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME)) + return; + + /* Removes all temporary files/dirs from previous runs in the target directory, i.e. all those starting with '.#' */ + + d = opendir(t->target.path); + if (!d) { + if (errno == ENOENT) + return; + + log_debug_errno(errno, "Failed to open target directory '%s', ignoring: %m", t->target.path); + return; + } + + for (;;) { + struct dirent *de; + + errno = 0; + de = readdir_no_dot(d); + if (!de) { + if (errno != 0) + log_debug_errno(errno, "Failed to read target directory '%s', ignoring: %m", t->target.path); + break; + } + + if (!startswith(de->d_name, ".#")) + continue; + + r = rm_rf_child(dirfd(d), de->d_name, REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_CHMOD); + if (r == -ENOENT) + continue; + if (r < 0) { + log_warning_errno(r, "Failed to remove temporary resource instance '%s/%s', ignoring: %m", t->target.path, de->d_name); + continue; + } + + log_debug("Removed temporary resource instance '%s/%s'.", t->target.path, de->d_name); + } +} + +int transfer_vacuum( + Transfer *t, + uint64_t space, + const char *extra_protected_version) { + + uint64_t instances_max, limit; + int r, count = 0; + + assert(t); + + transfer_remove_temporary(t); + + /* First, calculate how many instances to keep, based on the instance limit — but keep at least one */ + + instances_max = arg_instances_max != UINT64_MAX ? arg_instances_max : t->instances_max; + assert(instances_max >= 1); + if (instances_max == UINT64_MAX) /* Keep infinite instances? */ + limit = UINT64_MAX; + else if (space > instances_max) + return log_error_errno(SYNTHETIC_ERRNO(ENOSPC), + "Asked to delete more instances than total maximum allowed number of instances, refusing."); + else if (space == instances_max) + return log_error_errno(SYNTHETIC_ERRNO(ENOSPC), + "Asked to delete all possible instances, can't allow that. One instance must always remain."); + else + limit = instances_max - space; + + if (t->target.type == RESOURCE_PARTITION) { + uint64_t rm, remain; + + /* If we are looking at a partition table, we also have to take into account how many + * partition slots of the right type are available */ + + if (t->target.n_empty + t->target.n_instances < 2) + return log_error_errno(SYNTHETIC_ERRNO(ENOSPC), + "Partition table has less than two partition slots of the right type " SD_ID128_UUID_FORMAT_STR " (%s), refusing.", + SD_ID128_FORMAT_VAL(t->target.partition_type), + gpt_partition_type_uuid_to_string(t->target.partition_type)); + if (space > t->target.n_empty + t->target.n_instances) + return log_error_errno(SYNTHETIC_ERRNO(ENOSPC), + "Partition table does not have enough partition slots of right type " SD_ID128_UUID_FORMAT_STR " (%s) for operation.", + SD_ID128_FORMAT_VAL(t->target.partition_type), + gpt_partition_type_uuid_to_string(t->target.partition_type)); + if (space == t->target.n_empty + t->target.n_instances) + return log_error_errno(SYNTHETIC_ERRNO(ENOSPC), + "Asked to empty all partition table slots of the right type " SD_ID128_UUID_FORMAT_STR " (%s), can't allow that. One instance must always remain.", + SD_ID128_FORMAT_VAL(t->target.partition_type), + gpt_partition_type_uuid_to_string(t->target.partition_type)); + + rm = LESS_BY(space, t->target.n_empty); + remain = LESS_BY(t->target.n_instances, rm); + limit = MIN(limit, remain); + } + + while (t->target.n_instances > limit) { + Instance *oldest; + size_t p = t->target.n_instances - 1; + + for (;;) { + oldest = t->target.instances[p]; + assert(oldest); + + /* If this is listed among the protected versions, then let's not remove it */ + if (!strv_contains(t->protected_versions, oldest->metadata.version) && + (!extra_protected_version || !streq(extra_protected_version, oldest->metadata.version))) + break; + + log_debug("Version '%s' is protected, not removing.", oldest->metadata.version); + if (p == 0) { + oldest = NULL; + break; + } + + p--; + } + + if (!oldest) /* Nothing more to remove */ + break; + + assert(oldest->resource); + + log_info("%s Removing old '%s' (%s).", special_glyph(SPECIAL_GLYPH_RECYCLING), oldest->path, resource_type_to_string(oldest->resource->type)); + + switch (t->target.type) { + + case RESOURCE_REGULAR_FILE: + case RESOURCE_DIRECTORY: + case RESOURCE_SUBVOLUME: + r = rm_rf(oldest->path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME|REMOVE_MISSING_OK|REMOVE_CHMOD); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to make room, deleting '%s' failed: %m", oldest->path); + + break; + + case RESOURCE_PARTITION: { + PartitionInfo pinfo = oldest->partition_info; + + /* label "_empty" means "no contents" for our purposes */ + pinfo.label = (char*) "_empty"; + + r = patch_partition(t->target.path, &pinfo, PARTITION_LABEL); + if (r < 0) + return r; + + t->target.n_empty++; + break; + } + + default: + assert_not_reached(); + break; + } + + instance_free(oldest); + memmove(t->target.instances + p, t->target.instances + p + 1, (t->target.n_instances - p - 1) * sizeof(Instance*)); + t->target.n_instances--; + + count++; + } + + return count; +} + +static void compile_pattern_fields( + const Transfer *t, + const Instance *i, + InstanceMetadata *ret) { + + assert(t); + assert(i); + assert(ret); + + *ret = (InstanceMetadata) { + .version = i->metadata.version, + + /* We generally prefer explicitly configured values for the transfer over those automatically + * derived from the source instance. Also, if the source is a tar archive, then let's not + * patch mtime/mode and use the one embedded in the tar file */ + .partition_uuid = t->partition_uuid_set ? t->partition_uuid : i->metadata.partition_uuid, + .partition_uuid_set = t->partition_uuid_set || i->metadata.partition_uuid_set, + .partition_flags = t->partition_flags_set ? t->partition_flags : i->metadata.partition_flags, + .partition_flags_set = t->partition_flags_set || i->metadata.partition_flags_set, + .mtime = RESOURCE_IS_TAR(i->resource->type) ? USEC_INFINITY : i->metadata.mtime, + .mode = t->mode != MODE_INVALID ? t->mode : (RESOURCE_IS_TAR(i->resource->type) ? MODE_INVALID : i->metadata.mode), + .size = i->metadata.size, + .tries_done = t->tries_done != UINT64_MAX ? t->tries_done : + i->metadata.tries_done != UINT64_MAX ? i->metadata.tries_done : 0, + .tries_left = t->tries_left != UINT64_MAX ? t->tries_left : + i->metadata.tries_left != UINT64_MAX ? i->metadata.tries_left : 3, + .no_auto = t->no_auto >= 0 ? t->no_auto : i->metadata.no_auto, + .read_only = t->read_only >= 0 ? t->read_only : i->metadata.read_only, + .growfs = t->growfs >= 0 ? t->growfs : i->metadata.growfs, + .sha256sum_set = i->metadata.sha256sum_set, + }; + + memcpy(ret->sha256sum, i->metadata.sha256sum, sizeof(ret->sha256sum)); +} + +static int run_helper( + const char *name, + const char *path, + const char * const cmdline[]) { + + int r; + + assert(name); + assert(path); + assert(cmdline); + + r = safe_fork(name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + if (r < 0) + return r; + if (r == 0) { + /* Child */ + + (void) unsetenv("NOTIFY_SOCKET"); + execv(path, (char *const*) cmdline); + log_error_errno(errno, "Failed to execute %s tool: %m", path); + _exit(EXIT_FAILURE); + } + + return 0; +} + +int transfer_acquire_instance(Transfer *t, Instance *i) { + _cleanup_free_ char *formatted_pattern = NULL, *digest = NULL; + char offset[DECIMAL_STR_MAX(uint64_t)+1], max_size[DECIMAL_STR_MAX(uint64_t)+1]; + const char *where = NULL; + InstanceMetadata f; + Instance *existing; + int r; + + assert(t); + assert(i); + assert(i->resource); + assert(t == container_of(i->resource, Transfer, source)); + + /* Does this instance already exist in the target? Then we don't need to acquire anything */ + existing = resource_find_instance(&t->target, i->metadata.version); + if (existing) { + log_info("No need to acquire '%s', already installed.", i->path); + return 0; + } + + assert(!t->final_path); + assert(!t->temporary_path); + assert(!strv_isempty(t->target.patterns)); + + /* Format the target name using the first pattern specified */ + compile_pattern_fields(t, i, &f); + r = pattern_format(t->target.patterns[0], &f, &formatted_pattern); + if (r < 0) + return log_error_errno(r, "Failed to format target pattern: %m"); + + if (RESOURCE_IS_FILESYSTEM(t->target.type)) { + + if (!filename_is_valid(formatted_pattern)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as file name, refusing: %s", formatted_pattern); + + t->final_path = path_join(t->target.path, formatted_pattern); + if (!t->final_path) + return log_oom(); + + r = tempfn_random(t->final_path, "sysupdate", &t->temporary_path); + if (r < 0) + return log_error_errno(r, "Failed to generate temporary target path: %m"); + + where = t->final_path; + } + + if (t->target.type == RESOURCE_PARTITION) { + r = gpt_partition_label_valid(formatted_pattern); + if (r < 0) + return log_error_errno(r, "Failed to determine if formatted pattern is suitable as GPT partition label: %s", formatted_pattern); + if (!r) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Formatted pattern is not suitable as GPT partition label, refusing: %s", formatted_pattern); + + r = find_suitable_partition( + t->target.path, + i->metadata.size, + t->target.partition_type_set ? &t->target.partition_type : NULL, + &t->partition_info); + if (r < 0) + return r; + + xsprintf(offset, "%" PRIu64, t->partition_info.start); + xsprintf(max_size, "%" PRIu64, t->partition_info.size); + + where = t->partition_info.device; + } + + assert(where); + + log_info("%s Acquiring %s %s %s...", special_glyph(SPECIAL_GLYPH_DOWNLOAD), i->path, special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), where); + + if (RESOURCE_IS_URL(i->resource->type)) { + /* For URL sources we require the SHA256 sum to be known so that we can validate the + * download. */ + + if (!i->metadata.sha256sum_set) + return log_error_errno(r, "SHA256 checksum not known for download '%s', refusing.", i->path); + + digest = hexmem(i->metadata.sha256sum, sizeof(i->metadata.sha256sum)); + if (!digest) + return log_oom(); + } + + switch (i->resource->type) { /* Source */ + + case RESOURCE_REGULAR_FILE: + + switch (t->target.type) { /* Target */ + + case RESOURCE_REGULAR_FILE: + + /* regular file → regular file (why fork off systemd-import for such a simple file + * copy case? implicit decompression mostly, and thus also sandboxing. Also, the + * importer has some tricks up its sleeve, such as sparse file generation, which we + * want to take benefit of, too.) */ + + r = run_helper("(sd-import-raw)", + import_binary_path(), + (const char* const[]) { + "systemd-import", + "raw", + "--direct", /* just copy/unpack the specified file, don't do anything else */ + arg_sync ? "--sync=yes" : "--sync=no", + i->path, + t->temporary_path, + NULL + }); + break; + + case RESOURCE_PARTITION: + + /* regular file → partition */ + + r = run_helper("(sd-import-raw)", + import_binary_path(), + (const char* const[]) { + "systemd-import", + "raw", + "--direct", /* just copy/unpack the specified file, don't do anything else */ + "--offset", offset, + "--size-max", max_size, + arg_sync ? "--sync=yes" : "--sync=no", + i->path, + t->target.path, + NULL + }); + break; + + default: + assert_not_reached(); + } + + break; + + case RESOURCE_DIRECTORY: + case RESOURCE_SUBVOLUME: + assert(IN_SET(t->target.type, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME)); + + /* directory/subvolume → directory/subvolume */ + + r = run_helper("(sd-import-fs)", + import_fs_binary_path(), + (const char* const[]) { + "systemd-import-fs", + "run", + "--direct", /* just untar the specified file, don't do anything else */ + arg_sync ? "--sync=yes" : "--sync=no", + t->target.type == RESOURCE_SUBVOLUME ? "--btrfs-subvol=yes" : "--btrfs-subvol=no", + i->path, + t->temporary_path, + NULL + }); + break; + + case RESOURCE_TAR: + assert(IN_SET(t->target.type, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME)); + + /* tar → directory/subvolume */ + + r = run_helper("(sd-import-tar)", + import_binary_path(), + (const char* const[]) { + "systemd-import", + "tar", + "--direct", /* just untar the specified file, don't do anything else */ + arg_sync ? "--sync=yes" : "--sync=no", + t->target.type == RESOURCE_SUBVOLUME ? "--btrfs-subvol=yes" : "--btrfs-subvol=no", + i->path, + t->temporary_path, + NULL + }); + break; + + case RESOURCE_URL_FILE: + + switch (t->target.type) { + + case RESOURCE_REGULAR_FILE: + + /* url file → regular file */ + + r = run_helper("(sd-pull-raw)", + pull_binary_path(), + (const char* const[]) { + "systemd-pull", + "raw", + "--direct", /* just download the specified URL, don't download anything else */ + "--verify", digest, /* validate by explicit SHA256 sum */ + arg_sync ? "--sync=yes" : "--sync=no", + i->path, + t->temporary_path, + NULL + }); + break; + + case RESOURCE_PARTITION: + + /* url file → partition */ + + r = run_helper("(sd-pull-raw)", + pull_binary_path(), + (const char* const[]) { + "systemd-pull", + "raw", + "--direct", /* just download the specified URL, don't download anything else */ + "--verify", digest, /* validate by explicit SHA256 sum */ + "--offset", offset, + "--size-max", max_size, + arg_sync ? "--sync=yes" : "--sync=no", + i->path, + t->target.path, + NULL + }); + break; + + default: + assert_not_reached(); + } + + break; + + case RESOURCE_URL_TAR: + assert(IN_SET(t->target.type, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME)); + + r = run_helper("(sd-pull-tar)", + pull_binary_path(), + (const char*const[]) { + "systemd-pull", + "tar", + "--direct", /* just download the specified URL, don't download anything else */ + "--verify", digest, /* validate by explicit SHA256 sum */ + t->target.type == RESOURCE_SUBVOLUME ? "--btrfs-subvol=yes" : "--btrfs-subvol=no", + arg_sync ? "--sync=yes" : "--sync=no", + i->path, + t->temporary_path, + NULL + }); + break; + + default: + assert_not_reached(); + } + if (r < 0) + return r; + + if (RESOURCE_IS_FILESYSTEM(t->target.type)) { + bool need_sync = false; + assert(t->temporary_path); + + /* Apply file attributes if set */ + if (f.mtime != USEC_INFINITY) { + struct timespec ts; + + timespec_store(&ts, f.mtime); + + if (utimensat(AT_FDCWD, t->temporary_path, (struct timespec[2]) { ts, ts }, AT_SYMLINK_NOFOLLOW) < 0) + return log_error_errno(errno, "Failed to adjust mtime of '%s': %m", t->temporary_path); + + need_sync = true; + } + + if (f.mode != MODE_INVALID) { + /* Try with AT_SYMLINK_NOFOLLOW first, because it's the safe thing to do. Older + * kernels don't support that however, in that case we fall back to chmod(). Not as + * safe, but shouldn't be a problem, given that we don't create symlinks here. */ + if (fchmodat(AT_FDCWD, t->temporary_path, f.mode, AT_SYMLINK_NOFOLLOW) < 0 && + (!ERRNO_IS_NOT_SUPPORTED(errno) || chmod(t->temporary_path, f.mode) < 0)) + return log_error_errno(errno, "Failed to adjust mode of '%s': %m", t->temporary_path); + + need_sync = true; + } + + /* Synchronize */ + if (arg_sync && need_sync) { + if (t->target.type == RESOURCE_REGULAR_FILE) + r = fsync_path_and_parent_at(AT_FDCWD, t->temporary_path); + else { + assert(IN_SET(t->target.type, RESOURCE_DIRECTORY, RESOURCE_SUBVOLUME)); + r = syncfs_path(AT_FDCWD, t->temporary_path); + } + if (r < 0) + return log_error_errno(r, "Failed to synchronize file system backing '%s': %m", t->temporary_path); + } + + t->install_read_only = f.read_only; + } + + if (t->target.type == RESOURCE_PARTITION) { + free_and_replace(t->partition_info.label, formatted_pattern); + t->partition_change = PARTITION_LABEL; + + if (f.partition_uuid_set) { + t->partition_info.uuid = f.partition_uuid; + t->partition_change |= PARTITION_UUID; + } + + if (f.partition_flags_set) { + t->partition_info.flags = f.partition_flags; + t->partition_change |= PARTITION_FLAGS; + } + + if (f.no_auto >= 0) { + t->partition_info.no_auto = f.no_auto; + t->partition_change |= PARTITION_NO_AUTO; + } + + if (f.read_only >= 0) { + t->partition_info.read_only = f.read_only; + t->partition_change |= PARTITION_READ_ONLY; + } + + if (f.growfs >= 0) { + t->partition_info.growfs = f.growfs; + t->partition_change |= PARTITION_GROWFS; + } + } + + /* For regular file cases the only step left is to install the file in place, which install_file() + * will do via rename(). For partition cases the only step left is to update the partition table, + * which is done at the same place. */ + + log_info("Successfully acquired '%s'.", i->path); + return 0; +} + +int transfer_install_instance( + Transfer *t, + Instance *i, + const char *root) { + + int r; + + assert(t); + assert(i); + assert(i->resource); + assert(t == container_of(i->resource, Transfer, source)); + + if (t->temporary_path) { + assert(RESOURCE_IS_FILESYSTEM(t->target.type)); + assert(t->final_path); + + r = install_file(AT_FDCWD, t->temporary_path, + AT_FDCWD, t->final_path, + INSTALL_REPLACE| + (t->install_read_only > 0 ? INSTALL_READ_ONLY : 0)| + (t->target.type == RESOURCE_REGULAR_FILE ? INSTALL_FSYNC_FULL : INSTALL_SYNCFS)); + if (r < 0) + return log_error_errno(r, "Failed to move '%s' into place: %m", t->final_path); + + log_info("Successfully installed '%s' (%s) as '%s' (%s).", + i->path, + resource_type_to_string(i->resource->type), + t->final_path, + resource_type_to_string(t->target.type)); + + t->temporary_path = mfree(t->temporary_path); + } + + if (t->partition_change != 0) { + assert(t->target.type == RESOURCE_PARTITION); + + r = patch_partition( + t->target.path, + &t->partition_info, + t->partition_change); + if (r < 0) + return r; + + log_info("Successfully installed '%s' (%s) as '%s' (%s).", + i->path, + resource_type_to_string(i->resource->type), + t->partition_info.device, + resource_type_to_string(t->target.type)); + } + + if (t->current_symlink) { + _cleanup_free_ char *buf = NULL, *parent = NULL, *relative = NULL, *resolved = NULL; + const char *link_path, *link_target; + bool resolve_link_path = false; + + if (RESOURCE_IS_FILESYSTEM(t->target.type)) { + + assert(t->target.path); + + if (path_is_absolute(t->current_symlink)) { + link_path = t->current_symlink; + resolve_link_path = true; + } else { + buf = path_make_absolute(t->current_symlink, t->target.path); + if (!buf) + return log_oom(); + + link_path = buf; + } + + link_target = t->final_path; + + } else if (t->target.type == RESOURCE_PARTITION) { + + assert(path_is_absolute(t->current_symlink)); + + link_path = t->current_symlink; + link_target = t->partition_info.device; + + resolve_link_path = true; + } else + assert_not_reached(); + + if (resolve_link_path && root) { + r = chase_symlinks(link_path, root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL); + if (r < 0) + return log_error_errno(r, "Failed to resolve current symlink path '%s': %m", link_path); + + link_path = resolved; + } + + if (link_target) { + r = path_extract_directory(link_path, &parent); + if (r < 0) + return log_error_errno(r, "Failed to extract directory of target path '%s': %m", link_path); + + r = path_make_relative(parent, link_target, &relative); + if (r < 0) + return log_error_errno(r, "Failed to make symlink path '%s' relative to '%s': %m", link_target, parent); + + r = symlink_atomic(relative, link_path); + if (r < 0) + return log_error_errno(r, "Failed to update current symlink '%s' → '%s': %m", link_path, relative); + + log_info("Updated symlink '%s' → '%s'.", link_path, relative); + } + } + + return 0; +} diff --git a/src/sysupdate/sysupdate-transfer.h b/src/sysupdate/sysupdate-transfer.h new file mode 100644 index 000000000..b0c2a6e45 --- /dev/null +++ b/src/sysupdate/sysupdate-transfer.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include +#include + +#include "sd-id128.h" + +/* Forward declare this type so that the headers below can use it */ +typedef struct Transfer Transfer; + +#include "sysupdate-partition.h" +#include "sysupdate-resource.h" + +struct Transfer { + char *definition_path; + char *min_version; + char **protected_versions; + char *current_symlink; + bool verify; + + Resource source, target; + + uint64_t instances_max; + bool remove_temporary; + + /* When creating a new partition/file, optionally override these attributes explicitly */ + sd_id128_t partition_uuid; + bool partition_uuid_set; + uint64_t partition_flags; + bool partition_flags_set; + mode_t mode; + uint64_t tries_left, tries_done; + int no_auto; + int read_only; + int growfs; + + /* If we create a new file/dir/subvol in the fs, the temporary and final path we create it under, as well as the read-only flag for it */ + char *temporary_path; + char *final_path; + int install_read_only; + + /* If we write to a partition in a partition table, the metrics of it */ + PartitionInfo partition_info; + PartitionChange partition_change; +}; + +Transfer *transfer_new(void); + +Transfer *transfer_free(Transfer *t); +DEFINE_TRIVIAL_CLEANUP_FUNC(Transfer*, transfer_free); + +int transfer_read_definition(Transfer *t, const char *path); + +int transfer_resolve_paths(Transfer *t, const char *root, const char *node); + +int transfer_vacuum(Transfer *t, uint64_t space, const char *extra_protected_version); + +int transfer_acquire_instance(Transfer *t, Instance *i); + +int transfer_install_instance(Transfer *t, Instance *i, const char *root); diff --git a/src/sysupdate/sysupdate-update-set.c b/src/sysupdate/sysupdate-update-set.c new file mode 100644 index 000000000..6d6051d15 --- /dev/null +++ b/src/sysupdate/sysupdate-update-set.c @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "alloc-util.h" +#include "glyph-util.h" +#include "string-util.h" +#include "sysupdate-update-set.h" +#include "terminal-util.h" + +UpdateSet *update_set_free(UpdateSet *us) { + if (!us) + return NULL; + + free(us->version); + free(us->instances); /* The objects referenced by this array are freed via resource_free(), not us */ + + return mfree(us); +} + +int update_set_cmp(UpdateSet *const*a, UpdateSet *const*b) { + assert(a); + assert(b); + assert(*a); + assert(*b); + assert((*a)->version); + assert((*b)->version); + + /* Newest version at the beginning */ + return -strverscmp_improved((*a)->version, (*b)->version); +} + +const char *update_set_flags_to_color(UpdateSetFlags flags) { + + if (flags == 0 || (flags & UPDATE_OBSOLETE)) + return (flags & UPDATE_NEWEST) ? ansi_highlight_grey() : ansi_grey(); + + if (FLAGS_SET(flags, UPDATE_INSTALLED|UPDATE_NEWEST)) + return ansi_highlight(); + + if (FLAGS_SET(flags, UPDATE_INSTALLED|UPDATE_PROTECTED)) + return ansi_highlight_magenta(); + + if ((flags & (UPDATE_AVAILABLE|UPDATE_INSTALLED|UPDATE_NEWEST|UPDATE_OBSOLETE)) == (UPDATE_AVAILABLE|UPDATE_NEWEST)) + return ansi_highlight_green(); + + return NULL; +} + +const char *update_set_flags_to_glyph(UpdateSetFlags flags) { + + if (flags == 0 || (flags & UPDATE_OBSOLETE)) + return special_glyph(SPECIAL_GLYPH_MULTIPLICATION_SIGN); + + if (FLAGS_SET(flags, UPDATE_INSTALLED|UPDATE_NEWEST)) + return special_glyph(SPECIAL_GLYPH_BLACK_CIRCLE); + + if (FLAGS_SET(flags, UPDATE_INSTALLED|UPDATE_PROTECTED)) + return special_glyph(SPECIAL_GLYPH_WHITE_CIRCLE); + + if ((flags & (UPDATE_AVAILABLE|UPDATE_INSTALLED|UPDATE_NEWEST|UPDATE_OBSOLETE)) == (UPDATE_AVAILABLE|UPDATE_NEWEST)) + return special_glyph(SPECIAL_GLYPH_CIRCLE_ARROW); + + return " "; +} diff --git a/src/sysupdate/sysupdate-update-set.h b/src/sysupdate/sysupdate-update-set.h new file mode 100644 index 000000000..5dd94bce4 --- /dev/null +++ b/src/sysupdate/sysupdate-update-set.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include +#include + +typedef struct UpdateSet UpdateSet; + +#include "sysupdate-instance.h" + +typedef enum UpdateSetFlags { + UPDATE_NEWEST = 1 << 0, + UPDATE_AVAILABLE = 1 << 1, + UPDATE_INSTALLED = 1 << 2, + UPDATE_OBSOLETE = 1 << 3, + UPDATE_PROTECTED = 1 << 4, +} UpdateSetFlags; + +struct UpdateSet { + UpdateSetFlags flags; + char *version; + Instance **instances; + size_t n_instances; +}; + +UpdateSet *update_set_free(UpdateSet *us); + +int update_set_cmp(UpdateSet *const*a, UpdateSet *const*b); + +const char *update_set_flags_to_color(UpdateSetFlags flags); +const char *update_set_flags_to_glyph(UpdateSetFlags flags); diff --git a/src/sysupdate/sysupdate-util.c b/src/sysupdate/sysupdate-util.c new file mode 100644 index 000000000..c7a23015c --- /dev/null +++ b/src/sysupdate/sysupdate-util.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "path-util.h" +#include "sysupdate-util.h" + +bool version_is_valid(const char *s) { + if (isempty(s)) + return false; + + if (!filename_is_valid(s)) + return false; + + if (!in_charset(s, ALPHANUMERICAL ".,_-+")) + return false; + + return true; +} diff --git a/src/sysupdate/sysupdate-util.h b/src/sysupdate/sysupdate-util.h new file mode 100644 index 000000000..afa3a9d49 --- /dev/null +++ b/src/sysupdate/sysupdate-util.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include + +bool version_is_valid(const char *s); diff --git a/src/sysupdate/sysupdate.c b/src/sysupdate/sysupdate.c new file mode 100644 index 000000000..76c5bae05 --- /dev/null +++ b/src/sysupdate/sysupdate.c @@ -0,0 +1,1411 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include + +#include "bus-error.h" +#include "bus-locator.h" +#include "chase-symlinks.h" +#include "conf-files.h" +#include "def.h" +#include "dirent-util.h" +#include "dissect-image.h" +#include "fd-util.h" +#include "format-table.h" +#include "glyph-util.h" +#include "hexdecoct.h" +#include "login-util.h" +#include "main-func.h" +#include "mount-util.h" +#include "os-util.h" +#include "pager.h" +#include "parse-argument.h" +#include "parse-util.h" +#include "path-util.h" +#include "pretty-print.h" +#include "set.h" +#include "sort-util.h" +#include "string-util.h" +#include "strv.h" +#include "sysupdate-transfer.h" +#include "sysupdate-update-set.h" +#include "sysupdate.h" +#include "terminal-util.h" +#include "utf8.h" +#include "verbs.h" + +static char *arg_definitions = NULL; +bool arg_sync = true; +uint64_t arg_instances_max = UINT64_MAX; +static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; +static PagerFlags arg_pager_flags = 0; +static bool arg_legend = true; +char *arg_root = NULL; +static char *arg_image = NULL; +static bool arg_reboot = false; +static char *arg_component = NULL; +static int arg_verify = -1; + +STATIC_DESTRUCTOR_REGISTER(arg_definitions, freep); +STATIC_DESTRUCTOR_REGISTER(arg_root, freep); +STATIC_DESTRUCTOR_REGISTER(arg_image, freep); +STATIC_DESTRUCTOR_REGISTER(arg_component, freep); + +typedef struct Context { + Transfer **transfers; + size_t n_transfers; + + UpdateSet **update_sets; + size_t n_update_sets; + + UpdateSet *newest_installed, *candidate; + + Hashmap *web_cache; /* Cache for downloaded resources, keyed by URL */ +} Context; + +static Context *context_free(Context *c) { + if (!c) + return NULL; + + for (size_t i = 0; i < c->n_transfers; i++) + transfer_free(c->transfers[i]); + free(c->transfers); + + for (size_t i = 0; i < c->n_update_sets; i++) + update_set_free(c->update_sets[i]); + free(c->update_sets); + + hashmap_free(c->web_cache); + + return mfree(c); +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(Context*, context_free); + +static Context *context_new(void) { + /* For now, no fields to initialize non-zero */ + return new0(Context, 1); +} + +static int context_read_definitions( + Context *c, + const char *directory, + const char *component, + const char *root, + const char *node) { + + _cleanup_strv_free_ char **files = NULL; + int r; + + assert(c); + + if (directory) + r = conf_files_list_strv(&files, ".conf", NULL, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) STRV_MAKE(directory)); + else if (component) { + _cleanup_strv_free_ char **n = NULL; + char **l = CONF_PATHS_STRV(""); + size_t k = 0; + + n = new0(char*, strv_length(l) + 1); + if (!n) + return log_oom(); + + STRV_FOREACH(i, l) { + char *j; + + j = strjoin(*i, "sysupdate.", component, ".d"); + if (!j) + return log_oom(); + + n[k++] = j; + } + + r = conf_files_list_strv(&files, ".conf", root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) n); + } else + r = conf_files_list_strv(&files, ".conf", root, CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char**) CONF_PATHS_STRV("sysupdate.d")); + if (r < 0) + return log_error_errno(r, "Failed to enumerate *.conf files: %m"); + + STRV_FOREACH(f, files) { + _cleanup_(transfer_freep) Transfer *t = NULL; + + if (!GREEDY_REALLOC(c->transfers, c->n_transfers + 1)) + return log_oom(); + + t = transfer_new(); + if (!t) + return log_oom(); + + t->definition_path = strdup(*f); + if (!t->definition_path) + return log_oom(); + + r = transfer_read_definition(t, *f); + if (r < 0) + return r; + + c->transfers[c->n_transfers++] = TAKE_PTR(t); + } + + if (c->n_transfers == 0) { + if (arg_component) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "No transfer definitions for component '%s' found.", arg_component); + + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "No transfer definitions found."); + } + + for (size_t i = 0; i < c->n_transfers; i++) { + r = transfer_resolve_paths(c->transfers[i], root, node); + if (r < 0) + return r; + } + + return 0; +} + +static int context_load_installed_instances(Context *c) { + int r; + + assert(c); + + log_info("Discovering installed instances…"); + + for (size_t i = 0; i < c->n_transfers; i++) { + r = resource_load_instances( + &c->transfers[i]->target, + arg_verify >= 0 ? arg_verify : c->transfers[i]->verify, + &c->web_cache); + if (r < 0) + return r; + } + + return 0; +} + +static int context_load_available_instances(Context *c) { + int r; + + assert(c); + + log_info("Discovering available instances…"); + + for (size_t i = 0; i < c->n_transfers; i++) { + assert(c->transfers[i]); + + r = resource_load_instances( + &c->transfers[i]->source, + arg_verify >= 0 ? arg_verify : c->transfers[i]->verify, + &c->web_cache); + if (r < 0) + return r; + } + + return 0; +} + +static int context_discover_update_sets_by_flag(Context *c, UpdateSetFlags flags) { + _cleanup_free_ Instance **cursor_instances = NULL; + _cleanup_free_ char *boundary = NULL; + bool newest_found = false; + int r; + + assert(c); + assert(IN_SET(flags, UPDATE_AVAILABLE, UPDATE_INSTALLED)); + + for (;;) { + bool incomplete = false, exists = false; + UpdateSetFlags extra_flags = 0; + _cleanup_free_ char *cursor = NULL; + UpdateSet *us = NULL; + + for (size_t k = 0; k < c->n_transfers; k++) { + Transfer *t = c->transfers[k]; + bool cursor_found = false; + Resource *rr; + + assert(t); + + if (flags == UPDATE_AVAILABLE) + rr = &t->source; + else { + assert(flags == UPDATE_INSTALLED); + rr = &t->target; + } + + for (size_t j = 0; j < rr->n_instances; j++) { + Instance *i = rr->instances[j]; + + assert(i); + + /* Is the instance we are looking at equal or newer than the boundary? If so, we + * already checked this version, and it wasn't complete, let's ignore it. */ + if (boundary && strverscmp_improved(i->metadata.version, boundary) >= 0) + continue; + + if (cursor) { + if (strverscmp_improved(i->metadata.version, cursor) != 0) + continue; + } else { + cursor = strdup(i->metadata.version); + if (!cursor) + return log_oom(); + } + + cursor_found = true; + + if (!cursor_instances) { + cursor_instances = new(Instance*, c->n_transfers); + if (!cursor_instances) + return -ENOMEM; + } + cursor_instances[k] = i; + break; + } + + if (!cursor) /* No suitable instance beyond the boundary found? Then we are done! */ + break; + + if (!cursor_found) { + /* Hmm, we didn't find the version indicated by 'cursor' among the instances + * of this transfer, let's skip it. */ + incomplete = true; + break; + } + + if (t->min_version && strverscmp_improved(t->min_version, cursor) > 0) + extra_flags |= UPDATE_OBSOLETE; + + if (strv_contains(t->protected_versions, cursor)) + extra_flags |= UPDATE_PROTECTED; + } + + if (!cursor) /* EOL */ + break; + + r = free_and_strdup_warn(&boundary, cursor); + if (r < 0) + return r; + + if (incomplete) /* One transfer was missing this version, ignore the whole thing */ + continue; + + /* See if we already have this update set in our table */ + for (size_t i = 0; i < c->n_update_sets; i++) { + if (strverscmp_improved(c->update_sets[i]->version, cursor) != 0) + continue; + + /* We only store the instances we found first, but we remember we also found it again */ + c->update_sets[i]->flags |= flags | extra_flags; + exists = true; + newest_found = true; + break; + } + + if (exists) + continue; + + /* Doesn't exist yet, let's add it */ + if (!GREEDY_REALLOC(c->update_sets, c->n_update_sets + 1)) + return log_oom(); + + us = new(UpdateSet, 1); + if (!us) + return log_oom(); + + *us = (UpdateSet) { + .flags = flags | (newest_found ? 0 : UPDATE_NEWEST) | extra_flags, + .version = TAKE_PTR(cursor), + .instances = TAKE_PTR(cursor_instances), + .n_instances = c->n_transfers, + }; + + c->update_sets[c->n_update_sets++] = us; + + newest_found = true; + + /* Remember which one is the newest installed */ + if ((us->flags & (UPDATE_NEWEST|UPDATE_INSTALLED)) == (UPDATE_NEWEST|UPDATE_INSTALLED)) + c->newest_installed = us; + + /* Remember which is the newest non-obsolete, available (and not installed) version, which we declare the "candidate" */ + if ((us->flags & (UPDATE_NEWEST|UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_OBSOLETE)) == (UPDATE_NEWEST|UPDATE_AVAILABLE)) + c->candidate = us; + } + + /* Newest installed is newer than or equal to candidate? Then suppress the candidate */ + if (c->newest_installed && c->candidate && strverscmp_improved(c->newest_installed->version, c->candidate->version) >= 0) + c->candidate = NULL; + + return 0; +} + +static int context_discover_update_sets(Context *c) { + int r; + + assert(c); + + log_info("Determining installed update sets…"); + + r = context_discover_update_sets_by_flag(c, UPDATE_INSTALLED); + if (r < 0) + return r; + + log_info("Determining available update sets…"); + + r = context_discover_update_sets_by_flag(c, UPDATE_AVAILABLE); + if (r < 0) + return r; + + typesafe_qsort(c->update_sets, c->n_update_sets, update_set_cmp); + return 0; +} + +static const char *update_set_flags_to_string(UpdateSetFlags flags) { + + switch ((unsigned) flags) { + + case 0: + return "n/a"; + + case UPDATE_INSTALLED|UPDATE_NEWEST: + case UPDATE_INSTALLED|UPDATE_NEWEST|UPDATE_PROTECTED: + case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_NEWEST: + case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_NEWEST|UPDATE_PROTECTED: + return "current"; + + case UPDATE_AVAILABLE|UPDATE_NEWEST: + case UPDATE_AVAILABLE|UPDATE_NEWEST|UPDATE_PROTECTED: + return "candidate"; + + case UPDATE_INSTALLED: + case UPDATE_INSTALLED|UPDATE_AVAILABLE: + return "installed"; + + case UPDATE_INSTALLED|UPDATE_PROTECTED: + case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_PROTECTED: + return "protected"; + + case UPDATE_AVAILABLE: + case UPDATE_AVAILABLE|UPDATE_PROTECTED: + return "available"; + + case UPDATE_INSTALLED|UPDATE_OBSOLETE|UPDATE_NEWEST: + case UPDATE_INSTALLED|UPDATE_OBSOLETE|UPDATE_NEWEST|UPDATE_PROTECTED: + case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_NEWEST: + case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_NEWEST|UPDATE_PROTECTED: + return "current+obsolete"; + + case UPDATE_INSTALLED|UPDATE_OBSOLETE: + case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_OBSOLETE: + return "installed+obsolete"; + + case UPDATE_INSTALLED|UPDATE_OBSOLETE|UPDATE_PROTECTED: + case UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_PROTECTED: + return "protected+obsolete"; + + case UPDATE_AVAILABLE|UPDATE_OBSOLETE: + case UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_PROTECTED: + case UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_NEWEST: + case UPDATE_AVAILABLE|UPDATE_OBSOLETE|UPDATE_NEWEST|UPDATE_PROTECTED: + return "available+obsolete"; + + default: + assert_not_reached(); + } +} + + +static int context_show_table(Context *c) { + _cleanup_(table_unrefp) Table *t = NULL; + int r; + + assert(c); + + t = table_new("", "version", "installed", "available", "assessment"); + if (!t) + return log_oom(); + + (void) table_set_align_percent(t, table_get_cell(t, 0, 0), 100); + (void) table_set_align_percent(t, table_get_cell(t, 0, 2), 50); + (void) table_set_align_percent(t, table_get_cell(t, 0, 3), 50); + + for (size_t i = 0; i < c->n_update_sets; i++) { + UpdateSet *us = c->update_sets[i]; + const char *color; + + color = update_set_flags_to_color(us->flags); + + r = table_add_many(t, + TABLE_STRING, update_set_flags_to_glyph(us->flags), + TABLE_SET_COLOR, color, + TABLE_STRING, us->version, + TABLE_SET_COLOR, color, + TABLE_STRING, special_glyph_check_mark_space(FLAGS_SET(us->flags, UPDATE_INSTALLED)), + TABLE_SET_COLOR, color, + TABLE_STRING, special_glyph_check_mark_space(FLAGS_SET(us->flags, UPDATE_AVAILABLE)), + TABLE_SET_COLOR, color, + TABLE_STRING, update_set_flags_to_string(us->flags), + TABLE_SET_COLOR, color); + if (r < 0) + return table_log_add_error(r); + } + + return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend); +} + +static UpdateSet *context_update_set_by_version(Context *c, const char *version) { + assert(c); + assert(version); + + for (size_t i = 0; i < c->n_update_sets; i++) + if (streq(c->update_sets[i]->version, version)) + return c->update_sets[i]; + + return NULL; +} + +static int context_show_version(Context *c, const char *version) { + bool show_fs_columns = false, show_partition_columns = false, + have_fs_attributes = false, have_partition_attributes = false, + have_size = false, have_tries = false, have_no_auto = false, + have_read_only = false, have_growfs = false, have_sha256 = false; + _cleanup_(table_unrefp) Table *t = NULL; + UpdateSet *us; + int r; + + assert(c); + assert(version); + + us = context_update_set_by_version(c, version); + if (!us) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Update '%s' not found.", version); + + if (arg_json_format_flags & (JSON_FORMAT_OFF|JSON_FORMAT_PRETTY|JSON_FORMAT_PRETTY_AUTO)) + (void) pager_open(arg_pager_flags); + + if (FLAGS_SET(arg_json_format_flags, JSON_FORMAT_OFF)) + printf("%s%s%s Version: %s\n" + " State: %s%s%s\n" + "Installed: %s%s\n" + "Available: %s%s\n" + "Protected: %s%s%s\n" + " Obsolete: %s%s%s\n\n", + strempty(update_set_flags_to_color(us->flags)), update_set_flags_to_glyph(us->flags), ansi_normal(), us->version, + strempty(update_set_flags_to_color(us->flags)), update_set_flags_to_string(us->flags), ansi_normal(), + yes_no(us->flags & UPDATE_INSTALLED), FLAGS_SET(us->flags, UPDATE_INSTALLED|UPDATE_NEWEST) ? " (newest)" : "", + yes_no(us->flags & UPDATE_AVAILABLE), (us->flags & (UPDATE_INSTALLED|UPDATE_AVAILABLE|UPDATE_NEWEST)) == (UPDATE_AVAILABLE|UPDATE_NEWEST) ? " (newest)" : "", + FLAGS_SET(us->flags, UPDATE_INSTALLED|UPDATE_PROTECTED) ? ansi_highlight() : "", yes_no(FLAGS_SET(us->flags, UPDATE_INSTALLED|UPDATE_PROTECTED)), ansi_normal(), + us->flags & UPDATE_OBSOLETE ? ansi_highlight_red() : "", yes_no(us->flags & UPDATE_OBSOLETE), ansi_normal()); + + + t = table_new("type", "path", "ptuuid", "ptflags", "mtime", "mode", "size", "tries-done", "tries-left", "noauto", "ro", "growfs", "sha256"); + if (!t) + return log_oom(); + + (void) table_set_align_percent(t, table_get_cell(t, 0, 3), 100); + (void) table_set_align_percent(t, table_get_cell(t, 0, 4), 100); + (void) table_set_align_percent(t, table_get_cell(t, 0, 5), 100); + (void) table_set_align_percent(t, table_get_cell(t, 0, 6), 100); + (void) table_set_align_percent(t, table_get_cell(t, 0, 7), 100); + (void) table_set_align_percent(t, table_get_cell(t, 0, 8), 100); + (void) table_set_empty_string(t, "-"); + + /* Determine if the target will make use of partition/fs attributes for any of the transfers */ + for (size_t n = 0; n < c->n_transfers; n++) { + Transfer *tr = c->transfers[n]; + + if (tr->target.type == RESOURCE_PARTITION) + show_partition_columns = true; + if (RESOURCE_IS_FILESYSTEM(tr->target.type)) + show_fs_columns = true; + } + + for (size_t n = 0; n < us->n_instances; n++) { + Instance *i = us->instances[n]; + + r = table_add_many(t, + TABLE_STRING, resource_type_to_string(i->resource->type), + TABLE_PATH, i->path); + if (r < 0) + return table_log_add_error(r); + + if (i->metadata.partition_uuid_set) { + have_partition_attributes = true; + r = table_add_cell(t, NULL, TABLE_UUID, &i->metadata.partition_uuid); + } else + r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return table_log_add_error(r); + + if (i->metadata.partition_flags_set) { + have_partition_attributes = true; + r = table_add_cell(t, NULL, TABLE_UINT64_HEX, &i->metadata.partition_flags); + } else + r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return table_log_add_error(r); + + if (i->metadata.mtime != USEC_INFINITY) { + have_fs_attributes = true; + r = table_add_cell(t, NULL, TABLE_TIMESTAMP, &i->metadata.mtime); + } else + r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return table_log_add_error(r); + + if (i->metadata.mode != MODE_INVALID) { + have_fs_attributes = true; + r = table_add_cell(t, NULL, TABLE_MODE, &i->metadata.mode); + } else + r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return table_log_add_error(r); + + if (i->metadata.size != UINT64_MAX) { + have_size = true; + r = table_add_cell(t, NULL, TABLE_SIZE, &i->metadata.size); + } else + r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return table_log_add_error(r); + + if (i->metadata.tries_done != UINT64_MAX) { + have_tries = true; + r = table_add_cell(t, NULL, TABLE_UINT64, &i->metadata.tries_done); + } else + r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return table_log_add_error(r); + + if (i->metadata.tries_left != UINT64_MAX) { + have_tries = true; + r = table_add_cell(t, NULL, TABLE_UINT64, &i->metadata.tries_left); + } else + r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return table_log_add_error(r); + + if (i->metadata.no_auto >= 0) { + bool b; + + have_no_auto = true; + b = i->metadata.no_auto; + r = table_add_cell(t, NULL, TABLE_BOOLEAN, &b); + } else + r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return table_log_add_error(r); + if (i->metadata.read_only >= 0) { + bool b; + + have_read_only = true; + b = i->metadata.read_only; + r = table_add_cell(t, NULL, TABLE_BOOLEAN, &b); + } else + r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return table_log_add_error(r); + + if (i->metadata.growfs >= 0) { + bool b; + + have_growfs = true; + b = i->metadata.growfs; + r = table_add_cell(t, NULL, TABLE_BOOLEAN, &b); + } else + r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return table_log_add_error(r); + + if (i->metadata.sha256sum_set) { + _cleanup_free_ char *formatted = NULL; + + have_sha256 = true; + + formatted = hexmem(i->metadata.sha256sum, sizeof(i->metadata.sha256sum)); + if (!formatted) + return log_oom(); + + r = table_add_cell(t, NULL, TABLE_STRING, formatted); + } else + r = table_add_cell(t, NULL, TABLE_EMPTY, NULL); + if (r < 0) + return table_log_add_error(r); + } + + /* Hide the fs/partition columns if we don't have any data to show there */ + if (!have_fs_attributes) + show_fs_columns = false; + if (!have_partition_attributes) + show_partition_columns = false; + + if (!show_partition_columns) + (void) table_hide_column_from_display(t, 2, 3); + if (!show_fs_columns) + (void) table_hide_column_from_display(t, 4, 5); + if (!have_size) + (void) table_hide_column_from_display(t, 6); + if (!have_tries) + (void) table_hide_column_from_display(t, 7, 8); + if (!have_no_auto) + (void) table_hide_column_from_display(t, 9); + if (!have_read_only) + (void) table_hide_column_from_display(t, 10); + if (!have_growfs) + (void) table_hide_column_from_display(t, 11); + if (!have_sha256) + (void) table_hide_column_from_display(t, 12); + + return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend); +} + +static int context_vacuum( + Context *c, + uint64_t space, + const char *extra_protected_version) { + + int r, count = 0; + + assert(c); + + if (space == 0) + log_info("Making room…"); + else + log_info("Making room for %" PRIu64 " updates…", space); + + for (size_t i = 0; i < c->n_transfers; i++) { + r = transfer_vacuum(c->transfers[i], space, extra_protected_version); + if (r < 0) + return r; + + count = MAX(count, r); + } + + if (count > 0) + log_info("Removed %i instances.", count); + else + log_info("Removed no instances."); + + return 0; +} + +static int context_make_offline(Context **ret, const char *node) { + _cleanup_(context_freep) Context* context = NULL; + int r; + + assert(ret); + + /* Allocates a context object and initializes everything we can initialize offline, i.e. without + * checking on the update source (i.e. the Internet) what versions are available */ + + context = context_new(); + if (!context) + return log_oom(); + + r = context_read_definitions(context, arg_definitions, arg_component, arg_root, node); + if (r < 0) + return r; + + r = context_load_installed_instances(context); + if (r < 0) + return r; + + *ret = TAKE_PTR(context); + return 0; +} + +static int context_make_online(Context **ret, const char *node) { + _cleanup_(context_freep) Context* context = NULL; + int r; + + assert(ret); + + /* Like context_make_offline(), but also communicates with the update source looking for new + * versions. */ + + r = context_make_offline(&context, node); + if (r < 0) + return r; + + r = context_load_available_instances(context); + if (r < 0) + return r; + + r = context_discover_update_sets(context); + if (r < 0) + return r; + + *ret = TAKE_PTR(context); + return 0; +} + +static int context_apply( + Context *c, + const char *version, + UpdateSet **ret_applied) { + + UpdateSet *us = NULL; + int r; + + assert(c); + + if (version) { + us = context_update_set_by_version(c, version); + if (!us) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), "Update '%s' not found.", version); + } else { + if (!c->candidate) { + log_info("No update needed."); + + if (ret_applied) + *ret_applied = NULL; + + return 0; + } + + us = c->candidate; + } + + if (FLAGS_SET(us->flags, UPDATE_INSTALLED)) { + log_info("Selected update '%s' is already installed. Skipping update.", us->version); + + if (ret_applied) + *ret_applied = NULL; + + return 0; + } + if (!FLAGS_SET(us->flags, UPDATE_AVAILABLE)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected update '%s' is not available, refusing.", us->version); + if (FLAGS_SET(us->flags, UPDATE_OBSOLETE)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Selected update '%s' is obsolete, refusing.", us->version); + + assert((us->flags & (UPDATE_AVAILABLE|UPDATE_INSTALLED|UPDATE_OBSOLETE)) == UPDATE_AVAILABLE); + + if (!FLAGS_SET(us->flags, UPDATE_NEWEST)) + log_notice("Selected update '%s' is not the newest, proceeding anyway.", us->version); + if (c->newest_installed && strverscmp_improved(c->newest_installed->version, us->version) > 0) + log_notice("Selected update '%s' is older than newest installed version, proceeding anyway.", us->version); + + log_info("Selected update '%s' for install.", us->version); + + (void) sd_notifyf(false, + "STATUS=Making room for '%s'.", us->version); + + /* Let's make some room. We make sure for each transfer we have one free space to fill. While + * removing stuff we'll protect the version we are trying to acquire. Why that? Maybe an earlier + * download succeeded already, in which case we shouldn't remove it just to acquire it again */ + r = context_vacuum( + c, + /* space = */ 1, + /* extra_protected_version = */ us->version); + if (r < 0) + return r; + + if (arg_sync) + sync(); + + (void) sd_notifyf(false, + "STATUS=Updating to '%s'.\n", us->version); + + /* There should now be one instance picked for each transfer, and the order is the same */ + assert(us->n_instances == c->n_transfers); + + for (size_t i = 0; i < c->n_transfers; i++) { + r = transfer_acquire_instance(c->transfers[i], us->instances[i]); + if (r < 0) + return r; + } + + if (arg_sync) + sync(); + + for (size_t i = 0; i < c->n_transfers; i++) { + r = transfer_install_instance(c->transfers[i], us->instances[i], arg_root); + if (r < 0) + return r; + } + + log_info("%s Successfully installed update '%s'.", special_glyph(SPECIAL_GLYPH_SPARKLES), us->version); + + if (ret_applied) + *ret_applied = us; + + return 1; +} + +static int reboot_now(void) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_close_unrefp) sd_bus *bus = NULL; + int r; + + r = sd_bus_open_system(&bus); + if (r < 0) + return log_error_errno(r, "Failed to open bus connection: %m"); + + r = bus_call_method(bus, bus_login_mgr, "RebootWithFlags", &error, NULL, "t", + (uint64_t) SD_LOGIND_ROOT_CHECK_INHIBITORS); + if (r < 0) + return log_error_errno(r, "Failed to issue reboot request: %s", bus_error_message(&error, r)); + + return 0; +} + +static int process_image( + bool ro, + char **ret_mounted_dir, + LoopDevice **ret_loop_device, + DecryptedImage **ret_decrypted_image) { + + _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; + _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; + _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; + int r; + + assert(ret_mounted_dir); + assert(ret_loop_device); + assert(ret_decrypted_image); + + if (!arg_image) + return 0; + + assert(!arg_root); + + r = mount_image_privately_interactively( + arg_image, + (ro ? DISSECT_IMAGE_READ_ONLY : 0) | + DISSECT_IMAGE_FSCK | + DISSECT_IMAGE_MKDIR | + DISSECT_IMAGE_GROWFS | + DISSECT_IMAGE_RELAX_VAR_CHECK | + DISSECT_IMAGE_USR_NO_ROOT | + DISSECT_IMAGE_GENERIC_ROOT | + DISSECT_IMAGE_REQUIRE_ROOT, + &mounted_dir, + &loop_device, + &decrypted_image); + if (r < 0) + return r; + + arg_root = strdup(mounted_dir); + if (!arg_root) + return log_oom(); + + *ret_mounted_dir = TAKE_PTR(mounted_dir); + *ret_loop_device = TAKE_PTR(loop_device); + *ret_decrypted_image = TAKE_PTR(decrypted_image); + + return 0; +} + +static int verb_list(int argc, char **argv, void *userdata) { + _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; + _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; + _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; + _cleanup_(context_freep) Context* context = NULL; + const char *version; + int r; + + assert(argc <= 2); + version = argc >= 2 ? argv[1] : NULL; + + r = process_image(/* ro= */ true, &mounted_dir, &loop_device, &decrypted_image); + if (r < 0) + return r; + + r = context_make_online(&context, loop_device ? loop_device->node : NULL); + if (r < 0) + return r; + + if (version) + return context_show_version(context, version); + else + return context_show_table(context); +} + +static int verb_check_new(int argc, char **argv, void *userdata) { + _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; + _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; + _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; + _cleanup_(context_freep) Context* context = NULL; + int r; + + assert(argc <= 1); + + r = process_image(/* ro= */ true, &mounted_dir, &loop_device, &decrypted_image); + if (r < 0) + return r; + + r = context_make_online(&context, loop_device ? loop_device->node : NULL); + if (r < 0) + return r; + + if (!context->candidate) { + log_debug("No candidate found."); + return EXIT_FAILURE; + } + + puts(context->candidate->version); + return EXIT_SUCCESS; +} + +static int verb_vacuum(int argc, char **argv, void *userdata) { + _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; + _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; + _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; + _cleanup_(context_freep) Context* context = NULL; + int r; + + assert(argc <= 1); + + r = process_image(/* ro= */ false, &mounted_dir, &loop_device, &decrypted_image); + if (r < 0) + return r; + + r = context_make_offline(&context, loop_device ? loop_device->node : NULL); + if (r < 0) + return r; + + return context_vacuum(context, 0, NULL); +} + +static int verb_update(int argc, char **argv, void *userdata) { + _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; + _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; + _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; + _cleanup_(context_freep) Context* context = NULL; + _cleanup_free_ char *booted_version = NULL; + UpdateSet *applied = NULL; + const char *version; + int r; + + assert(argc <= 2); + version = argc >= 2 ? argv[1] : NULL; + + if (arg_reboot) { + /* If automatic reboot on completion is requested, let's first determine the currently booted image */ + + r = parse_os_release(arg_root, "IMAGE_VERSION", &booted_version); + if (r < 0) + return log_error_errno(r, "Failed to parse /etc/os-release: %m"); + if (!booted_version) + return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "/etc/os-release lacks IMAGE_VERSION field."); + } + + r = process_image(/* ro= */ false, &mounted_dir, &loop_device, &decrypted_image); + if (r < 0) + return r; + + r = context_make_online(&context, loop_device ? loop_device->node : NULL); + if (r < 0) + return r; + + r = context_apply(context, version, &applied); + if (r < 0) + return r; + + if (r > 0 && arg_reboot) { + assert(applied); + assert(booted_version); + + if (strverscmp_improved(applied->version, booted_version) > 0) { + log_notice("Newly installed version is newer than booted version, rebooting."); + return reboot_now(); + } + + log_info("Booted version is newer or identical to newly installed version, not rebooting."); + } + + return 0; +} + +static int verb_pending_or_reboot(int argc, char **argv, void *userdata) { + _cleanup_(context_freep) Context* context = NULL; + _cleanup_free_ char *booted_version = NULL; + int r; + + assert(argc == 1); + + if (arg_image || arg_root) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "The --root=/--image switches may not be combined with the '%s' operation.", argv[0]); + + r = context_make_offline(&context, NULL); + if (r < 0) + return r; + + log_info("Determining installed update sets…"); + + r = context_discover_update_sets_by_flag(context, UPDATE_INSTALLED); + if (r < 0) + return r; + if (!context->newest_installed) + return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "Couldn't find any suitable installed versions."); + + r = parse_os_release(arg_root, "IMAGE_VERSION", &booted_version); + if (r < 0) /* yes, arg_root is NULL here, but we have to pass something, and it's a lot more readable + * if we see what the first argument is about */ + return log_error_errno(r, "Failed to parse /etc/os-release: %m"); + if (!booted_version) + return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "/etc/os-release lacks IMAGE_VERSION= field."); + + r = strverscmp_improved(context->newest_installed->version, booted_version); + if (r > 0) { + log_notice("Newest installed version '%s' is newer than booted version '%s'.%s", + context->newest_installed->version, booted_version, + streq(argv[0], "pending") ? " Reboot recommended." : ""); + + if (streq(argv[0], "reboot")) + return reboot_now(); + + return EXIT_SUCCESS; + } else if (r == 0) + log_info("Newest installed version '%s' matches booted version '%s'.", + context->newest_installed->version, booted_version); + else + log_warning("Newest installed version '%s' is older than booted version '%s'.", + context->newest_installed->version, booted_version); + + if (streq(argv[0], "pending")) /* When called as 'pending' tell the caller via failure exit code that there's nothing newer installed */ + return EXIT_FAILURE; + + return EXIT_SUCCESS; +} + +static int component_name_valid(const char *c) { + _cleanup_free_ char *j = NULL; + + /* See if the specified string enclosed in the directory prefix+suffix would be a valid file name */ + + if (isempty(c)) + return false; + + if (string_has_cc(c, NULL)) + return false; + + if (!utf8_is_valid(c)) + return false; + + j = strjoin("sysupdate.", c, ".d"); + if (!j) + return -ENOMEM; + + return filename_is_valid(j); +} + +static int verb_components(int argc, char **argv, void *userdata) { + _cleanup_(loop_device_unrefp) LoopDevice *loop_device = NULL; + _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; + _cleanup_(umount_and_rmdir_and_freep) char *mounted_dir = NULL; + _cleanup_(set_freep) Set *names = NULL; + _cleanup_free_ char **z = NULL; /* We use simple free() rather than strv_free() here, since set_free() will free the strings for us */ + char **l = CONF_PATHS_STRV(""); + bool has_default_component = false; + int r; + + assert(argc <= 1); + + r = process_image(/* ro= */ false, &mounted_dir, &loop_device, &decrypted_image); + if (r < 0) + return r; + + STRV_FOREACH(i, l) { + _cleanup_closedir_ DIR *d = NULL; + _cleanup_free_ char *p = NULL; + + r = chase_symlinks_and_opendir(*i, arg_root, CHASE_PREFIX_ROOT, &p, &d); + if (r == -ENOENT) + continue; + if (r < 0) + return log_error_errno(r, "Failed to open directory '%s': %m", *i); + + for (;;) { + _cleanup_free_ char *n = NULL; + struct dirent *de; + const char *e, *a; + + de = readdir_ensure_type(d); + if (!de) { + if (errno != 0) + return log_error_errno(errno, "Failed to enumerate directory '%s': %m", p); + + break; + } + + if (de->d_type != DT_DIR) + continue; + + if (dot_or_dot_dot(de->d_name)) + continue; + + if (streq(de->d_name, "sysupdate.d")) { + has_default_component = true; + continue; + } + + e = startswith(de->d_name, "sysupdate."); + if (!e) + continue; + + a = endswith(e, ".d"); + if (!a) + continue; + + n = strndup(e, a - e); + if (!n) + return log_oom(); + + r = component_name_valid(n); + if (r < 0) + return log_error_errno(r, "Unable to validate component name: %m"); + if (r == 0) + continue; + + r = set_ensure_consume(&names, &string_hash_ops_free, TAKE_PTR(n)); + if (r < 0 && r != -EEXIST) + return log_error_errno(r, "Failed to add component to set: %m"); + } + } + + if (!has_default_component && set_isempty(names)) { + log_info("No components defined."); + return 0; + } + + z = set_get_strv(names); + if (!z) + return log_oom(); + + strv_sort(z); + + if (has_default_component) + printf("%s%s\n", + ansi_highlight(), ansi_normal()); + + STRV_FOREACH(i, z) + puts(*i); + + return 0; +} + +static int verb_help(int argc, char **argv, void *userdata) { + _cleanup_free_ char *link = NULL; + int r; + + r = terminal_urlify_man("systemd-sysupdate", "1", &link); + if (r < 0) + return log_oom(); + + printf("%1$s [OPTIONS...] [VERSION]\n" + "\n%5$sUpdate OS images.%6$s\n" + "\n%3$sCommands:%4$s\n" + " list [VERSION] Show installed and available versions\n" + " check-new Check if there's a new version available\n" + " update [VERSION] Install new version now\n" + " vacuum Make room, by deleting old versions\n" + " pending Report whether a newer version is installed than\n" + " currently booted\n" + " reboot Reboot if a newer version is installed than booted\n" + " components Show list of components\n" + " -h --help Show this help\n" + " --version Show package version\n" + "\n%3$sOptions:%4$s\n" + " -C --component=NAME Select component to update\n" + " --definitions=DIR Find transfer definitions in specified directory\n" + " --root=PATH Operate relative to root path\n" + " --image=PATH Operate relative to image file\n" + " -m --instances-max=INT How many instances to maintain\n" + " --sync=BOOL Controls whether to sync data to disk\n" + " --verify=BOOL Force signature verification on or off\n" + " --reboot Reboot after updating to newer version\n" + " --no-pager Do not pipe output into a pager\n" + " --no-legend Do not show the headers and footers\n" + " --json=pretty|short|off\n" + " Generate JSON output\n" + "\nSee the %2$s for details.\n" + , program_invocation_short_name + , link + , ansi_underline(), ansi_normal() + , ansi_highlight(), ansi_normal() + ); + + return 0; +} + +static int parse_argv(int argc, char *argv[]) { + + enum { + ARG_VERSION = 0x100, + ARG_NO_PAGER, + ARG_NO_LEGEND, + ARG_SYNC, + ARG_DEFINITIONS, + ARG_JSON, + ARG_ROOT, + ARG_IMAGE, + ARG_REBOOT, + ARG_VERIFY, + }; + + static const struct option options[] = { + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "no-legend", no_argument, NULL, ARG_NO_LEGEND }, + { "definitions", required_argument, NULL, ARG_DEFINITIONS }, + { "instances-max", required_argument, NULL, 'm' }, + { "sync", required_argument, NULL, ARG_SYNC }, + { "json", required_argument, NULL, ARG_JSON }, + { "root", required_argument, NULL, ARG_ROOT }, + { "image", required_argument, NULL, ARG_IMAGE }, + { "reboot", no_argument, NULL, ARG_REBOOT }, + { "component", required_argument, NULL, 'C' }, + { "verify", required_argument, NULL, ARG_VERIFY }, + {} + }; + + int c, r; + + assert(argc >= 0); + assert(argv); + + while ((c = getopt_long(argc, argv, "hm:C:", options, NULL)) >= 0) { + + switch (c) { + + case 'h': + return verb_help(0, NULL, NULL); + + case ARG_VERSION: + return version(); + + case ARG_NO_PAGER: + arg_pager_flags |= PAGER_DISABLE; + break; + + case ARG_NO_LEGEND: + arg_legend = false; + break; + + case 'm': + r = safe_atou64(optarg, &arg_instances_max); + if (r < 0) + return log_error_errno(r, "Failed to parse --instances-max= parameter: %s", optarg); + + break; + + case ARG_SYNC: + r = parse_boolean_argument("--sync=", optarg, &arg_sync); + if (r < 0) + return r; + break; + + case ARG_DEFINITIONS: + r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_definitions); + if (r < 0) + return r; + break; + + case ARG_JSON: + r = parse_json_argument(optarg, &arg_json_format_flags); + if (r <= 0) + return r; + + break; + + case ARG_ROOT: + r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_root); + if (r < 0) + return r; + break; + + case ARG_IMAGE: + r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image); + if (r < 0) + return r; + break; + + case ARG_REBOOT: + arg_reboot = true; + break; + + case 'C': + if (isempty(optarg)) { + arg_component = mfree(arg_component); + break; + } + + r = component_name_valid(optarg); + if (r < 0) + return log_error_errno(r, "Failed to determine if component name is valid: %m"); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Component name invalid: %s", optarg); + + r = free_and_strdup_warn(&arg_component, optarg); + if (r < 0) + return r; + + break; + + case ARG_VERIFY: { + bool b; + + r = parse_boolean_argument("--verify=", optarg, &b); + if (r < 0) + return r; + + arg_verify = b; + break; + } + + case '?': + return -EINVAL; + + default: + assert_not_reached(); + } + } + + if (arg_image && arg_root) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Please specify either --root= or --image=, the combination of both is not supported."); + + if ((arg_image || arg_root) && arg_reboot) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --reboot switch may not be combined with --root= or --image=."); + + if (arg_definitions && arg_component) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The --definitions= and --component= switches may not be combined."); + + return 1; +} + +static int sysupdate_main(int argc, char *argv[]) { + + static const Verb verbs[] = { + { "list", VERB_ANY, 2, VERB_DEFAULT, verb_list }, + { "components", VERB_ANY, 1, 0, verb_components }, + { "check-new", VERB_ANY, 1, 0, verb_check_new }, + { "update", VERB_ANY, 2, 0, verb_update }, + { "vacuum", VERB_ANY, 1, 0, verb_vacuum }, + { "reboot", 1, 1, 0, verb_pending_or_reboot }, + { "pending", 1, 1, 0, verb_pending_or_reboot }, + { "help", VERB_ANY, 1, 0, verb_help }, + {} + }; + + return dispatch_verb(argc, argv, verbs, NULL); +} + +static int run(int argc, char *argv[]) { + int r; + + log_setup(); + + r = parse_argv(argc, argv); + if (r <= 0) + return r; + + return sysupdate_main(argc, argv); +} + +DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); diff --git a/src/sysupdate/sysupdate.h b/src/sysupdate/sysupdate.h new file mode 100644 index 000000000..6d387b7a5 --- /dev/null +++ b/src/sysupdate/sysupdate.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include + +extern bool arg_sync; +extern uint64_t arg_instances_max; +extern char *arg_root; + +static inline const char* import_binary_path(void) { + return secure_getenv("SYSTEMD_IMPORT_PATH") ?: SYSTEMD_IMPORT_PATH; +} + +static inline const char* import_fs_binary_path(void) { + return secure_getenv("SYSTEMD_IMPORT_FS_PATH") ?: SYSTEMD_IMPORT_FS_PATH; +} + +static inline const char *pull_binary_path(void) { + return secure_getenv("SYSTEMD_PULL_PATH") ?: SYSTEMD_PULL_PATH; +} diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index 07a65a2eb..59b76c862 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -312,14 +312,13 @@ static int putgrent_with_members(const struct group *gr, FILE *group) { if (a) { _cleanup_strv_free_ char **l = NULL; bool added = false; - char **i; l = strv_copy(gr->gr_mem); if (!l) return -ENOMEM; STRV_FOREACH(i, a) { - if (strv_find(l, *i)) + if (strv_contains(l, *i)) continue; if (strv_extend(&l, *i) < 0) @@ -357,14 +356,13 @@ static int putsgent_with_members(const struct sgrp *sg, FILE *gshadow) { if (a) { _cleanup_strv_free_ char **l = NULL; bool added = false; - char **i; l = strv_copy(sg->sg_mem); if (!l) return -ENOMEM; STRV_FOREACH(i, a) { - if (strv_find(l, *i)) + if (strv_contains(l, *i)) continue; if (strv_extend(&l, *i) < 0) @@ -1406,8 +1404,6 @@ static int add_implicit(void) { /* Implicitly create additional users and groups, if they were listed in "m" lines */ ORDERED_HASHMAP_FOREACH_KEY(l, g, members) { - char **m; - STRV_FOREACH(m, l) if (!ordered_hashmap_get(users, *m)) { _cleanup_(item_freep) Item *j = NULL; @@ -1977,7 +1973,6 @@ static int parse_argv(int argc, char *argv[]) { } static int parse_arguments(char **args) { - char **arg; unsigned pos = 1; int r; @@ -1999,7 +1994,6 @@ static int parse_arguments(char **args) { static int read_config_files(char **args) { _cleanup_strv_free_ char **files = NULL; _cleanup_free_ char *p = NULL; - char **f; int r; r = conf_files_list_with_replacement(arg_root, CONF_PATHS_STRV("sysusers.d"), arg_replace, &files, &p); diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c index e9976540b..14ae873dc 100644 --- a/src/sysv-generator/sysv-generator.c +++ b/src/sysv-generator/sysv-generator.c @@ -103,7 +103,6 @@ static int generate_unit_file(SysvStub *s) { _cleanup_free_ char *path_escaped = NULL; _cleanup_fclose_ FILE *f = NULL; const char *unit; - char **p; int r; assert(s); @@ -707,7 +706,6 @@ static int acquire_search_path(const char *def, const char *envvar, char ***ret) static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { _cleanup_strv_free_ char **sysvinit_path = NULL; - char **path; int r; assert(lp); @@ -749,7 +747,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { if (hashmap_contains(all_services, name)) continue; - r = unit_file_exists(UNIT_FILE_SYSTEM, lp, name); + r = unit_file_exists(LOOKUP_SCOPE_SYSTEM, lp, name); if (r < 0 && !IN_SET(r, -ELOOP, -ERFKILL, -EADDRNOTAVAIL)) { log_debug_errno(r, "Failed to detect whether %s exists, skipping: %m", name); continue; @@ -791,7 +789,6 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic Set *runlevel_services[ELEMENTSOF(rcnd_table)] = {}; _cleanup_strv_free_ char **sysvrcnd_path = NULL; SysvStub *service; - char **p; int r; assert(lp); @@ -894,9 +891,9 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) assert_se(arg_dest = dest_late); - r = lookup_paths_init(&lp, UNIT_FILE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL); + r = lookup_paths_init_or_warn(&lp, LOOKUP_SCOPE_SYSTEM, LOOKUP_PATHS_EXCLUDE_GENERATED, NULL); if (r < 0) - return log_error_errno(r, "Failed to find lookup paths: %m"); + return r; all_services = hashmap_new(&string_hash_ops); if (!all_services) diff --git a/src/test/meson.build b/src/test/meson.build index 42d34a209..297a65d9a 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -11,7 +11,7 @@ test_hashmap_ordered_c = custom_target( test_include_dir = include_directories('.') -path = run_command(sh, '-c', 'echo "$PATH"').stdout().strip() +path = run_command(sh, '-c', 'echo "$PATH"', check: true).stdout().strip() test_env = environment() test_env.set('SYSTEMD_LANGUAGE_FALLBACK_MAP', language_fallback_map) test_env.set('PATH', project_build_root + ':' + path) @@ -41,11 +41,11 @@ test_dlopen_c = files('test-dlopen.c') ############################################################ tests += [ - [['src/test/test-device-nodes.c']], + [files('test-device-nodes.c')], - [['src/test/test-ether-addr-util.c']], + [files('test-ether-addr-util.c')], - [['src/test/test-engine.c'], + [files('test-engine.c'), [libcore, libshared], [threads, @@ -56,21 +56,21 @@ tests += [ libblkid], core_includes], - [['src/test/test-emergency-action.c'], + [files('test-emergency-action.c'), [libcore, libshared], [], core_includes], - [['src/test/test-chown-rec.c'], + [files('test-chown-rec.c'), [libcore, libshared], [], core_includes], - [['src/test/test-dlopen-so.c']], + [files('test-dlopen-so.c')], - [['src/test/test-job-type.c'], + [files('test-job-type.c'), [libcore, libshared], [threads, @@ -81,7 +81,7 @@ tests += [ libblkid], core_includes], - [['src/test/test-ns.c'], + [files('test-ns.c'), [libcore, libshared], [threads, @@ -92,7 +92,7 @@ tests += [ libblkid], core_includes, '', 'manual'], - [['src/test/test-loopback.c'], + [files('test-loopback.c'), [libcore, libshared], [threads, @@ -103,14 +103,14 @@ tests += [ libblkid], core_includes], - [['src/test/test-dns-domain.c']], + [files('test-dns-domain.c')], - [['src/test/test-boot-timestamps.c'], + [files('test-boot-timestamps.c'), [], [], [], 'ENABLE_EFI'], - [['src/test/test-unit-file.c']], + [files('test-unit-file.c')], - [['src/test/test-unit-name.c'], + [files('test-unit-name.c'), [libcore, libshared], [threads, @@ -121,7 +121,7 @@ tests += [ libblkid], core_includes], - [['src/test/test-load-fragment.c'], + [files('test-load-fragment.c'), [libcore, libshared], [threads, @@ -132,9 +132,9 @@ tests += [ libblkid], core_includes], - [['src/test/test-serialize.c']], + [files('test-serialize.c')], - [['src/test/test-unit-serialize.c'], + [files('test-unit-serialize.c'), [libcore, libshared], [threads, @@ -145,198 +145,198 @@ tests += [ libblkid], core_includes], - [['src/test/test-utf8.c']], + [files('test-utf8.c')], - [['src/test/test-kbd-util.c']], + [files('test-kbd-util.c')], - [['src/test/test-blockdev-util.c']], + [files('test-blockdev-util.c')], - [['src/test/test-dev-setup.c']], + [files('test-dev-setup.c')], - [['src/test/test-capability.c'], + [files('test-capability.c'), [], [libcap]], - [['src/test/test-async.c'], + [files('test-async.c'), [], [], [], '', 'timeout=120'], - [['src/test/test-locale-util.c']], + [files('test-locale-util.c')], - [['src/test/test-copy.c']], + [files('test-copy.c')], - [['src/test/test-recurse-dir.c']], + [files('test-recurse-dir.c')], - [['src/test/test-data-fd-util.c']], + [files('test-data-fd-util.c')], - [['src/test/test-static-destruct.c']], + [files('test-static-destruct.c')], - [['src/test/test-sigbus.c']], + [files('test-sigbus.c')], - [['src/test/test-condition.c']], + [files('test-condition.c')], - [['src/test/test-fdset.c']], + [files('test-fdset.c')], - [['src/test/test-fstab-util.c']], + [files('test-fstab-util.c')], - [['src/test/test-random-util.c'], + [files('test-random-util.c'), [], [libm], [], '', 'timeout=120'], - [['src/test/test-format-table.c']], + [files('test-format-table.c')], - [['src/test/test-format-util.c']], + [files('test-format-util.c')], - [['src/test/test-ratelimit.c']], + [files('test-ratelimit.c')], - [['src/test/test-util.c']], + [files('test-util.c')], - [['src/test/test-macro.c']], + [files('test-macro.c')], - [['src/test/test-json.c']], + [files('test-mkdir.c')], - [['src/test/test-mkdir.c']], + [files('test-json.c')], - [['src/test/test-modhex.c']], + [files('test-modhex.c')], - [['src/test/test-libmount.c'], + [files('test-libmount.c'), [], [threads, libmount]], - [['src/test/test-mount-util.c']], + [files('test-mount-util.c')], - [['src/test/test-mountpoint-util.c']], + [files('test-mountpoint-util.c')], - [['src/test/test-exec-util.c']], + [files('test-exec-util.c')], - [['src/test/test-hexdecoct.c']], + [files('test-hexdecoct.c')], - [['src/test/test-alloc-util.c']], + [files('test-alloc-util.c')], - [['src/test/test-xattr-util.c']], + [files('test-xattr-util.c')], - [['src/test/test-io-util.c']], + [files('test-io-util.c')], - [['src/test/test-glob-util.c']], + [files('test-glob-util.c')], - [['src/test/test-fs-util.c']], + [files('test-fs-util.c')], - [['src/test/test-install-file.c']], + [files('test-install-file.c')], - [['src/test/test-umask-util.c']], + [files('test-umask-util.c')], - [['src/test/test-proc-cmdline.c']], + [files('test-proc-cmdline.c')], - [['src/test/test-fd-util.c'], + [files('test-fd-util.c'), [], [libseccomp]], - [['src/test/test-web-util.c']], + [files('test-web-util.c')], - [['src/test/test-cpu-set-util.c']], + [files('test-cpu-set-util.c')], - [['src/test/test-stat-util.c']], + [files('test-stat-util.c')], - [['src/test/test-os-util.c']], + [files('test-os-util.c')], - [['src/test/test-libcrypt-util.c'], + [files('test-libcrypt-util.c'), [], [libcrypt], [], '', 'timeout=120'], - [['src/test/test-escape.c']], + [files('test-escape.c')], - [['src/test/test-exit-status.c']], + [files('test-exit-status.c')], - [['src/test/test-specifier.c']], + [files('test-specifier.c')], - [['src/test/test-string-util.c']], + [files('test-string-util.c')], - [['src/test/test-extract-word.c']], + [files('test-extract-word.c')], - [['src/test/test-parse-argument.c']], + [files('test-parse-argument.c')], - [['src/test/test-parse-socket-bind-item.c']], + [files('test-parse-socket-bind-item.c')], - [['src/test/test-parse-util.c']], + [files('test-parse-util.c')], - [['src/test/test-sysctl-util.c']], + [files('test-sysctl-util.c')], - [['src/test/test-import-util.c']], + [files('test-import-util.c')], - [['src/test/test-uid-alloc-range.c']], + [files('test-uid-alloc-range.c')], - [['src/test/test-user-util.c']], + [files('test-user-util.c')], - [['src/test/test-hostname-setup.c']], + [files('test-hostname-setup.c')], - [['src/test/test-hostname-util.c']], + [files('test-hostname-util.c')], - [['src/test/test-process-util.c']], + [files('test-process-util.c')], - [['src/test/test-terminal-util.c']], + [files('test-terminal-util.c')], - [['src/test/test-path-lookup.c']], + [files('test-path-lookup.c')], - [['src/test/test-pretty-print.c']], + [files('test-pretty-print.c')], - [['src/test/test-uid-range.c']], + [files('test-uid-range.c')], - [['src/test/test-cap-list.c', - generated_gperf_headers], + [files('test-cap-list.c') + + generated_gperf_headers, [], [libcap]], - [['src/test/test-socket-util.c']], + [files('test-socket-util.c')], - [['src/test/test-socket-netlink.c']], + [files('test-socket-netlink.c')], - [['src/test/test-in-addr-util.c']], + [files('test-in-addr-util.c')], - [['src/test/test-in-addr-prefix-util.c']], + [files('test-in-addr-prefix-util.c')], - [['src/test/test-barrier.c']], + [files('test-barrier.c')], - [['src/test/test-tmpfiles.c']], + [files('test-tmpfiles.c')], - [['src/test/test-namespace.c'], + [files('test-namespace.c'), [libcore, libshared], [threads, libblkid], core_includes], - [['src/test/test-verbs.c']], + [files('test-verbs.c')], - [['src/test/test-install-root.c']], + [files('test-install-root.c')], - [['src/test/test-acl-util.c'], + [files('test-acl-util.c'), [], [], [], 'HAVE_ACL'], - [['src/test/test-seccomp.c'], + [files('test-seccomp.c'), [], [libseccomp], [], 'HAVE_SECCOMP'], - [['src/test/test-rlimit-util.c']], + [files('test-rlimit-util.c')], - [['src/test/test-ask-password-api.c'], + [files('test-ask-password-api.c'), [], [], [], '', 'manual'], - [['src/test/test-signal-util.c']], + [files('test-signal-util.c')], - [['src/test/test-loop-block.c'], + [files('test-loop-block.c'), [libcore, libshared], [threads, libblkid], core_includes, '', '', [], false], - [['src/test/test-selinux.c']], + [files('test-selinux.c')], - [['src/test/test-sizeof.c'], + [files('test-sizeof.c'), [libbasic]], - [['src/test/test-bpf-devices.c'], + [files('test-bpf-devices.c'), [libcore, libshared], [libmount, @@ -347,7 +347,7 @@ tests += [ libblkid], core_includes], - [['src/test/test-bpf-firewall.c'], + [files('test-bpf-firewall.c'), [libcore, libshared], [libmount, @@ -358,13 +358,13 @@ tests += [ libblkid], core_includes], - [['src/test/test-bpf-foreign-programs.c'], + [files('test-bpf-foreign-programs.c'), [libcore, libshared], [], core_includes], - [['src/test/test-bpf-lsm.c'], + [files('test-bpf-lsm.c'), [libcore, libshared], [libmount, @@ -375,7 +375,7 @@ tests += [ libblkid], core_includes], - [['src/test/test-watch-pid.c'], + [files('test-watch-pid.c'), [libcore, libshared], [libmount, @@ -386,32 +386,32 @@ tests += [ libblkid], core_includes], - [['src/test/test-hashmap.c', - 'src/test/test-hashmap-plain.c', - test_hashmap_ordered_c], + [files('test-hashmap.c', + 'test-hashmap-plain.c') + + [test_hashmap_ordered_c], [], [], [], '', 'timeout=180'], - [['src/test/test-set.c']], + [files('test-set.c')], - [['src/test/test-ordered-set.c']], + [files('test-ordered-set.c')], - [['src/test/test-set-disable-mempool.c'], + [files('test-set-disable-mempool.c'), [], [threads]], - [['src/test/test-hash-funcs.c']], + [files('test-hash-funcs.c')], - [['src/test/test-bitmap.c']], + [files('test-bitmap.c')], - [['src/test/test-xml.c']], + [files('test-xml.c')], - [['src/test/test-list.c']], + [files('test-list.c')], - [['src/test/test-procfs-util.c']], + [files('test-procfs-util.c')], - [['src/test/test-unaligned.c']], + [files('test-unaligned.c')], - [['src/test/test-tables.c'], + [files('test-tables.c'), [libcore, libjournal_core, libudevd_core, @@ -421,73 +421,74 @@ tests += [ libmount, libxz, liblz4, - libblkid], + libblkid, + libselinux], [core_includes, journal_includes, udev_includes]], - [['src/test/test-prioq.c']], + [files('test-prioq.c')], - [['src/test/test-fileio.c']], + [files('test-fileio.c')], - [['src/test/test-time-util.c']], + [files('test-time-util.c')], - [['src/test/test-clock.c']], + [files('test-clock.c')], - [['src/test/test-tmpfile-util.c']], + [files('test-tmpfile-util.c')], - [['src/test/test-architecture.c']], + [files('test-architecture.c')], - [['src/test/test-gpt.c']], + [files('test-gpt.c')], - [['src/test/test-log.c']], + [files('test-log.c')], - [['src/test/test-ipcrm.c'], + [files('test-ipcrm.c'), [], [], [], '', 'unsafe'], - [['src/test/test-btrfs.c'], + [files('test-btrfs.c'), [], [], [], '', 'manual'], - [['src/test/test-firewall-util.c']], + [files('test-firewall-util.c')], - [['src/test/test-net-naming-scheme.c']], + [files('test-net-naming-scheme.c')], - [['src/test/test-netlink-manual.c'], + [files('test-netlink-manual.c'), [], [libkmod], [], 'HAVE_KMOD', 'manual'], - [['src/test/test-ellipsize.c']], + [files('test-ellipsize.c')], - [['src/test/test-date.c']], + [files('test-date.c')], - [['src/test/test-sleep.c']], + [files('test-sleep.c')], - [['src/test/test-tpm2.c']], + [files('test-tpm2.c')], - [['src/test/test-replace-var.c']], + [files('test-replace-var.c')], - [['src/test/test-calendarspec.c']], + [files('test-calendarspec.c')], - [['src/test/test-strip-tab-ansi.c']], + [files('test-strip-tab-ansi.c')], - [['src/test/test-coredump-util.c']], + [files('test-coredump-util.c')], - [['src/test/test-daemon.c']], + [files('test-daemon.c')], - [['src/test/test-cgroup.c']], + [files('test-cgroup.c')], - [['src/test/test-cgroup-cpu.c'], + [files('test-cgroup-cpu.c'), [libcore, libshared], [], core_includes], - [['src/test/test-cgroup-unit-default.c'], + [files('test-cgroup-unit-default.c'), [libcore, libshared], [], core_includes], - [['src/test/test-cgroup-mask.c'], + [files('test-cgroup-mask.c'), [libcore, libshared], [threads, @@ -498,30 +499,32 @@ tests += [ libblkid], core_includes], - [['src/test/test-varlink.c'], + [files('test-varlink.c'), [], [threads]], - [['src/test/test-cgroup-util.c']], + [files('test-cgroup-util.c')], - [['src/test/test-cgroup-setup.c']], + [files('test-cgroup-setup.c')], - [['src/test/test-env-file.c']], + [files('test-env-file.c')], - [['src/test/test-env-util.c']], + [files('test-env-util.c')], - [['src/test/test-strbuf.c']], + [files('test-strbuf.c')], - [['src/test/test-strv.c']], + [files('test-bootspec.c')], - [['src/test/test-path-util.c']], + [files('test-strv.c')], - [['src/test/test-rm-rf.c']], + [files('test-path-util.c')], - [['src/test/test-chase-symlinks.c'], + [files('test-rm-rf.c')], + + [files('test-chase-symlinks.c'), [], [], [], '', 'manual'], - [['src/test/test-path.c'], + [files('test-path.c'), [libcore, libshared], [threads, @@ -532,7 +535,7 @@ tests += [ libblkid], core_includes, '', 'timeout=120'], - [['src/test/test-execute.c'], + [files('test-execute.c'), [libcore, libshared], [threads, @@ -543,20 +546,20 @@ tests += [ libblkid], core_includes, '', 'timeout=360'], - [['src/test/test-siphash24.c']], + [files('test-siphash24.c')], - [['src/test/test-strxcpyx.c']], + [files('test-strxcpyx.c')], - [['src/test/test-install.c'], + [files('test-install.c'), [libcore, libshared], [], core_includes, '', 'manual'], - [['src/test/test-watchdog.c'], + [files('test-watchdog.c'), [], [], [], '', 'unsafe'], - [['src/test/test-sched-prio.c'], + [files('test-sched-prio.c'), [libcore, libshared], [threads, @@ -567,25 +570,25 @@ tests += [ libblkid], core_includes], - [['src/test/test-conf-files.c']], + [files('test-conf-files.c')], - [['src/test/test-conf-parser.c']], + [files('test-conf-parser.c')], - [['src/test/test-af-list.c', - generated_gperf_headers]], + [files('test-af-list.c') + + generated_gperf_headers], - [['src/test/test-arphrd-util.c', - generated_gperf_headers]], + [files('test-arphrd-util.c') + + generated_gperf_headers], - [['src/test/test-errno-list.c', - generated_gperf_headers]], + [files('test-errno-list.c') + + generated_gperf_headers], - [['src/test/test-ip-protocol-list.c', - shared_generated_gperf_headers]], + [files('test-ip-protocol-list.c') + + shared_generated_gperf_headers], - [['src/test/test-journal-importer.c']], + [files('test-journal-importer.c')], - [['src/test/test-udev.c'], + [files('test-udev.c'), [libudevd_core, libshared], [threads, @@ -596,49 +599,49 @@ tests += [ libselinux], udev_includes, '', 'manual'], - [['src/test/test-udev-util.c']], + [files('test-udev-util.c')], - [['src/test/test-id128.c']], + [files('test-id128.c')], - [['src/test/test-cryptolib.c'], + [files('test-cryptolib.c'), [libshared], [lib_openssl_or_gcrypt], [], 'HAVE_OPENSSL_OR_GCRYPT'], - [['src/test/test-nss-hosts.c', - 'src/test/nss-test-util.c', - 'src/test/nss-test-util.h'], + [files('test-nss-hosts.c', + 'nss-test-util.c', + 'nss-test-util.h'), [], [libdl], - [], 'ENABLE_NSS', 'manual'], + [], 'ENABLE_NSS', 'timeout=120'], - [['src/test/test-nss-users.c', - 'src/test/nss-test-util.c', - 'src/test/nss-test-util.h'], + [files('test-nss-users.c', + 'nss-test-util.c', + 'nss-test-util.h'), [], [libdl], - [], 'ENABLE_NSS', 'manual'], + [], 'ENABLE_NSS'], - [['src/test/test-bus-util.c']], + [files('test-bus-util.c')], - [['src/test/test-percent-util.c']], + [files('test-percent-util.c')], - [['src/test/test-sd-hwdb.c']], + [files('test-sd-hwdb.c')], - [['src/test/test-sd-path.c']], + [files('test-sd-path.c')], - [['src/test/test-local-addresses.c']], + [files('test-local-addresses.c')], - [['src/test/test-psi-util.c']], + [files('test-psi-util.c')], - [['src/test/test-qrcode-util.c'], + [files('test-qrcode-util.c'), [], [libdl]], - [['src/test/test-nscd-flush.c'], + [files('test-nscd-flush.c'), [], [], [], 'ENABLE_NSCD', 'manual'], - [['src/test/test-hmac.c']], + [files('test-hmac.c')], ] ############################################################ @@ -646,23 +649,23 @@ tests += [ # define some tests here, because the link_with deps were not defined earlier tests += [ - [['src/libsystemd/sd-bus/test-bus-error.c'], + [files('../libsystemd/sd-bus/test-bus-error.c'), [libshared_static, libsystemd_static]], - [['src/libsystemd/sd-device/test-sd-device-thread.c'], + [files('../libsystemd/sd-device/test-sd-device-thread.c'), [libsystemd], [threads]], - [['src/libudev/test-udev-device-thread.c'], + [files('../libudev/test-udev-device-thread.c'), [libudev], [threads]], ] tests += [ - [['src/test/test-socket-bind.c'], - [libcore, - libshared], + [files('test-socket-bind.c'), + [libcore, + libshared], [libdl], core_includes, 'BPF_FRAMEWORK'], diff --git a/src/test/test-barrier.c b/src/test/test-barrier.c index 8998282af..bbd7e2bdd 100644 --- a/src/test/test-barrier.c +++ b/src/test/test-barrier.c @@ -421,25 +421,27 @@ TEST_BARRIER(barrier_pending_exit, }), TEST_BARRIER_WAIT_SUCCESS(pid2)); -DEFINE_CUSTOM_TEST_MAIN( - LOG_INFO, - ({ - if (!slow_tests_enabled()) - return log_tests_skipped("slow tests are disabled"); - /* - * This test uses real-time alarms and sleeps to test for CPU races - * explicitly. This is highly fragile if your system is under load. We - * already increased the BASE_TIME value to make the tests more robust, - * but that just makes the test take significantly longer. Given the recent - * issues when running the test in a virtualized environments, limit it - * to bare metal machines only, to minimize false-positives in CIs. - */ - int v = detect_virtualization(); - if (IN_SET(v, -EPERM, -EACCES)) - return log_tests_skipped("Cannot detect virtualization"); +static int intro(void) { + if (!slow_tests_enabled()) + return log_tests_skipped("slow tests are disabled"); - if (v != VIRTUALIZATION_NONE) - return log_tests_skipped("This test requires a baremetal machine"); - }), - /* no outro */); + /* + * This test uses real-time alarms and sleeps to test for CPU races explicitly. This is highly + * fragile if your system is under load. We already increased the BASE_TIME value to make the tests + * more robust, but that just makes the test take significantly longer. Given the recent issues when + * running the test in a virtualized environments, limit it to bare metal machines only, to minimize + * false-positives in CIs. + */ + + int v = detect_virtualization(); + if (IN_SET(v, -EPERM, -EACCES)) + return log_tests_skipped("Cannot detect virtualization"); + + if (v != VIRTUALIZATION_NONE) + return log_tests_skipped("This test requires a baremetal machine"); + + return EXIT_SUCCESS; + } + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-bootspec.c b/src/test/test-bootspec.c new file mode 100644 index 000000000..46acd60e0 --- /dev/null +++ b/src/test/test-bootspec.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "bootspec.h" +#include "fileio.h" +#include "path-util.h" +#include "rm-rf.h" +#include "tests.h" +#include "tmpfile-util.h" + +TEST_RET(bootspec_sort) { + + static const struct { + const char *fname; + const char *contents; + } entries[] = { + { + .fname = "a-10.conf", + .contents = + "title A\n" + "version 10\n" + "machine-id dd235d00696545768f6f693bfd23b15f\n", + }, + { + .fname = "a-5.conf", + .contents = + "title A\n" + "version 5\n" + "machine-id dd235d00696545768f6f693bfd23b15f\n", + }, + { + .fname = "b.conf", + .contents = + "title B\n" + "version 3\n" + "machine-id b75451ad92f94feeab50b0b442768dbd\n", + }, + { + .fname = "c.conf", + .contents = + "title C\n" + "sort-key xxxx\n" + "version 5\n" + "machine-id 309de666fd5044268a9a26541ac93176\n", + }, + { + .fname = "cx.conf", + .contents = + "title C\n" + "sort-key xxxx\n" + "version 10\n" + "machine-id 309de666fd5044268a9a26541ac93176\n", + }, + { + .fname = "d.conf", + .contents = + "title D\n" + "sort-key kkkk\n" + "version 100\n" + "machine-id 81c6e3147cf544c19006af023e22b292\n", + }, + }; + + _cleanup_(rm_rf_physical_and_freep) char *d = NULL; + _cleanup_(boot_config_free) BootConfig config = BOOT_CONFIG_NULL; + + assert_se(mkdtemp_malloc("/tmp/bootspec-testXXXXXX", &d) >= 0); + + for (size_t i = 0; i < ELEMENTSOF(entries); i++) { + _cleanup_free_ char *j = NULL; + + j = path_join(d, "/loader/entries/", entries[i].fname); + assert_se(j); + + assert_se(write_string_file(j, entries[i].contents, WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); + } + + assert_se(boot_config_load(&config, d, NULL) >= 0); + + assert_se(config.n_entries == 6); + + /* First, because has sort key, and its the lowest one */ + assert_se(streq(config.entries[0].id, "d.conf")); + + /* These two have a sort key, and newest must be first */ + assert_se(streq(config.entries[1].id, "cx.conf")); + assert_se(streq(config.entries[2].id, "c.conf")); + + /* The following ones have no sort key, hence order by version compared ids, lowest first */ + assert_se(streq(config.entries[3].id, "b.conf")); + assert_se(streq(config.entries[4].id, "a-10.conf")); + assert_se(streq(config.entries[5].id, "a-5.conf")); + + return 0; +} + +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/test/test-bpf-devices.c b/src/test/test-bpf-devices.c index bbaa7b360..587591cf0 100644 --- a/src/test/test-bpf-devices.c +++ b/src/test/test-bpf-devices.c @@ -30,7 +30,6 @@ static void test_policy_closed(const char *cgroup_path, BPFProgram **installed_p r = bpf_devices_apply_policy(&prog, CGROUP_DEVICE_POLICY_CLOSED, true, cgroup_path, installed_prog); assert_se(r >= 0); - const char *s; FOREACH_STRING(s, "/dev/null", "/dev/zero", "/dev/full", diff --git a/src/test/test-bpf-firewall.c b/src/test/test-bpf-firewall.c index 2e19db600..8f4b48507 100644 --- a/src/test/test-bpf-firewall.c +++ b/src/test/test-bpf-firewall.c @@ -55,7 +55,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, &p); + r = bpf_program_new(BPF_PROG_TYPE_CGROUP_SKB, "sd_trivial", &p); assert_se(r == 0); r = bpf_program_add_instructions(p, exit_insn, ELEMENTSOF(exit_insn)); @@ -97,7 +97,7 @@ int main(int argc, char *argv[]) { /* The simple tests succeeded. Now let's try full unit-based use-case. */ - assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); assert_se(u = unit_new(m, sizeof(Service))); diff --git a/src/test/test-bpf-foreign-programs.c b/src/test/test-bpf-foreign-programs.c index 1765dc7a9..061426f28 100644 --- a/src/test/test-bpf-foreign-programs.c +++ b/src/test/test-bpf-foreign-programs.c @@ -133,8 +133,6 @@ static int bpf_foreign_test_to_string(enum bpf_attach_type attach_type, const ch } static char **unlink_paths_and_free(char **paths) { - char **i; - STRV_FOREACH(i, paths) (void) unlink(*i); @@ -162,7 +160,7 @@ static int pin_programs(Unit *u, CGroupContext *cc, const Test *test_suite, size if (r < 0) return log_error_errno(r, "Failed to convert program to string"); - r = bpf_program_new(test_suite[i].prog_type, &prog); + r = bpf_program_new(test_suite[i].prog_type, "sd_trivial", &prog); if (r < 0) return log_error_errno(r, "Failed to create program '%s'", str); @@ -289,11 +287,11 @@ int main(int argc, char *argv[]) { (void) setrlimit_closest(RLIMIT_MEMLOCK, &rl); if (!can_memlock()) - return log_tests_skipped("Can't use mlock(), skipping."); + return log_tests_skipped("Can't use mlock()"); r = cg_all_unified(); if (r <= 0) - return log_tests_skipped("Unified hierarchy is required, skipping."); + return log_tests_skipped("Unified hierarchy is required"); r = enter_cgroup_subroot(NULL); if (r == -ENOMEDIUM) @@ -303,7 +301,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); assert_se(test_bpf_cgroup_programs(m, diff --git a/src/test/test-bpf-lsm.c b/src/test/test-bpf-lsm.c index 258c2e575..e0e1b7f38 100644 --- a/src/test/test-bpf-lsm.c +++ b/src/test/test-bpf-lsm.c @@ -16,7 +16,6 @@ static int test_restrict_filesystems(Manager *m, const char *unit_name, const ch _cleanup_free_ char *exec_start = NULL; _cleanup_(unit_freep) Unit *u = NULL; ExecContext *ec = NULL; - char **allow_filesystem; int cld_code, r; assert_se(u = unit_new(m, sizeof(Service))); @@ -51,13 +50,11 @@ static int test_restrict_filesystems(Manager *m, const char *unit_name, const ch } cld_code = SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.code; - if (cld_code != CLD_EXITED) { + if (cld_code != CLD_EXITED) return log_error_errno(-SYNTHETIC_ERRNO(EBUSY), "ExecStart didn't exited, code='%s'", sigchld_code_to_string(cld_code)); - } - if (SERVICE(u)->state != SERVICE_DEAD) { + if (SERVICE(u)->state != SERVICE_DEAD) return log_error_errno(-SYNTHETIC_ERRNO(EBUSY), "Service is not dead"); - } return 0; } @@ -79,7 +76,7 @@ int main(int argc, char *argv[]) { (void) setrlimit_closest(RLIMIT_MEMLOCK, &rl); if (!can_memlock()) - return log_tests_skipped("Can't use mlock(), skipping."); + return log_tests_skipped("Can't use mlock()"); r = lsm_bpf_supported(); if (r <= 0) @@ -93,7 +90,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - assert_se(manager_new(UNIT_FILE_SYSTEM, MANAGER_TEST_RUN_BASIC, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_SYSTEM, MANAGER_TEST_RUN_BASIC, &m) >= 0); assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); /* We need to enable access to the filesystem where the binary is so we diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c index 6058f32b0..57483f72c 100644 --- a/src/test/test-cgroup-mask.c +++ b/src/test/test-cgroup-mask.c @@ -42,7 +42,7 @@ TEST_RET(cgroup_mask, .sd_booted = true) { assert_se(get_testdata_dir("units", &unit_dir) >= 0); assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m); if (IN_SET(r, -EPERM, -EACCES)) { log_error_errno(r, "manager_new: %m"); return log_tests_skipped("cannot create manager"); diff --git a/src/test/test-cgroup-setup.c b/src/test/test-cgroup-setup.c index 018992f96..c377ff0a0 100644 --- a/src/test/test-cgroup-setup.c +++ b/src/test/test-cgroup-setup.c @@ -64,10 +64,11 @@ TEST(is_wanted) { test_is_wanted_print_one(false); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_DEBUG, - ({ - if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) - return log_tests_skipped("can't read /proc/cmdline"); - }), - /* no outro */); +static int intro(void) { + if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) + return log_tests_skipped("can't read /proc/cmdline"); + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/test/test-cgroup-unit-default.c b/src/test/test-cgroup-unit-default.c index 4c8de1e38..94201a3cc 100644 --- a/src/test/test-cgroup-unit-default.c +++ b/src/test/test-cgroup-unit-default.c @@ -26,7 +26,7 @@ TEST_RET(default_memory_low, .sd_booted = true) { assert_se(get_testdata_dir("units", &unit_dir) >= 0); assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m); if (IN_SET(r, -EPERM, -EACCES)) { log_error_errno(r, "manager_new: %m"); return log_tests_skipped("cannot create manager"); diff --git a/src/test/test-cgroup-util.c b/src/test/test-cgroup-util.c index 93b114b3f..7113b07a9 100644 --- a/src/test/test-cgroup-util.c +++ b/src/test/test-cgroup-util.c @@ -426,4 +426,13 @@ TEST(cg_get_keyed_attribute) { } } +TEST(bfq_weight_conversion) { + assert_se(BFQ_WEIGHT(1) == 1); + assert_se(BFQ_WEIGHT(50) == 50); + assert_se(BFQ_WEIGHT(100) == 100); + assert_se(BFQ_WEIGHT(500) == 136); + assert_se(BFQ_WEIGHT(5000) == 545); + assert_se(BFQ_WEIGHT(10000) == 1000); +} + DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/test/test-chown-rec.c b/src/test/test-chown-rec.c index 53d44566d..97711f58b 100644 --- a/src/test/test-chown-rec.c +++ b/src/test/test-chown-rec.c @@ -149,10 +149,11 @@ TEST(chown_recursive) { assert_se(!has_xattr(p)); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_DEBUG, - ({ - if (geteuid() != 0) - return log_tests_skipped("not running as root"); - }), - /* no outro */); +static int intro(void) { + if (geteuid() != 0) + return log_tests_skipped("not running as root"); + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/test/test-copy.c b/src/test/test-copy.c index c7ed05420..a669df644 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -83,7 +83,6 @@ TEST(copy_tree) { char **hardlinks = STRV_MAKE("hlink", "file", "hlink2", "dir1/file"); const char *unixsockp; - char **p, **ll; struct stat st; int xattr_worked = -1; /* xattr support is optional in temporary directories, hence use it if we can, * but don't fail if we can't */ @@ -323,4 +322,60 @@ TEST(copy_proc) { assert_se(!isempty(a)); } +TEST_RET(copy_holes) { + char fn[] = "/var/tmp/test-copy-hole-fd-XXXXXX"; + char fn_copy[] = "/var/tmp/test-copy-hole-fd-XXXXXX"; + struct stat stat; + off_t blksz; + int r, fd, fd_copy; + char *buf; + + fd = mkostemp_safe(fn); + assert_se(fd >= 0); + + fd_copy = mkostemp_safe(fn_copy); + assert_se(fd_copy >= 0); + + r = RET_NERRNO(fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, 1)); + if (ERRNO_IS_NOT_SUPPORTED(r)) + return log_tests_skipped("Filesystem doesn't support hole punching"); + assert_se(r >= 0); + + assert_se(fstat(fd, &stat) >= 0); + blksz = stat.st_blksize; + buf = alloca_safe(blksz); + memset(buf, 1, blksz); + + /* We need to make sure to create hole in multiples of the block size, otherwise filesystems (btrfs) + * might silently truncate/extend the holes. */ + + assert_se(lseek(fd, blksz, SEEK_CUR) >= 0); + assert_se(write(fd, buf, blksz) >= 0); + assert_se(lseek(fd, 0, SEEK_END) == 2 * blksz); + /* Only ftruncate() can create holes at the end of a file. */ + assert_se(ftruncate(fd, 3 * blksz) >= 0); + assert_se(lseek(fd, 0, SEEK_SET) >= 0); + + assert_se(copy_bytes(fd, fd_copy, UINT64_MAX, COPY_HOLES) >= 0); + + /* Test that the hole starts at the beginning of the file. */ + assert_se(lseek(fd_copy, 0, SEEK_HOLE) == 0); + /* Test that the hole has the expected size. */ + assert_se(lseek(fd_copy, 0, SEEK_DATA) == blksz); + assert_se(lseek(fd_copy, blksz, SEEK_HOLE) == 2 * blksz); + assert_se(lseek(fd_copy, 2 * blksz, SEEK_DATA) < 0 && errno == ENXIO); + + /* Test that the copied file has the correct size. */ + assert_se(fstat(fd_copy, &stat) >= 0); + assert_se(stat.st_size == 3 * blksz); + + close(fd); + close(fd_copy); + + unlink(fn); + unlink(fn_copy); + + return 0; +} + DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/test/test-date.c b/src/test/test-date.c index 355f4e17d..b3b902e64 100644 --- a/src/test/test-date.c +++ b/src/test/test-date.c @@ -73,6 +73,8 @@ int main(int argc, char *argv[]) { test_one("12-10-03 12:13"); test_one("2012-12-30 18:42"); test_one("2012-10-02"); + test_one("Mar 12 12:01:01"); + test_one("Mar 12 12:01:01.687197"); test_one("Tue 2012-10-02"); test_one("yesterday"); test_one("today"); diff --git a/src/test/test-ellipsize.c b/src/test/test-ellipsize.c index b840355f5..731719336 100644 --- a/src/test/test-ellipsize.c +++ b/src/test/test-ellipsize.c @@ -64,18 +64,14 @@ static void test_ellipsize_mem_one(const char *s, size_t old_length, size_t new_ } TEST(ellipsize_mem) { - const char *s; - ssize_t l, k; - FOREACH_STRING(s, "_XXXXXXXXXXX_", /* ASCII */ "_aąęółśćńżźć_", /* two-byte utf-8 */ "გამარჯობა", /* multi-byte utf-8 */ "你好世界", /* wide characters */ "你გą世óoó界") /* a mix */ - - for (l = strlen(s); l >= 0; l--) - for (k = strlen(s) + 1; k >= 0; k--) + for (ssize_t l = strlen(s); l >= 0; l--) + for (ssize_t k = strlen(s) + 1; k >= 0; k--) test_ellipsize_mem_one(s, l, k); } diff --git a/src/test/test-engine.c b/src/test/test-engine.c index 673c66561..ef055360f 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -93,7 +93,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m); if (manager_errno_skip_test(r)) return log_tests_skipped_errno(r, "manager_new"); assert_se(r >= 0); @@ -192,17 +192,17 @@ int main(int argc, char *argv[]) { assert_se(unit_add_dependency(a, UNIT_PROPAGATES_RELOAD_TO, b, true, UNIT_DEPENDENCY_UDEV) == 0); assert_se(unit_add_dependency(a, UNIT_PROPAGATES_RELOAD_TO, c, true, UNIT_DEPENDENCY_PROC_SWAP) == 0); - assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b)); - assert_se(hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a)); - assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c)); - assert_se(hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a)); + assert_se( hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b)); + assert_se( hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a)); + assert_se( hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c)); + assert_se( hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a)); unit_remove_dependencies(a, UNIT_DEPENDENCY_UDEV); assert_se(!hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), b)); assert_se(!hashmap_get(unit_get_dependencies(b, UNIT_RELOAD_PROPAGATED_FROM), a)); - assert_se(hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c)); - assert_se(hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a)); + assert_se( hashmap_get(unit_get_dependencies(a, UNIT_PROPAGATES_RELOAD_TO), c)); + assert_se( hashmap_get(unit_get_dependencies(c, UNIT_RELOAD_PROPAGATED_FROM), a)); unit_remove_dependencies(a, UNIT_DEPENDENCY_PROC_SWAP); @@ -223,6 +223,7 @@ int main(int argc, char *argv[]) { assert_se(unit_add_dependency_by_name(stub, UNIT_AFTER, SPECIAL_ROOT_SLICE, true, UNIT_DEPENDENCY_FILE) >= 0); assert_se(unit_add_dependency_by_name(stub, UNIT_REQUIRES, "non-existing.mount", true, UNIT_DEPENDENCY_FILE) >= 0); assert_se(unit_add_dependency_by_name(stub, UNIT_ON_FAILURE, "non-existing-on-failure.target", true, UNIT_DEPENDENCY_FILE) >= 0); + assert_se(unit_add_dependency_by_name(stub, UNIT_ON_SUCCESS, "non-existing-on-success.target", true, UNIT_DEPENDENCY_FILE) >= 0); log_info("/* Merging a+stub, dumps before */"); unit_dump(a, stderr, NULL); @@ -231,13 +232,19 @@ int main(int argc, char *argv[]) { log_info("/* Dump of merged a+stub */"); unit_dump(a, stderr, NULL); - assert_se(unit_has_dependency(a, UNIT_ATOM_AFTER, manager_get_unit(m, SPECIAL_BASIC_TARGET))); - assert_se(unit_has_dependency(a, UNIT_ATOM_AFTER, manager_get_unit(m, "quux.target"))); - assert_se(unit_has_dependency(a, UNIT_ATOM_AFTER, manager_get_unit(m, SPECIAL_ROOT_SLICE))); - assert_se(unit_has_dependency(a, UNIT_ATOM_PULL_IN_START, manager_get_unit(m, "non-existing.mount"))); - assert_se(unit_has_dependency(a, UNIT_ATOM_RETROACTIVE_START_REPLACE, manager_get_unit(m, "non-existing.mount"))); - assert_se(unit_has_dependency(a, UNIT_ATOM_ON_FAILURE, manager_get_unit(m, "non-existing-on-failure.target"))); + assert_se( unit_has_dependency(a, UNIT_ATOM_AFTER, manager_get_unit(m, SPECIAL_BASIC_TARGET))); + assert_se( unit_has_dependency(a, UNIT_ATOM_AFTER, manager_get_unit(m, "quux.target"))); + assert_se( unit_has_dependency(a, UNIT_ATOM_AFTER, manager_get_unit(m, SPECIAL_ROOT_SLICE))); + assert_se( unit_has_dependency(a, UNIT_ATOM_PULL_IN_START, manager_get_unit(m, "non-existing.mount"))); + assert_se( unit_has_dependency(a, UNIT_ATOM_RETROACTIVE_START_REPLACE, manager_get_unit(m, "non-existing.mount"))); + assert_se( unit_has_dependency(a, UNIT_ATOM_ON_FAILURE, manager_get_unit(m, "non-existing-on-failure.target"))); + assert_se( unit_has_dependency(manager_get_unit(m, "non-existing-on-failure.target"), UNIT_ATOM_ON_FAILURE_OF, a)); + assert_se( unit_has_dependency(a, UNIT_ATOM_ON_SUCCESS, manager_get_unit(m, "non-existing-on-success.target"))); + assert_se( unit_has_dependency(manager_get_unit(m, "non-existing-on-success.target"), UNIT_ATOM_ON_SUCCESS_OF, a)); assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_FAILURE, manager_get_unit(m, "basic.target"))); + assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_SUCCESS, manager_get_unit(m, "basic.target"))); + assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_FAILURE_OF, manager_get_unit(m, "basic.target"))); + assert_se(!unit_has_dependency(a, UNIT_ATOM_ON_SUCCESS_OF, manager_get_unit(m, "basic.target"))); assert_se(!unit_has_dependency(a, UNIT_ATOM_PROPAGATES_RELOAD_TO, manager_get_unit(m, "non-existing-on-failure.target"))); assert_se(unit_has_name(a, "a.service")); @@ -275,19 +282,19 @@ int main(int argc, char *argv[]) { assert_se(UNIT_GET_SLICE(tomato) == zupa); assert_se(!unit_has_dependency(tomato, UNIT_ATOM_IN_SLICE, sauce)); assert_se(!unit_has_dependency(tomato, UNIT_ATOM_IN_SLICE, fruit)); - assert_se(unit_has_dependency(tomato, UNIT_ATOM_IN_SLICE, zupa)); + assert_se( unit_has_dependency(tomato, UNIT_ATOM_IN_SLICE, zupa)); assert_se(!unit_has_dependency(tomato, UNIT_ATOM_REFERENCES, sauce)); assert_se(!unit_has_dependency(tomato, UNIT_ATOM_REFERENCES, fruit)); - assert_se(unit_has_dependency(tomato, UNIT_ATOM_REFERENCES, zupa)); + assert_se( unit_has_dependency(tomato, UNIT_ATOM_REFERENCES, zupa)); assert_se(!unit_has_dependency(sauce, UNIT_ATOM_SLICE_OF, tomato)); assert_se(!unit_has_dependency(fruit, UNIT_ATOM_SLICE_OF, tomato)); - assert_se(unit_has_dependency(zupa, UNIT_ATOM_SLICE_OF, tomato)); + assert_se( unit_has_dependency(zupa, UNIT_ATOM_SLICE_OF, tomato)); assert_se(!unit_has_dependency(sauce, UNIT_ATOM_REFERENCED_BY, tomato)); assert_se(!unit_has_dependency(fruit, UNIT_ATOM_REFERENCED_BY, tomato)); - assert_se(unit_has_dependency(zupa, UNIT_ATOM_REFERENCED_BY, tomato)); + assert_se( unit_has_dependency(zupa, UNIT_ATOM_REFERENCED_BY, tomato)); return 0; } diff --git a/src/test/test-env-file.c b/src/test/test-env-file.c index 6cc2455c1..461a0f081 100644 --- a/src/test/test-env-file.c +++ b/src/test/test-env-file.c @@ -9,15 +9,20 @@ #include "tests.h" #include "tmpfile-util.h" +/* In case of repeating keys, later entries win. */ + #define env_file_1 \ + "a=a\n" \ + "a=b\n" \ + "a=b\n" \ "a=a\n" \ "b=b\\\n" \ "c\n" \ - "d=d\\\n" \ - "e\\\n" \ - "f\n" \ + "d= d\\\n" \ + "e \\\n" \ + "f \n" \ "g=g\\ \n" \ - "h=h\n" \ + "h= ąęół\\ śćńźżµ \n" \ "i=i\\" #define env_file_2 \ @@ -26,89 +31,74 @@ #define env_file_3 \ "#SPAMD_ARGS=\"-d --socketpath=/var/lib/bulwark/spamd \\\n" \ "#--nouser-config \\\n" \ - "normal=line" + "normal=line \\\n" \ + ";normal=ignored \\\n" \ + "normal_ignored \\\n" \ + "normal ignored \\\n" -#define env_file_4 \ - "# Generated\n" \ - "\n" \ - "HWMON_MODULES=\"coretemp f71882fg\"\n" \ - "\n" \ - "# For compatibility reasons\n" \ - "\n" \ - "MODULE_0=coretemp\n" \ - "MODULE_1=f71882fg" +#define env_file_4 \ + "# Generated\n" \ + "\n" \ + "HWMON_MODULES=\"coretemp f71882fg\"\n" \ + "\n" \ + "# For compatibility reasons\n" \ + "\n" \ + "MODULE_0=coretemp\n" \ + "MODULE_1=f71882fg" #define env_file_5 \ - "a=\n" \ + "a=\n" \ "b=" +#define env_file_6 \ + "a=\\ \\n \\t \\x \\y \\' \n" \ + "b= \\$' \n" \ + "c= ' \\n\\t\\$\\`\\\\\n" \ + "' \n" \ + "d= \" \\n\\t\\$\\`\\\\\n" \ + "\" \n" + + TEST(load_env_file_1) { - _cleanup_strv_free_ char **data = NULL; - int r; - _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; - _cleanup_close_ int fd; + assert_se(write_tmpfile(name, env_file_1) == 0); - fd = mkostemp_safe(name); - assert_se(fd >= 0); - assert_se(write(fd, env_file_1, strlen(env_file_1)) == strlen(env_file_1)); - - r = load_env_file(NULL, name, &data); - assert_se(r == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); assert_se(streq(data[0], "a=a")); assert_se(streq(data[1], "b=bc")); - assert_se(streq(data[2], "d=def")); + assert_se(streq(data[2], "d=de f")); assert_se(streq(data[3], "g=g ")); - assert_se(streq(data[4], "h=h")); + assert_se(streq(data[4], "h=ąęół śćńźżµ")); assert_se(streq(data[5], "i=i")); assert_se(data[6] == NULL); } TEST(load_env_file_2) { - _cleanup_strv_free_ char **data = NULL; - int r; - _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; - _cleanup_close_ int fd; + assert_se(write_tmpfile(name, env_file_2) == 0); - fd = mkostemp_safe(name); - assert_se(fd >= 0); - assert_se(write(fd, env_file_2, strlen(env_file_2)) == strlen(env_file_2)); - - r = load_env_file(NULL, name, &data); - assert_se(r == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); assert_se(streq(data[0], "a=a")); assert_se(data[1] == NULL); } TEST(load_env_file_3) { - _cleanup_strv_free_ char **data = NULL; - int r; - _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; - _cleanup_close_ int fd; + assert_se(write_tmpfile(name, env_file_3) == 0); - fd = mkostemp_safe(name); - assert_se(fd >= 0); - assert_se(write(fd, env_file_3, strlen(env_file_3)) == strlen(env_file_3)); - - r = load_env_file(NULL, name, &data); - assert_se(r == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); assert_se(data == NULL); } TEST(load_env_file_4) { - _cleanup_strv_free_ char **data = NULL; _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; - _cleanup_close_ int fd; - int r; + assert_se(write_tmpfile(name, env_file_4) == 0); - fd = mkostemp_safe(name); - assert_se(fd >= 0); - assert_se(write(fd, env_file_4, strlen(env_file_4)) == strlen(env_file_4)); - - r = load_env_file(NULL, name, &data); - assert_se(r == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); assert_se(streq(data[0], "HWMON_MODULES=coretemp f71882fg")); assert_se(streq(data[1], "MODULE_0=coretemp")); assert_se(streq(data[2], "MODULE_1=f71882fg")); @@ -116,26 +106,30 @@ TEST(load_env_file_4) { } TEST(load_env_file_5) { - _cleanup_strv_free_ char **data = NULL; - int r; - _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; - _cleanup_close_ int fd; + assert_se(write_tmpfile(name, env_file_5) == 0); - fd = mkostemp_safe(name); - assert_se(fd >= 0); - assert_se(write(fd, env_file_5, strlen(env_file_5)) == strlen(env_file_5)); - - r = load_env_file(NULL, name, &data); - assert_se(r == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); assert_se(streq(data[0], "a=")); assert_se(streq(data[1], "b=")); assert_se(data[2] == NULL); } -TEST(write_and_load_env_file) { - const char *v; +TEST(load_env_file_6) { + _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-load-env-file.XXXXXX"; + assert_se(write_tmpfile(name, env_file_6) == 0); + _cleanup_strv_free_ char **data = NULL; + assert_se(load_env_file(NULL, name, &data) == 0); + assert_se(streq(data[0], "a= n t x y '")); + assert_se(streq(data[1], "b=$'")); + assert_se(streq(data[2], "c= \\n\\t\\$\\`\\\\\n")); + assert_se(streq(data[3], "d= \\n\\t$`\\\n")); + assert_se(data[4] == NULL); +} + +TEST(write_and_load_env_file) { /* Make sure that our writer, parser and the shell agree on what our env var files mean */ FOREACH_STRING(v, diff --git a/src/test/test-env-util.c b/src/test/test-env-util.c index 19523aa0d..cc37d9632 100644 --- a/src/test/test-env-util.c +++ b/src/test/test-env-util.c @@ -406,17 +406,16 @@ TEST(setenv_systemd_exec_pid) { assert_se(set_unset_env("SYSTEMD_EXEC_PID", saved, 1) >= 0); } -TEST(unsetenv_erase) { +TEST(getenv_steal_erase) { int r; - r = safe_fork("(sd-unsetenverase)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(sd-getenvstealerase)", FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); if (r == 0) { _cleanup_strv_free_ char **l = NULL; - char **e; /* child */ - assert_se(unsetenv_erase("thisenvvardefinitelywontexist") == 0); + assert_se(getenv_steal_erase("thisenvvardefinitelywontexist", NULL) == 0); l = strv_new("FOO=BAR", "QUUX=PIFF", "ONE=TWO", "A=B"); assert_se(strv_length(l) == 4); @@ -424,7 +423,7 @@ TEST(unsetenv_erase) { environ = l; STRV_FOREACH(e, environ) { - _cleanup_free_ char *n = NULL; + _cleanup_free_ char *n = NULL, *copy1 = NULL, *copy2 = NULL; char *eq; eq = strchr(*e, '='); @@ -434,9 +433,13 @@ TEST(unsetenv_erase) { n = strndup(*e, eq - *e); assert_se(n); - assert_se(streq_ptr(getenv(n), eq + 1)); + copy1 = strdup(eq + 1); + assert_se(copy1); + + assert_se(streq_ptr(getenv(n), copy1)); assert_se(getenv(n) == eq + 1); - assert_se(unsetenv_erase(n) > 0); + assert_se(getenv_steal_erase(n, ©2) > 0); + assert_se(streq_ptr(copy1, copy2)); assert_se(isempty(eq + 1)); assert_se(!getenv(n)); } diff --git a/src/test/test-exec-util.c b/src/test/test-exec-util.c index 47a82eb96..e8b04e879 100644 --- a/src/test/test-exec-util.c +++ b/src/test/test-exec-util.c @@ -20,6 +20,7 @@ #include "string-util.h" #include "strv.h" #include "tests.h" +#include "tmpfile-util.h" static int here = 0, here2 = 0, here3 = 0; static void *ignore_stdout_args[] = { &here, &here2, &here3 }; @@ -54,9 +55,7 @@ static const gather_stdout_callback_t ignore_stdout[] = { }; static void test_execute_directory_one(bool gather_stdout) { - char template_lo[] = "/tmp/test-exec-util.lo.XXXXXXX"; - char template_hi[] = "/tmp/test-exec-util.hi.XXXXXXX"; - const char * dirs[] = {template_hi, template_lo, NULL}; + _cleanup_(rm_rf_physical_and_freep) char *tmp_lo = NULL, *tmp_hi = NULL; const char *name, *name2, *name3, *overridden, *override, *masked, *mask, @@ -65,20 +64,22 @@ static void test_execute_directory_one(bool gather_stdout) { log_info("/* %s (%s) */", __func__, gather_stdout ? "gathering stdout" : "asynchronous"); - assert_se(mkdtemp(template_lo)); - assert_se(mkdtemp(template_hi)); + assert_se(mkdtemp_malloc("/tmp/test-exec-util.lo.XXXXXXX", &tmp_lo) >= 0); + assert_se(mkdtemp_malloc("/tmp/test-exec-util.hi.XXXXXXX", &tmp_hi) >= 0); - name = strjoina(template_lo, "/script"); - name2 = strjoina(template_hi, "/script2"); - name3 = strjoina(template_lo, "/useless"); - overridden = strjoina(template_lo, "/overridden"); - override = strjoina(template_hi, "/overridden"); - masked = strjoina(template_lo, "/masked"); - mask = strjoina(template_hi, "/masked"); - masked2 = strjoina(template_lo, "/masked2"); - mask2 = strjoina(template_hi, "/masked2"); - masked2e = strjoina(template_lo, "/masked2e"); - mask2e = strjoina(template_hi, "/masked2e"); + const char * dirs[] = { tmp_hi, tmp_lo, NULL }; + + name = strjoina(tmp_lo, "/script"); + name2 = strjoina(tmp_hi, "/script2"); + name3 = strjoina(tmp_lo, "/useless"); + overridden = strjoina(tmp_lo, "/overridden"); + override = strjoina(tmp_hi, "/overridden"); + masked = strjoina(tmp_lo, "/masked"); + mask = strjoina(tmp_hi, "/masked"); + masked2 = strjoina(tmp_lo, "/masked2"); + mask2 = strjoina(tmp_hi, "/masked2"); + masked2e = strjoina(tmp_lo, "/masked2e"); + mask2e = strjoina(tmp_hi, "/masked2e"); assert_se(write_string_file(name, "#!/bin/sh\necho 'Executing '$0\ntouch $(dirname $0)/it_works", @@ -123,16 +124,13 @@ static void test_execute_directory_one(bool gather_stdout) { else execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); - assert_se(chdir(template_lo) == 0); + assert_se(chdir(tmp_lo) == 0); assert_se(access("it_works", F_OK) >= 0); assert_se(access("failed", F_OK) < 0); - assert_se(chdir(template_hi) == 0); + assert_se(chdir(tmp_hi) == 0); assert_se(access("it_works2", F_OK) >= 0); assert_se(access("failed", F_OK) < 0); - - (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL); - (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL); } TEST(execute_directory) { @@ -141,28 +139,28 @@ TEST(execute_directory) { } TEST(execution_order) { - char template_lo[] = "/tmp/test-exec-util-lo.XXXXXXX"; - char template_hi[] = "/tmp/test-exec-util-hi.XXXXXXX"; - const char *dirs[] = {template_hi, template_lo, NULL}; + _cleanup_(rm_rf_physical_and_freep) char *tmp_lo = NULL, *tmp_hi = NULL; const char *name, *name2, *name3, *overridden, *override, *masked, *mask; const char *output, *t; _cleanup_free_ char *contents = NULL; - assert_se(mkdtemp(template_lo)); - assert_se(mkdtemp(template_hi)); + assert_se(mkdtemp_malloc("/tmp/test-exec-util-lo.XXXXXXX", &tmp_lo) >= 0); + assert_se(mkdtemp_malloc("/tmp/test-exec-util-hi.XXXXXXX", &tmp_hi) >= 0); - output = strjoina(template_hi, "/output"); + const char *dirs[] = { tmp_hi, tmp_lo, NULL }; + + output = strjoina(tmp_hi, "/output"); log_info("/* %s >>%s */", __func__, output); /* write files in "random" order */ - name2 = strjoina(template_lo, "/90-bar"); - name = strjoina(template_hi, "/80-foo"); - name3 = strjoina(template_lo, "/last"); - overridden = strjoina(template_lo, "/30-override"); - override = strjoina(template_hi, "/30-override"); - masked = strjoina(template_lo, "/10-masked"); - mask = strjoina(template_hi, "/10-masked"); + name2 = strjoina(tmp_lo, "/90-bar"); + name = strjoina(tmp_hi, "/80-foo"); + name3 = strjoina(tmp_lo, "/last"); + overridden = strjoina(tmp_lo, "/30-override"); + override = strjoina(tmp_hi, "/30-override"); + masked = strjoina(tmp_lo, "/10-masked"); + mask = strjoina(tmp_hi, "/10-masked"); t = strjoina("#!/bin/sh\necho $(basename $0) >>", output); assert_se(write_string_file(name, t, WRITE_STRING_FILE_CREATE) == 0); @@ -198,9 +196,6 @@ TEST(execution_order) { assert_se(read_full_file(output, &contents, NULL) >= 0); assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n")); - - (void) rm_rf(template_lo, REMOVE_ROOT|REMOVE_PHYSICAL); - (void) rm_rf(template_hi, REMOVE_ROOT|REMOVE_PHYSICAL); } static int gather_stdout_one(int fd, void *arg) { @@ -217,7 +212,7 @@ static int gather_stdout_one(int fd, void *arg) { return 0; } static int gather_stdout_two(int fd, void *arg) { - char ***s = arg, **t; + char ***s = arg; STRV_FOREACH(t, *s) assert_se(write(fd, *t, strlen(*t)) == (ssize_t) strlen(*t)); @@ -243,8 +238,7 @@ const gather_stdout_callback_t gather_stdouts[] = { }; TEST(stdout_gathering) { - char template[] = "/tmp/test-exec-util.XXXXXXX"; - const char *dirs[] = {template, NULL}; + _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL; const char *name, *name2, *name3; int r; @@ -253,12 +247,14 @@ TEST(stdout_gathering) { void* args[] = {&tmp, &tmp, &output}; - assert_se(mkdtemp(template)); + assert_se(mkdtemp_malloc("/tmp/test-exec-util.XXXXXXX", &tmpdir) >= 0); + + const char *dirs[] = { tmpdir, NULL }; /* write files */ - name = strjoina(template, "/10-foo"); - name2 = strjoina(template, "/20-bar"); - name3 = strjoina(template, "/30-last"); + name = strjoina(tmpdir, "/10-foo"); + name2 = strjoina(tmpdir, "/20-bar"); + name3 = strjoina(tmpdir, "/30-last"); assert_se(write_string_file(name, "#!/bin/sh\necho a\necho b\necho c\n", @@ -287,8 +283,7 @@ TEST(stdout_gathering) { } TEST(environment_gathering) { - char template[] = "/tmp/test-exec-util.XXXXXXX", **p; - const char *dirs[] = {template, NULL}; + _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL; const char *name, *name2, *name3, *old; int r; @@ -297,12 +292,14 @@ TEST(environment_gathering) { void* const args[] = { &tmp, &tmp, &env }; - assert_se(mkdtemp(template)); + assert_se(mkdtemp_malloc("/tmp/test-exec-util.XXXXXXX", &tmpdir) >= 0); + + const char *dirs[] = { tmpdir, NULL }; /* write files */ - name = strjoina(template, "/10-foo"); - name2 = strjoina(template, "/20-bar"); - name3 = strjoina(template, "/30-last"); + name = strjoina(tmpdir, "/10-foo"); + name2 = strjoina(tmpdir, "/20-bar"); + name3 = strjoina(tmpdir, "/30-last"); assert_se(write_string_file(name, "#!/bin/sh\n" @@ -378,17 +375,18 @@ TEST(environment_gathering) { } TEST(error_catching) { - char template[] = "/tmp/test-exec-util.XXXXXXX"; - const char *dirs[] = {template, NULL}; + _cleanup_(rm_rf_physical_and_freep) char *tmpdir = NULL; const char *name, *name2, *name3; int r; - assert_se(mkdtemp(template)); + assert_se(mkdtemp_malloc("/tmp/test-exec-util.XXXXXXX", &tmpdir) >= 0); + + const char *dirs[] = { tmpdir, NULL }; /* write files */ - name = strjoina(template, "/10-foo"); - name2 = strjoina(template, "/20-bar"); - name3 = strjoina(template, "/30-last"); + name = strjoina(tmpdir, "/10-foo"); + name2 = strjoina(tmpdir, "/20-bar"); + name3 = strjoina(tmpdir, "/30-last"); assert_se(write_string_file(name, "#!/bin/sh\necho a\necho b\necho c\n", diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 49629c6bc..6fce05bb5 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -190,19 +190,15 @@ static bool check_user_has_group_with_same_name(const char *name) { } static bool is_inaccessible_available(void) { - const char *p; - FOREACH_STRING(p, - "/run/systemd/inaccessible/reg", - "/run/systemd/inaccessible/dir", - "/run/systemd/inaccessible/chr", - "/run/systemd/inaccessible/blk", - "/run/systemd/inaccessible/fifo", - "/run/systemd/inaccessible/sock" - ) { + "/run/systemd/inaccessible/reg", + "/run/systemd/inaccessible/dir", + "/run/systemd/inaccessible/chr", + "/run/systemd/inaccessible/blk", + "/run/systemd/inaccessible/fifo", + "/run/systemd/inaccessible/sock") if (access(p, F_OK) < 0) return false; - } return true; } @@ -610,7 +606,6 @@ static int find_libraries(const char *exec, char ***ret) { _cleanup_strv_free_ char **v = NULL; assert_se(strv_split_newlines_full(&v, result, 0) >= 0); - char **q; STRV_FOREACH(q, v) { _cleanup_free_ char *word = NULL; const char *p = *q; @@ -678,7 +673,6 @@ static void test_exec_mount_apivfs(Manager *m) { assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_touch, "\n")); assert_se(strextend(&data, "BindReadOnlyPaths=", fullpath_test, "\n")); - char **p; STRV_FOREACH(p, libraries) assert_se(strextend(&data, "BindReadOnlyPaths=", *p, "\n")); @@ -1086,6 +1080,7 @@ static void test_exec_specifier(Manager *m) { test(m, "exec-specifier.service", 0, CLD_EXITED); test(m, "exec-specifier@foo-bar.service", 0, CLD_EXITED); test(m, "exec-specifier-interpolation.service", 0, CLD_EXITED); + test(m, "exec-specifier-credentials-dir.service", 0, CLD_EXITED); } static void test_exec_standardinput(Manager *m) { @@ -1122,7 +1117,7 @@ typedef struct test_entry { #define entry(x) {x, #x} -static int run_tests(UnitFileScope scope, const test_entry tests[], char **patterns) { +static int run_tests(LookupScope scope, const test_entry tests[], char **patterns) { _cleanup_(manager_freep) Manager *m = NULL; int r; @@ -1244,11 +1239,11 @@ int main(int argc, char *argv[]) { assert_se(unsetenv("VAR2") == 0); assert_se(unsetenv("VAR3") == 0); - r = run_tests(UNIT_FILE_USER, user_tests, argv + 1); + r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1); if (r != 0) return r; - r = run_tests(UNIT_FILE_SYSTEM, system_tests, argv + 1); + r = run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1); if (r != 0) return r; @@ -1270,11 +1265,11 @@ int main(int argc, char *argv[]) { can_unshare = false; - r = run_tests(UNIT_FILE_USER, user_tests, argv + 1); + r = run_tests(LOOKUP_SCOPE_USER, user_tests, argv + 1); if (r != 0) return r; - return run_tests(UNIT_FILE_SYSTEM, system_tests, argv + 1); + return run_tests(LOOKUP_SCOPE_SYSTEM, system_tests, argv + 1); #else return 0; #endif diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 087d76f76..3e98d9401 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -35,7 +35,6 @@ TEST(parse_env_file) { *six = NULL, *seven = NULL, *eight = NULL, *nine = NULL, *ten = NULL, *eleven = NULL, *twelve = NULL, *thirteen = NULL; _cleanup_strv_free_ char **a = NULL, **b = NULL; - char **i; unsigned k; int r; @@ -110,8 +109,7 @@ TEST(parse_env_file) { "eleven", &eleven, "twelve", &twelve, "thirteen", &thirteen); - - assert_se(r >= 0); + assert_se(r == 0); log_info("one=[%s]", strna(one)); log_info("two=[%s]", strna(two)); @@ -172,7 +170,6 @@ TEST(parse_multiline_env_file) { p[] = "/tmp/test-fileio-out-XXXXXX"; FILE *f; _cleanup_strv_free_ char **a = NULL, **b = NULL; - char **i; int r; assert_se(fmkostemp_safe(t, "w", &f) == 0); @@ -222,7 +219,6 @@ TEST(merge_env_file) { _cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX"; _cleanup_fclose_ FILE *f = NULL; _cleanup_strv_free_ char **a = NULL; - char **i; int r; assert_se(fmkostemp_safe(t, "w", &f) == 0); @@ -286,7 +282,6 @@ TEST(merge_env_file_invalid) { _cleanup_(unlink_tempfilep) char t[] = "/tmp/test-fileio-XXXXXX"; _cleanup_fclose_ FILE *f = NULL; _cleanup_strv_free_ char **a = NULL; - char **i; int r; assert_se(fmkostemp_safe(t, "w", &f) == 0); @@ -487,7 +482,6 @@ TEST(load_env_file_pairs) { int fd, r; _cleanup_fclose_ FILE *f = NULL; _cleanup_strv_free_ char **l = NULL; - char **k, **v; fd = mkostemp_safe(fn); assert_se(fd >= 0); @@ -1003,7 +997,6 @@ TEST(read_full_file_offset_size) { } static void test_read_virtual_file_one(size_t max_size) { - const char *filename; int r; log_info("/* %s (max_size=%zu) */", __func__, max_size); diff --git a/src/test/test-format-table.c b/src/test/test-format-table.c index a3b29ca33..60d5fee71 100644 --- a/src/test/test-format-table.c +++ b/src/test/test-format-table.c @@ -396,28 +396,30 @@ TEST(table) { _cleanup_(table_unrefp) Table *t = NULL; _cleanup_free_ char *formatted = NULL; - assert_se(t = table_new("one", "two", "three")); + assert_se(t = table_new("one", "two", "three", "four")); - assert_se(table_set_align_percent(t, TABLE_HEADER_CELL(2), 100) >= 0); + assert_se(table_set_align_percent(t, TABLE_HEADER_CELL(3), 100) >= 0); assert_se(table_add_many(t, TABLE_STRING, "xxx", TABLE_STRING, "yyy", - TABLE_BOOLEAN, true) >= 0); + TABLE_BOOLEAN, true, + TABLE_INT, -1) >= 0); assert_se(table_add_many(t, TABLE_STRING, "a long field", TABLE_STRING, "yyy", TABLE_SET_UPPERCASE, 1, - TABLE_BOOLEAN, false) >= 0); + TABLE_BOOLEAN, false, + TABLE_INT, -999999) >= 0); assert_se(table_format(t, &formatted) >= 0); printf("%s\n", formatted); assert_se(streq(formatted, - "ONE TWO THREE\n" - "xxx yyy yes\n" - "a long field YYY no\n")); + "ONE TWO THREE FOUR\n" + "xxx yyy yes -1\n" + "a long field YYY no -999999\n")); formatted = mfree(formatted); @@ -427,20 +429,20 @@ TEST(table) { printf("%s\n", formatted); assert_se(streq(formatted, - "ONE TWO THREE\n" - "xxx yyy yes\n" - "a long field YYY no\n")); + "ONE TWO THREE FOUR\n" + "xxx yyy yes -1\n" + "a long field YYY no -999999\n")); formatted = mfree(formatted); - table_set_width(t, 12); + table_set_width(t, 15); assert_se(table_format(t, &formatted) >= 0); printf("%s\n", formatted); assert_se(streq(formatted, - "ONE TWO THR…\n" - "xxx yyy yes\n" - "a … YYY no\n")); + "ONE TWO TH… FO…\n" + "xxx yyy yes -1\n" + "a … YYY no -9…\n")); formatted = mfree(formatted); @@ -449,9 +451,9 @@ TEST(table) { printf("%s\n", formatted); assert_se(streq(formatted, - "… … …\n" - "… … …\n" - "… … …\n")); + "… … … …\n" + "… … … …\n" + "… … … …\n")); formatted = mfree(formatted); @@ -460,9 +462,9 @@ TEST(table) { printf("%s\n", formatted); assert_se(streq(formatted, - "… … …\n" - "… … …\n" - "… … …\n")); + "… … … …\n" + "… … … …\n" + "… … … …\n")); formatted = mfree(formatted); @@ -473,9 +475,9 @@ TEST(table) { printf("%s\n", formatted); assert_se(streq(formatted, - "ONE TWO THREE\n" - "a long field YYY no\n" - "xxx yyy yes\n")); + "ONE TWO THREE FOUR\n" + "a long field YYY no -999999\n" + "xxx yyy yes -1\n")); formatted = mfree(formatted); @@ -484,27 +486,30 @@ TEST(table) { assert_se(table_add_many(t, TABLE_STRING, "fäää", TABLE_STRING, "uuu", - TABLE_BOOLEAN, true) >= 0); + TABLE_BOOLEAN, true, + TABLE_INT, 42) >= 0); assert_se(table_add_many(t, TABLE_STRING, "fäää", TABLE_STRING, "zzz", - TABLE_BOOLEAN, false) >= 0); + TABLE_BOOLEAN, false, + TABLE_INT, 0) >= 0); assert_se(table_add_many(t, TABLE_EMPTY, TABLE_SIZE, (uint64_t) 4711, - TABLE_TIMESPAN, (usec_t) 5*USEC_PER_MINUTE) >= 0); + TABLE_TIMESPAN, (usec_t) 5*USEC_PER_MINUTE, + TABLE_INT64, (uint64_t) -123456789) >= 0); assert_se(table_format(t, &formatted) >= 0); printf("%s\n", formatted); assert_se(streq(formatted, - "a long field YYY no\n" - "fäää zzz no\n" - "fäää uuu yes\n" - "xxx yyy yes\n" - " 4.6K 5min\n")); + "a long field YYY no -999999\n" + "fäää zzz no 0\n" + "fäää uuu yes 42\n" + "xxx yyy yes -1\n" + " 4.6K 5min -123456789\n")); formatted = mfree(formatted); @@ -515,24 +520,24 @@ TEST(table) { if (isatty(STDOUT_FILENO)) assert_se(streq(formatted, - " no a long f… no a long f… a long fi…\n" - " no fäää no fäää fäää\n" - " yes fäää yes fäää fäää\n" - " yes xxx yes xxx xxx\n" + "no a long f… no a long f… a long fi…\n" + "no fäää no fäää fäää\n" + "yes fäää yes fäää fäää\n" + "yes xxx yes xxx xxx\n" "5min 5min \n")); else assert_se(streq(formatted, - " no a long field no a long field a long field\n" - " no fäää no fäää fäää\n" - " yes fäää yes fäää fäää\n" - " yes xxx yes xxx xxx\n" + "no a long field no a long field a long field\n" + "no fäää no fäää fäää\n" + "yes fäää yes fäää fäää\n" + "yes xxx yes xxx xxx\n" "5min 5min \n")); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_INFO, - ({ - assert_se(setenv("SYSTEMD_COLORS", "0", 1) >= 0); - assert_se(setenv("COLUMNS", "40", 1) >= 0); - }), - /* no outro */); +static int intro(void) { + assert_se(setenv("SYSTEMD_COLORS", "0", 1) >= 0); + assert_se(setenv("COLUMNS", "40", 1) >= 0); + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-format-util.c b/src/test/test-format-util.c index 3e640a84b..ee74d1c89 100644 --- a/src/test/test-format-util.c +++ b/src/test/test-format-util.c @@ -10,10 +10,12 @@ assert_cc(STRLEN("xxx") == 3); assert_cc(STRLEN("") == 0); assert_cc(STRLEN(L"xxx") == 3 * sizeof(wchar_t)); assert_cc(STRLEN(L"") == 0); -assert_cc(DECIMAL_STR_MAX(uint8_t) == 5); -assert_cc(DECIMAL_STR_MAX(int8_t) == 5); -assert_cc(DECIMAL_STR_MAX(uint64_t) == 22); -assert_cc(DECIMAL_STR_MAX(char) == 5); +assert_cc(DECIMAL_STR_MAX(uint8_t) == STRLEN("255")+1); +assert_cc(DECIMAL_STR_MAX(int8_t) == STRLEN("-127")+1); +assert_cc(DECIMAL_STR_MAX(uint64_t) == STRLEN("18446744073709551615")+1); +assert_cc(DECIMAL_STR_MAX(int64_t) == CONST_MAX(STRLEN("-9223372036854775808"), STRLEN("9223372036854775807"))+1); +assert_cc(DECIMAL_STR_MAX(signed char) == STRLEN("-127")+1); +assert_cc(DECIMAL_STR_MAX(unsigned char) == STRLEN("255")+1); assert_cc(CONST_MAX(DECIMAL_STR_MAX(int8_t), STRLEN("xxx")) == 5); static void test_format_bytes_one(uint64_t val, bool trailing_B, const char *iec_with_p, const char *iec_without_p, diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index d527ad3d7..f24e97b4c 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -692,7 +692,6 @@ TEST(rename_noreplace) { _cleanup_(rm_rf_physical_and_freep) char *z = NULL; const char *j = NULL; - char **a, **b; if (arg_test_dir) j = strjoina(arg_test_dir, "/testXXXXXX"); @@ -971,4 +970,77 @@ TEST(open_mkdir_at) { assert_se(subsubdir_fd >= 0); } -DEFINE_CUSTOM_TEST_MAIN(LOG_INFO, arg_test_dir = argv[1], /* no outro */); +TEST(openat_report_new) { + _cleanup_free_ char *j = NULL; + _cleanup_(rm_rf_physical_and_freep) char *d = NULL; + _cleanup_close_ int fd = -1; + bool b; + + assert_se(mkdtemp_malloc(NULL, &d) >= 0); + + j = path_join(d, "test"); + assert_se(j); + + fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); + assert_se(fd >= 0); + fd = safe_close(fd); + assert_se(b); + + fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); + assert_se(fd >= 0); + fd = safe_close(fd); + assert_se(!b); + + fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); + assert_se(fd >= 0); + fd = safe_close(fd); + assert_se(!b); + + assert_se(unlink(j) >= 0); + + fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); + assert_se(fd >= 0); + fd = safe_close(fd); + assert_se(b); + + fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); + assert_se(fd >= 0); + fd = safe_close(fd); + assert_se(!b); + + assert_se(unlink(j) >= 0); + + fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, NULL); + assert_se(fd >= 0); + fd = safe_close(fd); + + fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT, 0666, &b); + assert_se(fd >= 0); + fd = safe_close(fd); + assert_se(!b); + + fd = openat_report_new(AT_FDCWD, j, O_RDWR, 0666, &b); + assert_se(fd >= 0); + fd = safe_close(fd); + assert_se(!b); + + fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT|O_EXCL, 0666, &b); + assert_se(fd == -EEXIST); + + assert_se(unlink(j) >= 0); + + fd = openat_report_new(AT_FDCWD, j, O_RDWR, 0666, &b); + assert_se(fd == -ENOENT); + + fd = openat_report_new(AT_FDCWD, j, O_RDWR|O_CREAT|O_EXCL, 0666, &b); + assert_se(fd >= 0); + fd = safe_close(fd); + assert_se(b); +} + +static int intro(void) { + arg_test_dir = saved_argv[1]; + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-gpt.c b/src/test/test-gpt.c index ab26d5d09..05da7a9e4 100644 --- a/src/test/test-gpt.c +++ b/src/test/test-gpt.c @@ -11,16 +11,13 @@ #include "util.h" TEST(gpt_types_against_architectures) { - const char *prefix; int r; /* Dumps a table indicating for which architectures we know we have matching GPT partition * types. Also validates whether we can properly categorize the entries. */ FOREACH_STRING(prefix, "root-", "usr-") - for (int a = 0; a < _ARCHITECTURE_MAX; a++) { - const char *suffix; - + for (int a = 0; a < _ARCHITECTURE_MAX; a++) FOREACH_STRING(suffix, "", "-verity", "-verity-sig") { _cleanup_free_ char *joined = NULL; sd_id128_t id; @@ -48,7 +45,6 @@ TEST(gpt_types_against_architectures) { assert_se(gpt_partition_type_uuid_to_arch(id) == a); } - } } DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/test/test-hashmap.c b/src/test/test-hashmap.c index cba0c33a8..dbf762cc0 100644 --- a/src/test/test-hashmap.c +++ b/src/test/test-hashmap.c @@ -158,7 +158,15 @@ TEST(hashmap_put_strdup_null) { /* This variable allows us to assert that the tests from different compilation units were actually run. */ int n_extern_tests_run = 0; -DEFINE_CUSTOM_TEST_MAIN( - LOG_INFO, - assert_se(n_extern_tests_run == 0), - assert_se(n_extern_tests_run == 2)); /* Ensure hashmap and ordered_hashmap were tested. */ +static int intro(void) { + assert_se(n_extern_tests_run == 0); + return EXIT_SUCCESS; +} + +static int outro(void) { + /* Ensure hashmap and ordered_hashmap were tested. */ + assert_se(n_extern_tests_run == 2); + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_FULL(LOG_INFO, intro, outro); diff --git a/src/test/test-id128.c b/src/test/test-id128.c index 33c4f1a5e..5da7b0ec1 100644 --- a/src/test/test-id128.c +++ b/src/test/test-id128.c @@ -21,7 +21,7 @@ TEST(id128) { sd_id128_t id, id2; - char t[SD_ID128_STRING_MAX], q[ID128_UUID_STRING_MAX]; + char t[SD_ID128_STRING_MAX], q[SD_ID128_UUID_STRING_MAX]; _cleanup_free_ char *b = NULL; _cleanup_close_ int fd = -1; @@ -53,7 +53,7 @@ TEST(id128) { printf("waldi2: %s\n", b); assert_se(streq(t, b)); - printf("waldi3: %s\n", id128_to_uuid_string(ID128_WALDI, q)); + printf("waldi3: %s\n", sd_id128_to_uuid_string(ID128_WALDI, q)); assert_se(streq(q, UUID_WALDI)); b = mfree(b); @@ -136,7 +136,7 @@ TEST(id128) { assert_se(ftruncate(fd, 0) >= 0); assert_se(sd_id128_randomize(&id) >= 0); - assert_se(write(fd, id128_to_uuid_string(id, q), 36) == 36); + assert_se(write(fd, sd_id128_to_uuid_string(id, q), 36) == 36); assert_se(lseek(fd, 0, SEEK_SET) == 0); assert_se(id128_read_fd(fd, ID128_PLAIN, &id2) == -EINVAL); diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index ba715e6d7..a36536b85 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -11,8 +11,11 @@ #include "special.h" #include "string-util.h" #include "tests.h" +#include "tmpfile-util.h" -static char root[] = "/tmp/rootXXXXXX"; +static char *root = NULL; + +STATIC_DESTRUCTOR_REGISTER(root, rm_rf_physical_and_freep); TEST(basic_mask_and_enable) { const char *p; @@ -20,41 +23,41 @@ TEST(basic_mask_and_enable) { UnitFileChange *changes = NULL; size_t n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "f.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "e.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "f.service", NULL) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/a.service"); assert_se(write_string_file(p, "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/b.service"); assert_se(symlink("a.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); p = strjoina(root, "/usr/lib/systemd/system/c.service"); assert_se(symlink("/usr/lib/systemd/system/a.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); p = strjoina(root, "/usr/lib/systemd/system/d.service"); assert_se(symlink("c.service", p) >= 0); /* This one is interesting, as d follows a relative, then an absolute symlink */ - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_mask(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_mask(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/dev/null")); @@ -64,17 +67,17 @@ TEST(basic_mask_and_enable) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_MASKED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_MASKED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_MASKED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_MASKED); /* Enabling a masked unit should fail! */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == -ERFKILL); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == -ERFKILL); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_unmask(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_unmask(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/a.service"); @@ -82,27 +85,27 @@ TEST(basic_mask_and_enable) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) == 1); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service")); + assert_se(streq(changes[0].source, "../a.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); /* Enabling it again should succeed but be a NOP */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 0); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service"); @@ -110,63 +113,63 @@ TEST(basic_mask_and_enable) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); /* Disabling a disabled unit must succeed but be a NOP */ - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("a.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 0); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; /* Let's enable this indirectly via a symlink */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("d.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("d.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/a.service")); + assert_se(streq(changes[0].source, "../a.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); /* Let's try to reenable */ - assert_se(unit_file_reenable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("b.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_reenable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("b.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/a.service"); assert_se(streq(changes[0].path, p)); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/a.service")); + assert_se(streq(changes[1].source, "../a.service")); assert_se(streq(changes[1].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "b.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "c.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "d.service", &state) >= 0 && state == UNIT_FILE_ALIAS); /* Test masking with relative symlinks */ p = strjoina(root, "/usr/lib/systemd/system/e.service"); assert_se(symlink("../../../../../../dev/null", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "e.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED); assert_se(unlink(p) == 0); assert_se(symlink("/usr/../dev/null", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "e.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "e.service", &state) >= 0 && state == UNIT_FILE_MASKED); assert_se(unlink(p) == 0); @@ -177,13 +180,13 @@ TEST(basic_mask_and_enable) { "[Install]\n" "WantedBy=x.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "f.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "f.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "f.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "f.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("f.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("f.service"), &changes, &n_changes) == 1); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/f.service")); + assert_se(streq(changes[0].source, "../f.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/x.target.wants/f.service"); assert_se(streq(changes[0].path, p)); assert_se(changes[1].type_or_errno == UNIT_FILE_DESTINATION_NOT_PRESENT); @@ -193,7 +196,7 @@ TEST(basic_mask_and_enable) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "f.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "f.service", &state) >= 0 && state == UNIT_FILE_ENABLED); } TEST(linked_units) { @@ -233,9 +236,9 @@ TEST(linked_units) { "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked2.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked3.service", NULL) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/linked2.service"); assert_se(symlink("/opt/linked2.service", p) >= 0); @@ -243,12 +246,12 @@ TEST(linked_units) { p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked3.service"); assert_se(symlink("/opt/linked3.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked3.service", &state) >= 0 && state == UNIT_FILE_LINKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked3.service", &state) >= 0 && state == UNIT_FILE_LINKED); /* First, let's link the unit into the search path */ - assert_se(unit_file_link(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_link(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(streq(changes[0].source, "/opt/linked.service")); @@ -257,10 +260,10 @@ TEST(linked_units) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_LINKED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_LINKED); /* Let's unlink it from the search path again */ - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service"); @@ -268,16 +271,17 @@ TEST(linked_units) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", NULL) == -ENOENT); /* Now, let's not just link it, but also enable it */ - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("/opt/linked.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 2); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked.service"); q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service"); for (i = 0 ; i < n_changes; i++) { assert_se(changes[i].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[i].source, "/opt/linked.service")); + assert_se(STR_IN_SET(changes[i].source, + "../linked.service", "/opt/linked.service")); if (p && streq(changes[i].path, p)) p = NULL; @@ -290,10 +294,10 @@ TEST(linked_units) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", &state) >= 0 && state == UNIT_FILE_ENABLED); /* And let's unlink it again */ - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 2); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked.service"); q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked.service"); @@ -311,15 +315,16 @@ TEST(linked_units) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "linked.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "linked.service", NULL) == -ENOENT); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked2.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked2.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 2); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/linked2.service"); q = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/linked2.service"); for (i = 0 ; i < n_changes; i++) { assert_se(changes[i].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[i].source, "/opt/linked2.service")); + assert_se(STR_IN_SET(changes[i].source, + "../linked2.service", "/opt/linked2.service")); if (p && streq(changes[i].path, p)) p = NULL; @@ -332,12 +337,12 @@ TEST(linked_units) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("linked3.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("linked3.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(startswith(changes[0].path, root)); assert_se(endswith(changes[0].path, "linked3.service")); - assert_se(streq(changes[0].source, "/opt/linked3.service")); + assert_se(streq(changes[0].source, "../linked3.service")); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; } @@ -354,27 +359,27 @@ TEST(default) { p = strjoina(root, "/usr/lib/systemd/system/test-default.target"); assert_se(symlink("test-default-real.target", p) >= 0); - assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT); + assert_se(unit_file_get_default(LOOKUP_SCOPE_SYSTEM, root, &def) == -ENOENT); - assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, 0, root, "idontexist.target", &changes, &n_changes) == -ENOENT); + assert_se(unit_file_set_default(LOOKUP_SCOPE_SYSTEM, 0, root, "idontexist.target", &changes, &n_changes) == -ENOENT); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == -ENOENT); assert_se(streq_ptr(changes[0].path, "idontexist.target")); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) == -ENOENT); + assert_se(unit_file_get_default(LOOKUP_SCOPE_SYSTEM, root, &def) == -ENOENT); - assert_se(unit_file_set_default(UNIT_FILE_SYSTEM, 0, root, "test-default.target", &changes, &n_changes) >= 0); + assert_se(unit_file_set_default(LOOKUP_SCOPE_SYSTEM, 0, root, "test-default.target", &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/test-default-real.target")); + assert_se(streq(changes[0].source, "test-default-real.target")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR "/" SPECIAL_DEFAULT_TARGET); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_default(UNIT_FILE_SYSTEM, root, &def) >= 0); + assert_se(unit_file_get_default(LOOKUP_SCOPE_SYSTEM, root, &def) >= 0); assert_se(streq_ptr(def, "test-default-real.target")); } @@ -395,10 +400,10 @@ TEST(add_dependency) { p = strjoina(root, "/usr/lib/systemd/system/add-dependency-test-service.service"); assert_se(symlink("real-add-dependency-test-service.service", p) >= 0); - assert_se(unit_file_add_dependency(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("add-dependency-test-service.service"), "add-dependency-test-target.target", UNIT_WANTS, &changes, &n_changes) >= 0); + assert_se(unit_file_add_dependency(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("add-dependency-test-service.service"), "add-dependency-test-target.target", UNIT_WANTS, &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/real-add-dependency-test-service.service")); + assert_se(streq(changes[0].source, "../real-add-dependency-test-service.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/real-add-dependency-test-target.target.wants/real-add-dependency-test-service.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); @@ -413,10 +418,10 @@ TEST(template_enable) { log_info("== %s ==", __func__); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/template@.service"); assert_se(write_string_file(p, @@ -427,96 +432,97 @@ TEST(template_enable) { p = strjoina(root, "/usr/lib/systemd/system/template-symlink@.service"); assert_se(symlink("template@.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); log_info("== %s with template@.service enabled ==", __func__); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + assert_se(streq(changes[0].source, "../template@.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@def.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); log_info("== %s with template@foo.service enabled ==", __func__); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + assert_se(streq(changes[0].source, "../template@foo.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@foo.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0); + assert_se(state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("template@foo.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_DISABLED); log_info("== %s with template-symlink@quux.service enabled ==", __func__); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("template-symlink@quux.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("template-symlink@quux.service"), &changes, &n_changes) >= 0); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/template@.service")); + assert_se(streq(changes[0].source, "../template@quux.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/template@quux.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "template-symlink@quux.service", &state) >= 0 && state == UNIT_FILE_ENABLED); } TEST(indirect) { @@ -525,9 +531,9 @@ TEST(indirect) { UnitFileState state; const char *p; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirecta.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectb.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectc.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/indirecta.service"); assert_se(write_string_file(p, @@ -542,24 +548,24 @@ TEST(indirect) { p = strjoina(root, "/usr/lib/systemd/system/indirectc.service"); assert_se(symlink("indirecta.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/indirectb.service")); + assert_se(streq(changes[0].source, "../indirectb.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirecta.service", &state) >= 0 && state == UNIT_FILE_INDIRECT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectb.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "indirectc.service", &state) >= 0 && state == UNIT_FILE_ALIAS); - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("indirectc.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/indirectb.service"); @@ -577,8 +583,8 @@ TEST(preset_and_list) { UnitFileList *fl; Hashmap *h; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service"); assert_se(write_string_file(p, @@ -595,22 +601,22 @@ TEST(preset_and_list) { "enable *-yes.*\n" "disable *\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/preset-yes.service")); + assert_se(streq(changes[0].source, "../preset-yes.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-yes.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/preset-yes.service"); @@ -618,18 +624,18 @@ TEST(preset_and_list) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("preset-no.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes == 0); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset_all(LOOKUP_SCOPE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes > 0); @@ -638,7 +644,7 @@ TEST(preset_and_list) { for (i = 0; i < n_changes; i++) { if (changes[i].type_or_errno == UNIT_FILE_SYMLINK) { - assert_se(streq(changes[i].source, "/usr/lib/systemd/system/preset-yes.service")); + assert_se(streq(changes[i].source, "../preset-yes.service")); assert_se(streq(changes[i].path, p)); } else assert_se(changes[i].type_or_errno == UNIT_FILE_UNLINK); @@ -647,17 +653,17 @@ TEST(preset_and_list) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-yes.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "preset-no.service", &state) >= 0 && state == UNIT_FILE_DISABLED); assert_se(h = hashmap_new(&string_hash_ops)); - assert_se(unit_file_get_list(UNIT_FILE_SYSTEM, root, h, NULL, NULL) >= 0); + assert_se(unit_file_get_list(LOOKUP_SCOPE_SYSTEM, root, h, NULL, NULL) >= 0); p = strjoina(root, "/usr/lib/systemd/system/preset-yes.service"); q = strjoina(root, "/usr/lib/systemd/system/preset-no.service"); HASHMAP_FOREACH(fl, h) { - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, basename(fl->path), &state) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, basename(fl->path), &state) >= 0); assert_se(fl->state == state); if (streq(fl->path, p)) { @@ -681,17 +687,17 @@ TEST(revert) { UnitFileChange *changes = NULL; size_t n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "yy.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "xx.service", NULL) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "yy.service", NULL) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/xx.service"); assert_se(write_string_file(p, "# Empty\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", NULL) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "xx.service", &state) >= 0 && state == UNIT_FILE_STATIC); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "xx.service", NULL) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "xx.service", &state) >= 0 && state == UNIT_FILE_STATIC); /* Initially there's nothing to revert */ - assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_revert(LOOKUP_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 0); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; @@ -700,7 +706,7 @@ TEST(revert) { assert_se(write_string_file(p, "# Empty override\n", WRITE_STRING_FILE_CREATE) >= 0); /* Revert the override file */ - assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_revert(LOOKUP_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); assert_se(streq(changes[0].path, p)); @@ -711,7 +717,7 @@ TEST(revert) { assert_se(write_string_file(p, "# Empty dropin\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); /* Revert the dropin file */ - assert_se(unit_file_revert(UNIT_FILE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_revert(LOOKUP_SCOPE_SYSTEM, root, STRV_MAKE("xx.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); assert_se(streq(changes[0].path, p)); @@ -729,8 +735,8 @@ TEST(preset_order) { const char *p; UnitFileState state; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-1.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-2.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/prefix-1.service"); assert_se(write_string_file(p, @@ -748,48 +754,48 @@ TEST(preset_order) { "disable prefix-*.service\n" "enable prefix-2.service\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("prefix-1.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("prefix-1.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/prefix-1.service")); + assert_se(streq(changes[0].source, "../prefix-1.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/prefix-1.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("prefix-2.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("prefix-2.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes == 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "prefix-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); } TEST(static_instance) { UnitFileState state; const char *p; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@foo.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/static-instance@.service"); assert_se(write_string_file(p, "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/static-instance@foo.service"); assert_se(symlink("static-instance@.service", p) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_STATIC); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "static-instance@foo.service", &state) >= 0 && state == UNIT_FILE_STATIC); } TEST(with_dropin) { @@ -798,11 +804,11 @@ TEST(with_dropin) { UnitFileChange *changes = NULL; size_t n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4a.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4b.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4a.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4b.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1.service"); assert_se(write_string_file(p, @@ -814,7 +820,7 @@ TEST(with_dropin) { "[Install]\n" "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service"); assert_se(write_string_file(p, @@ -826,7 +832,7 @@ TEST(with_dropin) { "[Install]\n" "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-3.service"); assert_se(write_string_file(p, @@ -838,7 +844,7 @@ TEST(with_dropin) { "[Install]\n" "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-4a.service"); assert_se(write_string_file(p, @@ -850,21 +856,21 @@ TEST(with_dropin) { "[Install]\n" "Also=with-dropin-4b.service\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-4b.service"); assert_se(write_string_file(p, "[Install]\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1.service"), &changes, &n_changes) == 1); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1.service")); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1.service")); + assert_se(streq(changes[0].source, "../with-dropin-1.service")); + assert_se(streq(changes[1].source, "../with-dropin-1.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1.service"); @@ -872,13 +878,13 @@ TEST(with_dropin) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2.service"), &changes, &n_changes) == 1); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2.service"), &changes, &n_changes) == 1); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service")); - assert_se(streq(changes[1].source, SYSTEM_CONFIG_UNIT_DIR"/with-dropin-2.service")); + assert_se(streq(changes[0].source, "../with-dropin-2.service")); + assert_se(streq(changes[1].source, "../with-dropin-2.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2.service"); @@ -886,13 +892,13 @@ TEST(with_dropin) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3.service"), &changes, &n_changes) == 1); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3.service"), &changes, &n_changes) == 1); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3.service")); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-3.service")); + assert_se(streq(changes[0].source, "../with-dropin-3.service")); + assert_se(streq(changes[1].source, "../with-dropin-3.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-3.service"); @@ -900,13 +906,13 @@ TEST(with_dropin) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-4a.service"), &changes, &n_changes) == 2); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-4a.service"), &changes, &n_changes) == 2); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-4a.service")); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-4b.service")); + assert_se(streq(changes[0].source, "../with-dropin-4a.service")); + assert_se(streq(changes[1].source, "../with-dropin-4b.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4a.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-4b.service"); @@ -914,11 +920,11 @@ TEST(with_dropin) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4a.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-4b.service", &state) >= 0 && state == UNIT_FILE_ENABLED); } TEST(with_dropin_template) { @@ -927,9 +933,9 @@ TEST(with_dropin_template) { UnitFileChange *changes = NULL; size_t n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1@.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@.service", &state) == -ENOENT); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2@.service", &state) == -ENOENT); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3@.service", &state) == -ENOENT); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-1@.service"); assert_se(write_string_file(p, @@ -941,7 +947,7 @@ TEST(with_dropin_template) { "[Install]\n" "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-2@.service"); assert_se(write_string_file(p, @@ -953,7 +959,7 @@ TEST(with_dropin_template) { "[Install]\n" "WantedBy=graphical.target\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system/with-dropin-3@.service"); assert_se(write_string_file(p, @@ -966,14 +972,14 @@ TEST(with_dropin_template) { "[Install]\n" "DefaultInstance=instance-2\n", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1@instance-1.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-1@instance-1.service"), &changes, &n_changes) == 1); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-1@.service")); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-1@.service")); + assert_se(streq(changes[0].source, "../with-dropin-1@instance-1.service")); + assert_se(streq(changes[1].source, "../with-dropin-1@instance-1.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-1@instance-1.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-1@instance-1.service"); @@ -981,12 +987,12 @@ TEST(with_dropin_template) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-1.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-1.service"), &changes, &n_changes) == 1); assert_se(n_changes == 2); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); assert_se(changes[1].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service")); - assert_se(streq(changes[1].source, "/usr/lib/systemd/system/with-dropin-2@.service")); + assert_se(streq(changes[0].source, "../with-dropin-2@instance-1.service")); + assert_se(streq(changes[1].source, "../with-dropin-2@instance-1.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-1.service"); assert_se(streq(changes[0].path, p)); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/graphical.target.wants/with-dropin-2@instance-1.service"); @@ -994,29 +1000,29 @@ TEST(with_dropin_template) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-2.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-2@instance-2.service"), &changes, &n_changes) == 1); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-2@.service")); + assert_se(streq(changes[0].source, "../with-dropin-2@instance-2.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-2@instance-2.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_enable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3@.service"), &changes, &n_changes) == 1); + assert_se(unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("with-dropin-3@.service"), &changes, &n_changes) == 1); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); - assert_se(streq(changes[0].source, "/usr/lib/systemd/system/with-dropin-3@.service")); + assert_se(streq(changes[0].source, "../with-dropin-3@.service")); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/with-dropin-3@instance-2.service"); assert_se(streq(changes[0].path, p)); unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-1@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-2@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@instance-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "with-dropin-3@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-1@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2@instance-1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-2@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3@instance-1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "with-dropin-3@instance-2.service", &state) >= 0 && state == UNIT_FILE_ENABLED); } TEST(preset_multiple_instances) { @@ -1032,7 +1038,7 @@ TEST(preset_multiple_instances) { "DefaultInstance=def\n" "WantedBy=multi-user.target\n", WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@.service", &state) >= 0 && state == UNIT_FILE_DISABLED); p = strjoina(root, "/usr/lib/systemd/system-preset/test.preset"); assert_se(write_string_file(p, @@ -1040,11 +1046,11 @@ TEST(preset_multiple_instances) { "enable emptylist@.service\n" /* This line ensures the old functionality for templated unit still works */ "disable *\n" , WRITE_STRING_FILE_CREATE) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_DISABLED); /* Preset a single instantiated unit specified in the list */ - assert_se(unit_file_preset(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_SYMLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service"); @@ -1052,7 +1058,7 @@ TEST(preset_multiple_instances) { unit_file_changes_free(changes, n_changes); changes = NULL; n_changes = 0; - assert_se(unit_file_disable(UNIT_FILE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), &changes, &n_changes) >= 0); + assert_se(unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, root, STRV_MAKE("foo@bar0.service"), &changes, &n_changes) >= 0); assert_se(n_changes == 1); assert_se(changes[0].type_or_errno == UNIT_FILE_UNLINK); p = strjoina(root, SYSTEM_CONFIG_UNIT_DIR"/multi-user.target.wants/foo@bar0.service"); @@ -1061,17 +1067,17 @@ TEST(preset_multiple_instances) { changes = NULL; n_changes = 0; /* Check for preset-all case, only instances on the list should be enabled, not including the default instance */ - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_preset_all(UNIT_FILE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); + assert_se(unit_file_preset_all(LOOKUP_SCOPE_SYSTEM, 0, root, UNIT_FILE_PRESET_FULL, &changes, &n_changes) >= 0); assert_se(n_changes > 0); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); - assert_se(unit_file_get_state(UNIT_FILE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@def.service", &state) >= 0 && state == UNIT_FILE_DISABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bar0.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bar1.service", &state) >= 0 && state == UNIT_FILE_ENABLED); + assert_se(unit_file_get_state(LOOKUP_SCOPE_SYSTEM, root, "foo@bartest.service", &state) >= 0 && state == UNIT_FILE_ENABLED); unit_file_changes_free(changes, n_changes); } @@ -1088,7 +1094,7 @@ static void verify_one( if (i != last_info) log_info("-- %s --", (last_info = i)->name); - r = unit_file_verify_alias(i, alias, &alias2); + r = unit_file_verify_alias(i, alias, &alias2, NULL, NULL); log_info_errno(r, "alias %s ← %s: %d/%m (expected %d)%s%s%s", i->name, alias, r, expected, alias2 ? " [" : "", strempty(alias2), @@ -1239,10 +1245,10 @@ TEST(verify_alias) { verify_one(&di_inst_template, "goo.target.conf/plain.service", -EXDEV, NULL); } -static void setup_root(void) { +static int intro(void) { const char *p; - assert_se(mkdtemp(root)); + assert_se(mkdtemp_malloc("/tmp/rootXXXXXX", &root) >= 0); p = strjoina(root, "/usr/lib/systemd/system/"); assert_se(mkdir_p(p, 0755) >= 0); @@ -1264,6 +1270,9 @@ static void setup_root(void) { p = strjoina(root, "/usr/lib/systemd/system/graphical.target"); assert_se(write_string_file(p, "# pretty much empty", WRITE_STRING_FILE_CREATE) >= 0); + + return EXIT_SUCCESS; } -DEFINE_CUSTOM_TEST_MAIN(LOG_INFO, setup_root(), assert_se(rm_rf(root, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0)); + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-install.c b/src/test/test-install.c index 7a0beb2d2..6c5a03646 100644 --- a/src/test/test-install.c +++ b/src/test/test-install.c @@ -32,13 +32,13 @@ int main(int argc, char* argv[]) { test_setup_logging(LOG_DEBUG); h = hashmap_new(&string_hash_ops); - r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL); + r = unit_file_get_list(LOOKUP_SCOPE_SYSTEM, NULL, h, NULL, NULL); assert_se(r == 0); HASHMAP_FOREACH(p, h) { UnitFileState s = _UNIT_FILE_STATE_INVALID; - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(p->path), &s); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(p->path), &s); assert_se((r < 0 && p->state == UNIT_FILE_BAD) || (p->state == s)); @@ -52,18 +52,18 @@ int main(int argc, char* argv[]) { log_info("/*** enable **/"); - r = unit_file_enable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); log_info("/*** enable2 **/"); - r = unit_file_enable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); @@ -71,13 +71,13 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); @@ -85,16 +85,16 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_mask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_mask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); log_info("/*** mask2 ***/"); - r = unit_file_mask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_mask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); @@ -102,16 +102,16 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_unmask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_unmask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); log_info("/*** unmask2 ***/"); - r = unit_file_unmask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_unmask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); @@ -119,13 +119,13 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_mask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_mask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); @@ -133,16 +133,16 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); log_info("/*** disable2 ***/"); - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_MASKED); @@ -150,13 +150,13 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_unmask(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); + r = unit_file_unmask(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, files[0], &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, files[0], &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_DISABLED); @@ -164,13 +164,13 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_enable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_enable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); @@ -178,26 +178,26 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); log_info("/*** link files2 ***/"); changes = NULL; n_changes = 0; - r = unit_file_link(UNIT_FILE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_link(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_LINKED); @@ -205,26 +205,26 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); log_info("/*** link files2 ***/"); changes = NULL; n_changes = 0; - r = unit_file_link(UNIT_FILE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_link(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_LINKED); @@ -232,13 +232,13 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_reenable(UNIT_FILE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); + r = unit_file_reenable(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files2, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); @@ -246,25 +246,25 @@ int main(int argc, char* argv[]) { changes = NULL; n_changes = 0; - r = unit_file_disable(UNIT_FILE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); + r = unit_file_disable(LOOKUP_SCOPE_SYSTEM, 0, NULL, STRV_MAKE(basename(files2[0])), &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files2[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files2[0]), &state); assert_se(r < 0); log_info("/*** preset files ***/"); changes = NULL; n_changes = 0; - r = unit_file_preset(UNIT_FILE_SYSTEM, 0, NULL, (char**) files, UNIT_FILE_PRESET_FULL, &changes, &n_changes); + r = unit_file_preset(LOOKUP_SCOPE_SYSTEM, 0, NULL, (char**) files, UNIT_FILE_PRESET_FULL, &changes, &n_changes); assert_se(r >= 0); dump_changes(changes, n_changes); unit_file_changes_free(changes, n_changes); - r = unit_file_get_state(UNIT_FILE_SYSTEM, NULL, basename(files[0]), &state); + r = unit_file_get_state(LOOKUP_SCOPE_SYSTEM, NULL, basename(files[0]), &state); assert_se(r >= 0); assert_se(state == UNIT_FILE_ENABLED); diff --git a/src/test/test-job-type.c b/src/test/test-job-type.c index 024d976a7..0a9b6dc24 100644 --- a/src/test/test-job-type.c +++ b/src/test/test-job-type.c @@ -6,27 +6,24 @@ #include "unit.h" int main(int argc, char *argv[]) { - JobType a, b, c, ab, bc, ab_c, bc_a, a_bc; const ServiceState test_states[] = { SERVICE_DEAD, SERVICE_RUNNING }; - unsigned i; - bool merged_ab; - /* fake a unit */ - static Service s = { - .meta.load_state = UNIT_LOADED, - .type = SERVICE_SIMPLE, - }; - Unit *u = UNIT(&s); + for (size_t i = 0; i < ELEMENTSOF(test_states); i++) { + /* fake a unit */ + Service s = { + .meta.load_state = UNIT_LOADED, + .type = SERVICE_SIMPLE, + .state = test_states[i], + }; + Unit *u = UNIT(&s); - for (i = 0; i < ELEMENTSOF(test_states); i++) { - s.state = test_states[i]; printf("\nWith collapsing for service state %s\n" "=========================================\n", service_state_to_string(s.state)); - for (a = 0; a < _JOB_TYPE_MAX_MERGING; a++) { - for (b = 0; b < _JOB_TYPE_MAX_MERGING; b++) { + for (JobType a = 0; a < _JOB_TYPE_MAX_MERGING; a++) { + for (JobType b = 0; b < _JOB_TYPE_MAX_MERGING; b++) { - ab = a; - merged_ab = (job_type_merge_and_collapse(&ab, b, u) >= 0); + JobType ab = a; + bool merged_ab = job_type_merge_and_collapse(&ab, b, u) >= 0; if (!job_type_is_mergeable(a, b)) { assert_se(!merged_ab); @@ -37,7 +34,7 @@ int main(int argc, char *argv[]) { assert_se(merged_ab); printf("%s + %s = %s\n", job_type_to_string(a), job_type_to_string(b), job_type_to_string(ab)); - for (c = 0; c < _JOB_TYPE_MAX_MERGING; c++) { + for (JobType c = 0; c < _JOB_TYPE_MAX_MERGING; c++) { /* Verify transitivity of mergeability of job types */ assert_se(!job_type_is_mergeable(a, b) || @@ -53,18 +50,18 @@ int main(int argc, char *argv[]) { * either a or b is not mergeable with c either. */ assert_se(job_type_is_mergeable(ab, c) || !job_type_is_mergeable(a, c) || !job_type_is_mergeable(b, c)); - bc = b; + JobType bc = b; if (job_type_merge_and_collapse(&bc, c, u) >= 0) { /* Verify associativity */ - ab_c = ab; + JobType ab_c = ab; assert_se(job_type_merge_and_collapse(&ab_c, c, u) == 0); - bc_a = bc; + JobType bc_a = bc; assert_se(job_type_merge_and_collapse(&bc_a, a, u) == 0); - a_bc = a; + JobType a_bc = a; assert_se(job_type_merge_and_collapse(&a_bc, bc, u) == 0); assert_se(ab_c == bc_a); diff --git a/src/test/test-kbd-util.c b/src/test/test-kbd-util.c index f15cff479..0a166c6e1 100644 --- a/src/test/test-kbd-util.c +++ b/src/test/test-kbd-util.c @@ -7,7 +7,6 @@ int main(int argc, char *argv[]) { _cleanup_strv_free_ char **maps = NULL; - char **m; int r; log_show_color(true); diff --git a/src/test/test-libcrypt-util.c b/src/test/test-libcrypt-util.c index ebd520f7b..f88a9f9b2 100644 --- a/src/test/test-libcrypt-util.c +++ b/src/test/test-libcrypt-util.c @@ -39,7 +39,6 @@ static int test_hash_password(void) { /* As a warm-up exercise, check if we can hash passwords. */ bool have_sane_hash = false; - const char *hash; FOREACH_STRING(hash, "ew3bU1.hoKk4o", @@ -68,7 +67,6 @@ static void test_hash_password_full(void) { log_info("/* %s */", __func__); _cleanup_free_ void *cd_data = NULL; - const char *i; int cd_size = 0; log_info("sizeof(struct crypt_data): %zu bytes", sizeof(struct crypt_data)); diff --git a/src/test/test-list.c b/src/test/test-list.c index 25cc55a99..78bbad8e4 100644 --- a/src/test/test-list.c +++ b/src/test/test-list.c @@ -11,7 +11,6 @@ int main(int argc, const char *argv[]) { LIST_HEAD(list_item, head); LIST_HEAD(list_item, head2); list_item items[4]; - list_item *cursor; LIST_HEAD_INIT(head); LIST_HEAD_INIT(head2); @@ -57,6 +56,7 @@ int main(int argc, const char *argv[]) { assert_se(items[2].item_prev == &items[3]); assert_se(items[3].item_prev == NULL); + list_item *cursor; LIST_FIND_HEAD(item, &items[0], cursor); assert_se(cursor == &items[3]); diff --git a/src/test/test-load-fragment.c b/src/test/test-load-fragment.c index e878979a8..c3206e1e7 100644 --- a/src/test/test-load-fragment.c +++ b/src/test/test-load-fragment.c @@ -30,6 +30,10 @@ /* Nontrivial value serves as a placeholder to check that parsing function (didn't) change it */ #define CGROUP_LIMIT_DUMMY 3 +static char *runtime_dir = NULL; + +STATIC_DESTRUCTOR_REGISTER(runtime_dir, rm_rf_physical_and_freep); + TEST_RET(unit_file_get_set) { int r; Hashmap *h; @@ -38,7 +42,7 @@ TEST_RET(unit_file_get_set) { h = hashmap_new(&string_hash_ops); assert_se(h); - r = unit_file_get_list(UNIT_FILE_SYSTEM, NULL, h, NULL, NULL); + r = unit_file_get_list(LOOKUP_SCOPE_SYSTEM, NULL, h, NULL, NULL); if (IN_SET(r, -EPERM, -EACCES)) return log_tests_skipped_errno(r, "unit_file_get_list"); @@ -97,7 +101,7 @@ TEST(config_parse_exec) { _cleanup_(manager_freep) Manager *m = NULL; _cleanup_(unit_freep) Unit *u = NULL; - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (manager_errno_skip_test(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return; @@ -441,7 +445,7 @@ TEST(config_parse_log_extra_fields) { _cleanup_(unit_freep) Unit *u = NULL; ExecContext c = {}; - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (manager_errno_skip_test(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return; @@ -506,59 +510,74 @@ TEST(install_printf, .sd_booted = true) { assert_se(user = uid_to_name(getuid())); assert_se(asprintf(&uid, UID_FMT, getuid()) >= 0); -#define expect(src, pattern, result) \ +#define expect(scope, src, pattern, result) \ do { \ - _cleanup_free_ char *t = NULL; \ - _cleanup_free_ char \ - *d1 = strdup(i.name), \ - *d2 = strdup(i.path); \ - assert_se(install_name_printf(&src, pattern, NULL, &t) >= 0 || !result); \ + _cleanup_free_ char *t = NULL, \ + *d1 = ASSERT_PTR(strdup(i.name)), \ + *d2 = ASSERT_PTR(strdup(i.path)); \ + int r = install_name_printf(scope, &src, pattern, &t); \ + assert_se(result ? r >= 0 : r < 0); \ memzero(i.name, strlen(i.name)); \ memzero(i.path, strlen(i.path)); \ - assert_se(d1 && d2); \ if (result) { \ printf("%s\n", t); \ assert_se(streq(t, result)); \ - } else assert_se(t == NULL); \ + } else \ + assert_se(!t); \ strcpy(i.name, d1); \ strcpy(i.path, d2); \ } while (false) - expect(i, "%n", "name.service"); - expect(i, "%N", "name"); - expect(i, "%p", "name"); - expect(i, "%i", ""); - expect(i, "%j", "name"); - expect(i, "%g", group); - expect(i, "%G", gid); - expect(i, "%u", user); - expect(i, "%U", uid); + expect(LOOKUP_SCOPE_SYSTEM, i, "%n", "name.service"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%N", "name"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%p", "name"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%i", ""); + expect(LOOKUP_SCOPE_SYSTEM, i, "%j", "name"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%g", "root"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%G", "0"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%u", "root"); + expect(LOOKUP_SCOPE_SYSTEM, i, "%U", "0"); - expect(i, "%m", mid); - expect(i, "%b", bid); - expect(i, "%H", host); + expect(LOOKUP_SCOPE_SYSTEM, i, "%m", mid); + expect(LOOKUP_SCOPE_SYSTEM, i, "%b", bid); + expect(LOOKUP_SCOPE_SYSTEM, i, "%H", host); - expect(i2, "%g", group); - expect(i2, "%G", gid); - expect(i2, "%u", user); - expect(i2, "%U", uid); + expect(LOOKUP_SCOPE_SYSTEM, i2, "%g", "root"); + expect(LOOKUP_SCOPE_SYSTEM, i2, "%G", "0"); + expect(LOOKUP_SCOPE_SYSTEM, i2, "%u", "root"); + expect(LOOKUP_SCOPE_SYSTEM, i2, "%U", "0"); - expect(i3, "%n", "name@inst.service"); - expect(i3, "%N", "name@inst"); - expect(i3, "%p", "name"); - expect(i3, "%g", group); - expect(i3, "%G", gid); - expect(i3, "%u", user); - expect(i3, "%U", uid); + expect(LOOKUP_SCOPE_USER, i2, "%g", group); + expect(LOOKUP_SCOPE_USER, i2, "%G", gid); + expect(LOOKUP_SCOPE_USER, i2, "%u", user); + expect(LOOKUP_SCOPE_USER, i2, "%U", uid); - expect(i3, "%m", mid); - expect(i3, "%b", bid); - expect(i3, "%H", host); + /* gcc-12.0.1-0.9.fc36.x86_64 insist that streq(…, NULL) is called, + * even though the call is inside of a conditional where the pointer is checked. :( */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnonnull" + expect(LOOKUP_SCOPE_GLOBAL, i2, "%g", NULL); + expect(LOOKUP_SCOPE_GLOBAL, i2, "%G", NULL); + expect(LOOKUP_SCOPE_GLOBAL, i2, "%u", NULL); + expect(LOOKUP_SCOPE_GLOBAL, i2, "%U", NULL); +#pragma GCC diagnostic pop - expect(i4, "%g", group); - expect(i4, "%G", gid); - expect(i4, "%u", user); - expect(i4, "%U", uid); + expect(LOOKUP_SCOPE_SYSTEM, i3, "%n", "name@inst.service"); + expect(LOOKUP_SCOPE_SYSTEM, i3, "%N", "name@inst"); + expect(LOOKUP_SCOPE_SYSTEM, i3, "%p", "name"); + expect(LOOKUP_SCOPE_USER, i3, "%g", group); + expect(LOOKUP_SCOPE_USER, i3, "%G", gid); + expect(LOOKUP_SCOPE_USER, i3, "%u", user); + expect(LOOKUP_SCOPE_USER, i3, "%U", uid); + + expect(LOOKUP_SCOPE_SYSTEM, i3, "%m", mid); + expect(LOOKUP_SCOPE_SYSTEM, i3, "%b", bid); + expect(LOOKUP_SCOPE_SYSTEM, i3, "%H", host); + + expect(LOOKUP_SCOPE_USER, i4, "%g", group); + expect(LOOKUP_SCOPE_USER, i4, "%G", gid); + expect(LOOKUP_SCOPE_USER, i4, "%u", user); + expect(LOOKUP_SCOPE_USER, i4, "%U", uid); } static uint64_t make_cap(int cap) { @@ -769,6 +788,70 @@ TEST(config_parse_pass_environ) { assert_se(streq(passenv[0], "normal_name")); } +TEST(config_parse_unit_env_file) { + /* int config_parse_unit_env_file( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) */ + + _cleanup_(manager_freep) Manager *m = NULL; + Unit *u; + _cleanup_strv_free_ char **files = NULL; + int r; + + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + if (manager_errno_skip_test(r)) { + log_notice_errno(r, "Skipping test: manager_new: %m"); + return; + } + + assert_se(r >= 0); + assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); + + assert_se(u = unit_new(m, sizeof(Service))); + assert_se(unit_add_name(u, "foobar.service") == 0); + + r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1, + "EnvironmentFile", 0, "not-absolute", + &files, u); + assert_se(r == 0); + assert_se(strv_isempty(files)); + + r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1, + "EnvironmentFile", 0, "/absolute1", + &files, u); + assert_se(r == 0); + assert_se(strv_length(files) == 1); + + r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1, + "EnvironmentFile", 0, "/absolute2", + &files, u); + assert_se(r == 0); + assert_se(strv_length(files) == 2); + assert_se(streq(files[0], "/absolute1")); + assert_se(streq(files[1], "/absolute2")); + + r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1, + "EnvironmentFile", 0, "", + &files, u); + assert_se(r == 0); + assert_se(strv_isempty(files)); + + r = config_parse_unit_env_file(u->id, "fake", 1, "section", 1, + "EnvironmentFile", 0, "/path/%n.conf", + &files, u); + assert_se(r == 0); + assert_se(strv_length(files) == 1); + assert_se(streq(files[0], "/path/foobar.service.conf")); +} + TEST(unit_dump_config_items) { unit_dump_config_items(stdout); } @@ -856,7 +939,7 @@ TEST(unit_is_recursive_template_dependency) { Unit *u; int r; - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (manager_errno_skip_test(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return; @@ -894,15 +977,12 @@ TEST(unit_is_recursive_template_dependency) { assert_se(unit_is_likely_recursive_template_dependency(u, "foobar@foobar@123.mount", "foobar@%n.mount") == 0); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_INFO, +static int intro(void) { + if (enter_cgroup_subroot(NULL) == -ENOMEDIUM) + return log_tests_skipped("cgroupfs not available"); - _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; - ({ - if (enter_cgroup_subroot(NULL) == -ENOMEDIUM) - return log_tests_skipped("cgroupfs not available"); + assert_se(runtime_dir = setup_fake_runtime_dir()); + return EXIT_SUCCESS; +} - assert_se(runtime_dir = setup_fake_runtime_dir()); - }), - - /* no outro */); +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c index 55f86f7c2..6ec3f7f00 100644 --- a/src/test/test-locale-util.c +++ b/src/test/test-locale-util.c @@ -10,7 +10,6 @@ TEST(get_locales) { _cleanup_strv_free_ char **locales = NULL; - char **p; int r; r = get_locales(&locales); @@ -58,7 +57,6 @@ TEST(locale_is_installed) { TEST(keymaps) { _cleanup_strv_free_ char **kmaps = NULL; - char **p; int r; assert_se(!keymap_is_valid("")); @@ -93,13 +91,16 @@ TEST(dump_special_glyphs) { dump_glyph(SPECIAL_GLYPH_TREE_BRANCH); dump_glyph(SPECIAL_GLYPH_TREE_RIGHT); dump_glyph(SPECIAL_GLYPH_TREE_SPACE); + dump_glyph(SPECIAL_GLYPH_TREE_TOP); dump_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET); dump_glyph(SPECIAL_GLYPH_BLACK_CIRCLE); dump_glyph(SPECIAL_GLYPH_WHITE_CIRCLE); dump_glyph(SPECIAL_GLYPH_MULTIPLICATION_SIGN); dump_glyph(SPECIAL_GLYPH_CIRCLE_ARROW); dump_glyph(SPECIAL_GLYPH_BULLET); - dump_glyph(SPECIAL_GLYPH_ARROW); + dump_glyph(SPECIAL_GLYPH_ARROW_RIGHT); + dump_glyph(SPECIAL_GLYPH_ARROW_UP); + dump_glyph(SPECIAL_GLYPH_ARROW_DOWN); dump_glyph(SPECIAL_GLYPH_ELLIPSIS); dump_glyph(SPECIAL_GLYPH_MU); dump_glyph(SPECIAL_GLYPH_CHECK_MARK); diff --git a/src/test/test-loop-block.c b/src/test/test-loop-block.c index 1642f82e4..9c8c55bca 100644 --- a/src/test/test-loop-block.c +++ b/src/test/test-loop-block.c @@ -114,7 +114,6 @@ int main(int argc, char *argv[]) { _cleanup_(dissected_image_unrefp) DissectedImage *dissected = NULL; _cleanup_(umount_and_rmdir_and_freep) char *mounted = NULL; pthread_t threads[N_THREADS]; - const char *fs; sd_id128_t id; int r; diff --git a/src/test/test-macro.c b/src/test/test-macro.c index d33d22c18..ba319953c 100644 --- a/src/test/test-macro.c +++ b/src/test/test-macro.c @@ -340,7 +340,7 @@ TEST(flags) { assert_se(!FLAGS_SET(F1 | F2, F1 | F3)); assert_se(!FLAGS_SET(F1 | F2 | F3, ~F_ALL)); - // Check for no double eval. + /* Check for no double eval. */ n = F2; f = F1; assert_se(!FLAGS_SET(--n, ++f)); @@ -379,7 +379,7 @@ TEST(flags) { assert_se(UPDATE_FLAG(F1, F_ALL, false) == 0); assert_se(UPDATE_FLAG(F_ALL, F_ALL, false) == 0); - // Check for no double eval. + /* Check for no double eval. */ n = F2; f = F1; assert_se(UPDATE_FLAG(--n, ++f, true) == (F1 | F2)); @@ -387,4 +387,73 @@ TEST(flags) { assert_se(f == F2); } +TEST(DECIMAL_STR_WIDTH) { + assert_se(DECIMAL_STR_WIDTH(0) == 1); + assert_se(DECIMAL_STR_WIDTH(1) == 1); + assert_se(DECIMAL_STR_WIDTH(2) == 1); + assert_se(DECIMAL_STR_WIDTH(9) == 1); + assert_se(DECIMAL_STR_WIDTH(10) == 2); + assert_se(DECIMAL_STR_WIDTH(11) == 2); + assert_se(DECIMAL_STR_WIDTH(99) == 2); + assert_se(DECIMAL_STR_WIDTH(100) == 3); + assert_se(DECIMAL_STR_WIDTH(101) == 3); + assert_se(DECIMAL_STR_WIDTH(-1) == 2); + assert_se(DECIMAL_STR_WIDTH(-2) == 2); + assert_se(DECIMAL_STR_WIDTH(-9) == 2); + assert_se(DECIMAL_STR_WIDTH(-10) == 3); + assert_se(DECIMAL_STR_WIDTH(-11) == 3); + assert_se(DECIMAL_STR_WIDTH(-99) == 3); + assert_se(DECIMAL_STR_WIDTH(-100) == 4); + assert_se(DECIMAL_STR_WIDTH(-101) == 4); + assert_se(DECIMAL_STR_WIDTH(UINT64_MAX) == STRLEN("18446744073709551615")); + assert_se(DECIMAL_STR_WIDTH(INT64_MAX) == STRLEN("9223372036854775807")); + assert_se(DECIMAL_STR_WIDTH(INT64_MIN) == STRLEN("-9223372036854775808")); +} + +TEST(DECIMAL_STR_MAX) { + int8_t s8_longest = INT8_MIN; + int16_t s16_longest = INT16_MIN; + int32_t s32_longest = INT32_MIN; + int64_t s64_longest = INT64_MIN; + uint8_t u8_longest = UINT8_MAX; + uint16_t u16_longest = UINT16_MAX; + uint32_t u32_longest = UINT32_MAX; + uint64_t u64_longest = UINT64_MAX; + + /* NB: Always add +1, because DECIMAL_STR_MAX() includes space for trailing NUL byte, but + * DECIMAL_STR_WIDTH() does not! */ + assert_se(DECIMAL_STR_MAX(int8_t) == DECIMAL_STR_WIDTH(s8_longest)+1); + assert_se(DECIMAL_STR_MAX(int16_t) == DECIMAL_STR_WIDTH(s16_longest)+1); + assert_se(DECIMAL_STR_MAX(int32_t) == DECIMAL_STR_WIDTH(s32_longest)+1); + assert_se(DECIMAL_STR_MAX(int64_t) == DECIMAL_STR_WIDTH(s64_longest)+1); + + assert_se(DECIMAL_STR_MAX(uint8_t) == DECIMAL_STR_WIDTH(u8_longest)+1); + assert_se(DECIMAL_STR_MAX(uint16_t) == DECIMAL_STR_WIDTH(u16_longest)+1); + assert_se(DECIMAL_STR_MAX(uint32_t) == DECIMAL_STR_WIDTH(u32_longest)+1); + assert_se(DECIMAL_STR_MAX(uint64_t) == DECIMAL_STR_WIDTH(u64_longest)+1); +} + +TEST(PTR_SUB1) { + static const uint64_t x[4] = { 2, 3, 4, 5 }; + const uint64_t *p; + + p = x + ELEMENTSOF(x)-1; + assert_se(*p == 5); + + p = PTR_SUB1(p, x); + assert_se(*p == 4); + + p = PTR_SUB1(p, x); + assert_se(*p == 3); + + p = PTR_SUB1(p, x); + assert_se(*p == 2); + + p = PTR_SUB1(p, x); + assert_se(!p); + + p = PTR_SUB1(p, x); + assert_se(!p); +} + DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/test/test-mount-util.c b/src/test/test-mount-util.c index 74d352268..7e06fc419 100644 --- a/src/test/test-mount-util.c +++ b/src/test/test-mount-util.c @@ -128,7 +128,6 @@ TEST(mount_flags_to_string) { TEST(bind_remount_recursive) { _cleanup_(rm_rf_physical_and_freep) char *tmp = NULL; _cleanup_free_ char *subdir = NULL; - const char *p; if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0) { (void) log_tests_skipped("not running privileged"); diff --git a/src/test/test-mountpoint-util.c b/src/test/test-mountpoint-util.c index d11edf502..3c2523006 100644 --- a/src/test/test-mountpoint-util.c +++ b/src/test/test-mountpoint-util.c @@ -18,7 +18,7 @@ #include "tmpfile-util.h" static void test_mount_propagation_flags_one(const char *name, int ret, unsigned long expected) { - long unsigned flags; + unsigned long flags; log_info("/* %s(%s) */", __func__, name); @@ -294,17 +294,19 @@ TEST(fd_is_mount_point) { assert_se(IN_SET(fd_is_mount_point(fd, "root/", 0), -ENOENT, 0)); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_DEBUG, - ({ - /* let's move into our own mount namespace with all propagation from the host turned off, so - * that /proc/self/mountinfo is static and constant for the whole time our test runs. */ - if (unshare(CLONE_NEWNS) < 0) { - if (!ERRNO_IS_PRIVILEGE(errno)) - return log_error_errno(errno, "Failed to detach mount namespace: %m"); +static int intro(void) { + /* let's move into our own mount namespace with all propagation from the host turned off, so + * that /proc/self/mountinfo is static and constant for the whole time our test runs. */ - log_notice("Lacking privilege to create separate mount namespace, proceeding in originating mount namespace."); - } else - assert_se(mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL) >= 0); - }), - /* no outro */); + if (unshare(CLONE_NEWNS) < 0) { + if (!ERRNO_IS_PRIVILEGE(errno)) + return log_error_errno(errno, "Failed to detach mount namespace: %m"); + + log_notice("Lacking privilege to create separate mount namespace, proceeding in originating mount namespace."); + } else + assert_se(mount(NULL, "/", NULL, MS_PRIVATE | MS_REC, NULL) >= 0); + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c index 8df5533d6..37acc782e 100644 --- a/src/test/test-namespace.c +++ b/src/test/test-namespace.c @@ -207,6 +207,8 @@ TEST(protect_kernel_logs) { NULL, NULL, NULL, + NULL, + NULL, NULL); assert_se(r == 0); @@ -220,10 +222,11 @@ TEST(protect_kernel_logs) { assert_se(wait_for_terminate_and_check("ns-kernellogs", pid, WAIT_LOG) == EXIT_SUCCESS); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_INFO, - ({ - if (!have_namespaces()) - return log_tests_skipped("Don't have namespace support"); - }), - /* no outro */); +static int intro(void) { + if (!have_namespaces()) + return log_tests_skipped("Don't have namespace support"); + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-ns.c b/src/test/test-ns.c index b03eabb59..7eb29d109 100644 --- a/src/test/test-ns.c +++ b/src/test/test-ns.c @@ -108,6 +108,8 @@ int main(int argc, char *argv[]) { NULL, NULL, NULL, + NULL, + NULL, NULL); if (r < 0) { log_error_errno(r, "Failed to set up namespace: %m"); diff --git a/src/test/test-nss-hosts.c b/src/test/test-nss-hosts.c index eac2c74f4..69225da8b 100644 --- a/src/test/test-nss-hosts.c +++ b/src/test/test-nss-hosts.c @@ -67,8 +67,6 @@ static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) { } static void print_struct_hostent(struct hostent *host, const char *canon) { - char **s; - log_info(" \"%s\"", host->h_name); STRV_FOREACH(s, host->h_aliases) log_info(" alias \"%s\"", *s); @@ -376,7 +374,6 @@ static int test_one_module(const char *dir, if (!handle) return -EINVAL; - char **name; STRV_FOREACH(name, names) test_byname(handle, module, *name); @@ -420,11 +417,10 @@ static int parse_argv(int argc, char **argv, #if ENABLE_NSS_MYMACHINES "mymachines", #endif - "dns"); + NULL); assert_se(modules); if (argc > 2) { - char **name; int family; union in_addr_union address; @@ -463,7 +459,6 @@ static int run(int argc, char **argv) { _cleanup_strv_free_ char **modules = NULL, **names = NULL; _cleanup_free_ struct local_address *addresses = NULL; int n_addresses = 0; - char **module; int r; test_setup_logging(LOG_INFO); diff --git a/src/test/test-nss-users.c b/src/test/test-nss-users.c index c415c0ca3..5178779d5 100644 --- a/src/test/test-nss-users.c +++ b/src/test/test-nss-users.c @@ -170,7 +170,6 @@ static int test_one_module(const char *dir, if (!handle) return -EINVAL; - char **name; STRV_FOREACH(name, names) test_byname(handle, module, *name); @@ -214,7 +213,7 @@ static int parse_argv(int argc, char **argv, #if ENABLE_NSS_MYMACHINES "mymachines", #endif - "files"); + NULL); assert_se(modules); if (argc > 2) @@ -235,7 +234,6 @@ static int parse_argv(int argc, char **argv, static int run(int argc, char **argv) { _cleanup_free_ char *dir = NULL; _cleanup_strv_free_ char **modules = NULL, **names = NULL; - char **module; int r; test_setup_logging(LOG_INFO); diff --git a/src/test/test-os-util.c b/src/test/test-os-util.c index eb5466259..2cee6470c 100644 --- a/src/test/test-os-util.c +++ b/src/test/test-os-util.c @@ -2,8 +2,11 @@ #include +#include "fs-util.h" #include "log.h" #include "os-util.h" +#include "string-util.h" +#include "strv.h" #include "tests.h" TEST(path_is_os_tree) { @@ -12,4 +15,61 @@ TEST(path_is_os_tree) { assert_se(path_is_os_tree("/idontexist") == -ENOENT); } +TEST(parse_os_release) { + /* Let's assume that we're running in a valid system, so os-release is available */ + _cleanup_free_ char *id = NULL, *id2 = NULL, *name = NULL, *foobar = NULL; + assert_se(parse_os_release(NULL, "ID", &id) == 0); + log_info("ID: %s", id); + + assert_se(setenv("SYSTEMD_OS_RELEASE", "/dev/null", 1) == 0); + assert_se(parse_os_release(NULL, "ID", &id2) == 0); + log_info("ID: %s", strnull(id2)); + + _cleanup_(unlink_tempfilep) char tmpfile[] = "/tmp/test-os-util.XXXXXX"; + assert_se(write_tmpfile(tmpfile, + "ID=the-id \n" + "NAME=the-name") == 0); + + assert_se(setenv("SYSTEMD_OS_RELEASE", tmpfile, 1) == 0); + assert_se(parse_os_release(NULL, "ID", &id, "NAME", &name) == 0); + log_info("ID: %s NAME: %s", id, name); + assert_se(streq(id, "the-id")); + assert_se(streq(name, "the-name")); + + _cleanup_(unlink_tempfilep) char tmpfile2[] = "/tmp/test-os-util.XXXXXX"; + assert_se(write_tmpfile(tmpfile2, + "ID=\"ignored\" \n" + "ID=\"the-id\" \n" + "NAME='the-name'") == 0); + + assert_se(setenv("SYSTEMD_OS_RELEASE", tmpfile2, 1) == 0); + assert_se(parse_os_release(NULL, "ID", &id, "NAME", &name) == 0); + log_info("ID: %s NAME: %s", id, name); + assert_se(streq(id, "the-id")); + assert_se(streq(name, "the-name")); + + assert_se(parse_os_release(NULL, "FOOBAR", &foobar) == 0); + log_info("FOOBAR: %s", strnull(foobar)); + assert_se(foobar == NULL); + + assert_se(unsetenv("SYSTEMD_OS_RELEASE") == 0); +} + +TEST(load_os_release_pairs) { + _cleanup_(unlink_tempfilep) char tmpfile[] = "/tmp/test-os-util.XXXXXX"; + assert_se(write_tmpfile(tmpfile, + "ID=\"ignored\" \n" + "ID=\"the-id\" \n" + "NAME='the-name'") == 0); + + assert_se(setenv("SYSTEMD_OS_RELEASE", tmpfile, 1) == 0); + + _cleanup_strv_free_ char **pairs = NULL; + assert_se(load_os_release_pairs(NULL, &pairs) == 0); + assert_se(strv_equal(pairs, STRV_MAKE("ID", "the-id", + "NAME", "the-name"))); + + assert_se(unsetenv("SYSTEMD_OS_RELEASE") == 0); +} + DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/test/test-path-lookup.c b/src/test/test-path-lookup.c index a19a33c64..c98a1f4b7 100644 --- a/src/test/test-path-lookup.c +++ b/src/test/test-path-lookup.c @@ -9,49 +9,47 @@ #include "string-util.h" #include "strv.h" #include "tests.h" +#include "tmpfile-util.h" -static void test_paths_one(UnitFileScope scope) { - char template[] = "/tmp/test-path-lookup.XXXXXXX"; - +static void test_paths_one(LookupScope scope) { + _cleanup_(rm_rf_physical_and_freep) char *tmp = NULL; _cleanup_(lookup_paths_free) LookupPaths lp_without_env = {}; _cleanup_(lookup_paths_free) LookupPaths lp_with_env = {}; char *systemd_unit_path; - assert_se(mkdtemp(template)); + assert_se(mkdtemp_malloc("/tmp/test-path-lookup.XXXXXXX", &tmp) >= 0); assert_se(unsetenv("SYSTEMD_UNIT_PATH") == 0); assert_se(lookup_paths_init(&lp_without_env, scope, 0, NULL) >= 0); assert_se(!strv_isempty(lp_without_env.search_path)); lookup_paths_log(&lp_without_env); - systemd_unit_path = strjoina(template, "/systemd-unit-path"); + systemd_unit_path = strjoina(tmp, "/systemd-unit-path"); assert_se(setenv("SYSTEMD_UNIT_PATH", systemd_unit_path, 1) == 0); assert_se(lookup_paths_init(&lp_with_env, scope, 0, NULL) == 0); assert_se(strv_length(lp_with_env.search_path) == 1); assert_se(streq(lp_with_env.search_path[0], systemd_unit_path)); lookup_paths_log(&lp_with_env); assert_se(strv_equal(lp_with_env.search_path, STRV_MAKE(systemd_unit_path))); - - assert_se(rm_rf(template, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); } TEST(paths) { - test_paths_one(UNIT_FILE_SYSTEM); - test_paths_one(UNIT_FILE_USER); - test_paths_one(UNIT_FILE_GLOBAL); + test_paths_one(LOOKUP_SCOPE_SYSTEM); + test_paths_one(LOOKUP_SCOPE_USER); + test_paths_one(LOOKUP_SCOPE_GLOBAL); } TEST(user_and_global_paths) { _cleanup_(lookup_paths_free) LookupPaths lp_global = {}, lp_user = {}; - char **u, **g, **p; + char **u, **g; unsigned k = 0; assert_se(unsetenv("SYSTEMD_UNIT_PATH") == 0); assert_se(unsetenv("XDG_DATA_DIRS") == 0); assert_se(unsetenv("XDG_CONFIG_DIRS") == 0); - assert_se(lookup_paths_init(&lp_global, UNIT_FILE_GLOBAL, 0, NULL) == 0); - assert_se(lookup_paths_init(&lp_user, UNIT_FILE_USER, 0, NULL) == 0); + assert_se(lookup_paths_init(&lp_global, LOOKUP_SCOPE_GLOBAL, 0, NULL) == 0); + assert_se(lookup_paths_init(&lp_user, LOOKUP_SCOPE_USER, 0, NULL) == 0); g = lp_global.search_path; u = lp_user.search_path; @@ -72,49 +70,47 @@ TEST(user_and_global_paths) { log_info("+ %s", *p); } -static void test_generator_binary_paths_one(UnitFileScope scope) { - char template[] = "/tmp/test-path-lookup.XXXXXXX"; - +static void test_generator_binary_paths_one(LookupScope scope) { + _cleanup_(rm_rf_physical_and_freep) char *tmp = NULL; _cleanup_strv_free_ char **gp_without_env = NULL; _cleanup_strv_free_ char **env_gp_without_env = NULL; _cleanup_strv_free_ char **gp_with_env = NULL; _cleanup_strv_free_ char **env_gp_with_env = NULL; char *systemd_generator_path = NULL; char *systemd_env_generator_path = NULL; - char **dir; - assert_se(mkdtemp(template)); + assert_se(mkdtemp_malloc("/tmp/test-path-lookup.XXXXXXX", &tmp) >= 0); assert_se(unsetenv("SYSTEMD_GENERATOR_PATH") == 0); assert_se(unsetenv("SYSTEMD_ENVIRONMENT_GENERATOR_PATH") == 0); gp_without_env = generator_binary_paths(scope); - env_gp_without_env = env_generator_binary_paths(scope == UNIT_FILE_SYSTEM ? true : false); + env_gp_without_env = env_generator_binary_paths(scope == LOOKUP_SCOPE_SYSTEM ? true : false); - log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user"); + log_info("Generators dirs (%s):", scope == LOOKUP_SCOPE_SYSTEM ? "system" : "user"); STRV_FOREACH(dir, gp_without_env) log_info(" %s", *dir); - log_info("Environment generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user"); + log_info("Environment generators dirs (%s):", scope == LOOKUP_SCOPE_SYSTEM ? "system" : "user"); STRV_FOREACH(dir, env_gp_without_env) log_info(" %s", *dir); assert_se(!strv_isempty(gp_without_env)); assert_se(!strv_isempty(env_gp_without_env)); - systemd_generator_path = strjoina(template, "/systemd-generator-path"); - systemd_env_generator_path = strjoina(template, "/systemd-environment-generator-path"); + systemd_generator_path = strjoina(tmp, "/systemd-generator-path"); + systemd_env_generator_path = strjoina(tmp, "/systemd-environment-generator-path"); assert_se(setenv("SYSTEMD_GENERATOR_PATH", systemd_generator_path, 1) == 0); assert_se(setenv("SYSTEMD_ENVIRONMENT_GENERATOR_PATH", systemd_env_generator_path, 1) == 0); gp_with_env = generator_binary_paths(scope); - env_gp_with_env = env_generator_binary_paths(scope == UNIT_FILE_SYSTEM ? true : false); + env_gp_with_env = env_generator_binary_paths(scope == LOOKUP_SCOPE_SYSTEM ? true : false); - log_info("Generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user"); + log_info("Generators dirs (%s):", scope == LOOKUP_SCOPE_SYSTEM ? "system" : "user"); STRV_FOREACH(dir, gp_with_env) log_info(" %s", *dir); - log_info("Environment generators dirs (%s):", scope == UNIT_FILE_SYSTEM ? "system" : "user"); + log_info("Environment generators dirs (%s):", scope == LOOKUP_SCOPE_SYSTEM ? "system" : "user"); STRV_FOREACH(dir, env_gp_with_env) log_info(" %s", *dir); @@ -123,8 +119,8 @@ static void test_generator_binary_paths_one(UnitFileScope scope) { } TEST(generator_binary_paths) { - test_generator_binary_paths_one(UNIT_FILE_SYSTEM); - test_generator_binary_paths_one(UNIT_FILE_USER); + test_generator_binary_paths_one(LOOKUP_SCOPE_SYSTEM); + test_generator_binary_paths_one(LOOKUP_SCOPE_USER); } DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index b9c4ef412..d40febef5 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -481,7 +481,6 @@ TEST(path_strv_resolve) { char tmp_dir[] = "/tmp/test-path-util-XXXXXX"; _cleanup_strv_free_ char **search_dirs = NULL; _cleanup_strv_free_ char **absolute_dirs = NULL; - char **d; assert_se(mkdtemp(tmp_dir) != NULL); diff --git a/src/test/test-path.c b/src/test/test-path.c index 529487d1a..7fb1f7363 100644 --- a/src/test/test-path.c +++ b/src/test/test-path.c @@ -24,7 +24,6 @@ typedef void (*test_function_t)(Manager *m); static int setup_test(Manager **m) { char **tests_path = STRV_MAKE("exists", "existsglobFOOBAR", "changed", "modified", "unit", "directorynotempty", "makedirectory"); - char **test_path; Manager *tmp = NULL; int r; @@ -34,7 +33,7 @@ static int setup_test(Manager **m) { if (r == -ENOMEDIUM) return log_tests_skipped("cgroupfs not available"); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &tmp); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &tmp); if (manager_errno_skip_test(r)) return log_tests_skipped_errno(r, "manager_new"); assert_se(r >= 0); diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c index 1c8c9b80b..1f43bb3eb 100644 --- a/src/test/test-proc-cmdline.c +++ b/src/test/test-proc-cmdline.c @@ -247,10 +247,11 @@ TEST(proc_cmdline_key_startswith) { assert_se(!proc_cmdline_key_startswith("foo-bar", "foo_xx")); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_INFO, - ({ - if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) - return log_tests_skipped("can't read /proc/cmdline"); - }), - /* no outro */); +static int intro(void) { + if (access("/proc/cmdline", R_OK) < 0 && ERRNO_IS_PRIVILEGE(errno)) + return log_tests_skipped("can't read /proc/cmdline"); + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index 06a640b1c..7a8adad50 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -895,4 +895,9 @@ TEST(set_oom_score_adjust) { assert_se(b == a); } -DEFINE_CUSTOM_TEST_MAIN(LOG_INFO, log_show_color(true), /* no outro */); +static int intro(void) { + log_show_color(true); + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-random-util.c b/src/test/test-random-util.c index 2b09a4513..8cd457d57 100644 --- a/src/test/test-random-util.c +++ b/src/test/test-random-util.c @@ -24,11 +24,8 @@ static void test_genuine_random_bytes_one(RandomFlags flags) { } TEST(genuine_random_bytes) { - test_genuine_random_bytes_one(RANDOM_EXTEND_WITH_PSEUDO); test_genuine_random_bytes_one(0); test_genuine_random_bytes_one(RANDOM_BLOCK); - test_genuine_random_bytes_one(RANDOM_ALLOW_RDRAND); - test_genuine_random_bytes_one(RANDOM_ALLOW_INSECURE); } TEST(pseudo_random_bytes) { @@ -43,22 +40,6 @@ TEST(pseudo_random_bytes) { } } -TEST(rdrand) { - int r; - - for (unsigned i = 0; i < 10; i++) { - unsigned long x = 0; - - r = rdrand(&x); - if (r < 0) { - log_error_errno(r, "RDRAND failed: %m"); - return; - } - - printf("%lx\n", x); - } -} - #define TOTAL 100000 static void test_random_u64_range_one(unsigned mod) { diff --git a/src/test/test-sched-prio.c b/src/test/test-sched-prio.c index 35f7be491..721c4b61a 100644 --- a/src/test/test-sched-prio.c +++ b/src/test/test-sched-prio.c @@ -30,7 +30,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m); if (manager_errno_skip_test(r)) return log_tests_skipped_errno(r, "manager_new"); assert_se(r >= 0); diff --git a/src/test/test-sd-hwdb.c b/src/test/test-sd-hwdb.c index 7961c17c4..4251e2a80 100644 --- a/src/test/test-sd-hwdb.c +++ b/src/test/test-sd-hwdb.c @@ -52,12 +52,15 @@ TEST(basic_enumerate) { assert_se(len1 == len2); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_DEBUG, - ({ - _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; - int r = sd_hwdb_new(&hwdb); - if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r)) - return log_tests_skipped_errno(r, "cannot open hwdb"); - }), - /* no outro */); +static int intro(void) { + _cleanup_(sd_hwdb_unrefp) sd_hwdb *hwdb = NULL; + int r; + + r = sd_hwdb_new(&hwdb); + if (r == -ENOENT || ERRNO_IS_PRIVILEGE(r)) + return log_tests_skipped_errno(r, "cannot open hwdb"); + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/test/test-sd-path.c b/src/test/test-sd-path.c index 10a8a4a63..4f23e3bb6 100644 --- a/src/test/test-sd-path.c +++ b/src/test/test-sd-path.c @@ -32,7 +32,6 @@ TEST(sd_path_lookup) { TEST(sd_path_lookup_strv) { for (uint64_t i = 0; i < _SD_PATH_MAX; i++) { _cleanup_strv_free_ char **t = NULL, **s = NULL; - char **item; int r; r = sd_path_lookup_strv(i, NULL, &t); diff --git a/src/test/test-serialize.c b/src/test/test-serialize.c index fb04b3e7f..bcf2e843b 100644 --- a/src/test/test-serialize.c +++ b/src/test/test-serialize.c @@ -10,7 +10,7 @@ #include "tests.h" #include "tmpfile-util.h" -char long_string[LONG_LINE_MAX+1]; +static char long_string[LONG_LINE_MAX+1]; TEST(serialize_item) { _cleanup_(unlink_tempfilep) char fn[] = "/tmp/test-serialize.XXXXXX"; @@ -189,10 +189,10 @@ TEST(serialize_environment) { assert_se(strv_equal(env, env2)); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_INFO, - ({ - memset(long_string, 'x', sizeof(long_string)-1); - char_array_0(long_string); - }), - /* no outro */); +static int intro(void) { + memset(long_string, 'x', sizeof(long_string)-1); + char_array_0(long_string); + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-sizeof.c b/src/test/test-sizeof.c index f34985255..55bd81e22 100644 --- a/src/test/test-sizeof.c +++ b/src/test/test-sizeof.c @@ -53,8 +53,8 @@ int main(void) { info(unsigned char); info(short unsigned); info(unsigned); - info(long unsigned); - info(long long unsigned); + info(unsigned long); + info(unsigned long long); info(__syscall_ulong_t); info(__syscall_slong_t); info(intmax_t); diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c index 183ad4f7b..5aebcdd93 100644 --- a/src/test/test-sleep.c +++ b/src/test/test-sleep.c @@ -118,10 +118,11 @@ TEST(sleep) { log_info("Suspend-then-Hibernate configured and possible: %s", r >= 0 ? yes_no(r) : strerror_safe(r)); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_DEBUG, - ({ - if (getuid() != 0) - log_warning("This program is unlikely to work for unprivileged users"); - }), - /* no outro */); +static int intro(void) { + if (getuid() != 0) + log_warning("This program is unlikely to work for unprivileged users"); + + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/test/test-socket-bind.c b/src/test/test-socket-bind.c index ecad86bae..59d8cc965 100644 --- a/src/test/test-socket-bind.c +++ b/src/test/test-socket-bind.c @@ -13,7 +13,7 @@ #include "virt.h" static int find_netcat_executable(char **ret_path) { - char **candidates = STRV_MAKE("ncat", "nc", "netcat"), **c; + char **candidates = STRV_MAKE("ncat", "nc", "netcat"); int r = 0; STRV_FOREACH(c, candidates) { @@ -34,9 +34,7 @@ static int test_socket_bind( char **deny_rules) { _cleanup_free_ char *exec_start = NULL; _cleanup_(unit_freep) Unit *u = NULL; - CGroupSocketBindItem *bi; CGroupContext *cc = NULL; - char **rule; int cld_code, r; assert_se(u = unit_new(m, sizeof(Service))); @@ -120,14 +118,14 @@ int main(int argc, char *argv[]) { (void) setrlimit_closest(RLIMIT_MEMLOCK, &rl); if (!can_memlock()) - return log_tests_skipped("Can't use mlock(), skipping."); + return log_tests_skipped("Can't use mlock()"); r = bpf_socket_bind_supported(); if (r <= 0) - return log_tests_skipped("socket-bind is not supported, skipping."); + return log_tests_skipped("socket-bind is not supported"); if (find_netcat_executable(&netcat_path) != 0) - return log_tests_skipped("Can not find netcat executable, skipping."); + return log_tests_skipped("Can not find netcat executable"); r = enter_cgroup_subroot(NULL); if (r == -ENOMEDIUM) @@ -137,7 +135,7 @@ int main(int argc, char *argv[]) { assert_se(set_unit_path(unit_dir) >= 0); assert_se(runtime_dir = setup_fake_runtime_dir()); - assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); assert_se(test_socket_bind(m, "socket_bind_test.service", netcat_path, "2000", STRV_MAKE("2000"), STRV_MAKE("any")) >= 0); diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index 9ee651a5f..3245516f9 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -228,17 +228,12 @@ TEST(passfd_read) { if (r == 0) { /* Child */ - char tmpfile[] = "/tmp/test-socket-util-passfd-read-XXXXXX"; - _cleanup_close_ int tmpfd = -1; - pair[0] = safe_close(pair[0]); - tmpfd = mkostemp_safe(tmpfile); - assert_se(tmpfd >= 0); - assert_se(write(tmpfd, file_contents, strlen(file_contents)) == (ssize_t) strlen(file_contents)); - tmpfd = safe_close(tmpfd); + char tmpfile[] = "/tmp/test-socket-util-passfd-read-XXXXXX"; + assert_se(write_tmpfile(tmpfile, file_contents) == 0); - tmpfd = open(tmpfile, O_RDONLY); + _cleanup_close_ int tmpfd = open(tmpfile, O_RDONLY); assert_se(tmpfd >= 0); assert_se(unlink(tmpfile) == 0); @@ -277,16 +272,12 @@ TEST(passfd_contents_read) { /* Child */ struct iovec iov = IOVEC_INIT_STRING(wire_contents); char tmpfile[] = "/tmp/test-socket-util-passfd-contents-read-XXXXXX"; - _cleanup_close_ int tmpfd = -1; pair[0] = safe_close(pair[0]); - tmpfd = mkostemp_safe(tmpfile); - assert_se(tmpfd >= 0); - assert_se(write(tmpfd, file_contents, strlen(file_contents)) == (ssize_t) strlen(file_contents)); - tmpfd = safe_close(tmpfd); + assert_se(write_tmpfile(tmpfile, file_contents) == 0); - tmpfd = open(tmpfile, O_RDONLY); + _cleanup_close_ int tmpfd = open(tmpfile, O_RDONLY); assert_se(tmpfd >= 0); assert_se(unlink(tmpfile) == 0); diff --git a/src/test/test-specifier.c b/src/test/test-specifier.c index 40957eeb5..49b4f5a2a 100644 --- a/src/test/test-specifier.c +++ b/src/test/test-specifier.c @@ -3,10 +3,12 @@ #include "alloc-util.h" #include "log.h" #include "specifier.h" +#include "stat-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "tests.h" +#include "unit-file.h" static void test_specifier_escape_one(const char *a, const char *b) { _cleanup_free_ char *x = NULL; @@ -45,7 +47,7 @@ TEST(specifier_escape_strv) { static const Specifier specifier_table[] = { COMMON_SYSTEM_SPECIFIERS, - COMMON_CREDS_SPECIFIERS, + COMMON_CREDS_SPECIFIERS(LOOKUP_SCOPE_USER), { 'h', specifier_user_home, NULL }, COMMON_TMP_SPECIFIERS, @@ -56,6 +58,7 @@ TEST(specifier_printf) { static const Specifier table[] = { { 'X', specifier_string, (char*) "AAAA" }, { 'Y', specifier_string, (char*) "BBBB" }, + { 'e', specifier_string, NULL }, COMMON_SYSTEM_SPECIFIERS, {} }; @@ -63,25 +66,66 @@ TEST(specifier_printf) { _cleanup_free_ char *w = NULL; int r; - r = specifier_printf("xxx a=%X b=%Y yyy", SIZE_MAX, table, NULL, NULL, &w); + r = specifier_printf("xxx a=%X b=%Y e=%e yyy", SIZE_MAX, table, NULL, NULL, &w); assert_se(r >= 0); assert_se(w); puts(w); - assert_se(streq(w, "xxx a=AAAA b=BBBB yyy")); + assert_se(streq(w, "xxx a=AAAA b=BBBB e= yyy")); free(w); - r = specifier_printf("machine=%m, boot=%b, host=%H, version=%v, arch=%a", SIZE_MAX, table, NULL, NULL, &w); + r = specifier_printf("machine=%m, boot=%b, host=%H, pretty=%R, version=%v, arch=%a, empty=%e", SIZE_MAX, table, NULL, NULL, &w); assert_se(r >= 0); assert_se(w); puts(w); w = mfree(w); - specifier_printf("os=%o, os-version=%w, build=%B, variant=%W", SIZE_MAX, table, NULL, NULL, &w); + specifier_printf("os=%o, os-version=%w, build=%B, variant=%W, empty=%e%e%e", SIZE_MAX, table, NULL, NULL, &w); if (w) puts(w); } +TEST(specifier_real_path) { + static const Specifier table[] = { + { 'p', specifier_string, "/dev/initctl" }, + { 'y', specifier_real_path, "/dev/initctl" }, + { 'Y', specifier_real_directory, "/dev/initctl" }, + { 'w', specifier_real_path, "/dev/tty" }, + { 'W', specifier_real_directory, "/dev/tty" }, + {} + }; + + _cleanup_free_ char *w = NULL; + int r; + + r = specifier_printf("p=%p y=%y Y=%Y w=%w W=%W", SIZE_MAX, table, NULL, NULL, &w); + assert_se(r >= 0 || r == -ENOENT); + assert_se(w || r == -ENOENT); + puts(strnull(w)); + + /* /dev/initctl should normally be a symlink to /run/initctl */ + if (files_same("/dev/initctl", "/run/initctl", 0) > 0) + assert_se(streq(w, "p=/dev/initctl y=/run/initctl Y=/run w=/dev/tty W=/dev")); +} + +TEST(specifier_real_path_missing_file) { + static const Specifier table[] = { + { 'p', specifier_string, "/dev/-no-such-file--" }, + { 'y', specifier_real_path, "/dev/-no-such-file--" }, + { 'Y', specifier_real_directory, "/dev/-no-such-file--" }, + {} + }; + + _cleanup_free_ char *w = NULL; + int r; + + r = specifier_printf("p=%p y=%y", SIZE_MAX, table, NULL, NULL, &w); + assert_se(r == -ENOENT); + + r = specifier_printf("p=%p Y=%Y", SIZE_MAX, table, NULL, NULL, &w); + assert_se(r == -ENOENT); +} + TEST(specifiers) { for (const Specifier *s = specifier_table; s->specifier; s++) { char spec[3]; @@ -95,4 +139,18 @@ TEST(specifiers) { } } +TEST(specifiers_missing_data_ok) { + _cleanup_free_ char *resolved = NULL; + + assert_se(setenv("SYSTEMD_OS_RELEASE", "/dev/null", 1) == 0); + assert_se(specifier_printf("%A-%B-%M-%o-%w-%W", SIZE_MAX, specifier_table, NULL, NULL, &resolved) >= 0); + assert_se(streq(resolved, "-----")); + + assert_se(setenv("SYSTEMD_OS_RELEASE", "/nosuchfileordirectory", 1) == 0); + assert_se(specifier_printf("%A-%B-%M-%o-%w-%W", SIZE_MAX, specifier_table, NULL, NULL, &resolved) == -EUNATCH); + assert_se(streq(resolved, "-----")); + + assert_se(unsetenv("SYSTEMD_OS_RELEASE") == 0); +} + DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c index 0f7b3ca3c..9975a1848 100644 --- a/src/test/test-stat-util.c +++ b/src/test/test-stat-util.c @@ -18,6 +18,30 @@ #include "tests.h" #include "tmpfile-util.h" +TEST(null_or_empty_path) { + assert_se(null_or_empty_path("/dev/null") == 1); + assert_se(null_or_empty_path("/dev/tty") == 1); /* We assume that any character device is "empty", bleh. */ + assert_se(null_or_empty_path("../../../../../../../../../../../../../../../../../../../../dev/null") == 1); + assert_se(null_or_empty_path("/proc/self/exe") == 0); + assert_se(null_or_empty_path("/nosuchfileordir") == -ENOENT); +} + +TEST(null_or_empty_path_with_root) { + assert_se(null_or_empty_path_with_root("/dev/null", NULL) == 1); + assert_se(null_or_empty_path_with_root("/dev/null", "/") == 1); + assert_se(null_or_empty_path_with_root("/dev/null", "/.././../") == 1); + assert_se(null_or_empty_path_with_root("/dev/null", "/.././..") == 1); + assert_se(null_or_empty_path_with_root("../../../../../../../../../../../../../../../../../../../../dev/null", NULL) == 1); + assert_se(null_or_empty_path_with_root("../../../../../../../../../../../../../../../../../../../../dev/null", "/") == 1); + assert_se(null_or_empty_path_with_root("/proc/self/exe", NULL) == 0); + assert_se(null_or_empty_path_with_root("/proc/self/exe", "/") == 0); + assert_se(null_or_empty_path_with_root("/nosuchfileordir", NULL) == -ENOENT); + assert_se(null_or_empty_path_with_root("/nosuchfileordir", "/.././../") == -ENOENT); + assert_se(null_or_empty_path_with_root("/nosuchfileordir", "/.././..") == -ENOENT); + assert_se(null_or_empty_path_with_root("/foobar/barbar/dev/null", "/foobar/barbar") == 1); + assert_se(null_or_empty_path_with_root("/foobar/barbar/dev/null", "/foobar/barbar/") == 1); +} + TEST(files_same) { _cleanup_close_ int fd = -1; char name[] = "/tmp/test-files_same.XXXXXX"; @@ -67,7 +91,6 @@ TEST(path_is_fs_type) { } TEST(path_is_temporary_fs) { - const char *s; int r; FOREACH_STRING(s, "/", "/run", "/sys", "/sys/", "/proc", "/i-dont-exist", "/var", "/var/lib") { @@ -85,7 +108,6 @@ TEST(path_is_temporary_fs) { } TEST(path_is_read_only_fs) { - const char *s; int r; FOREACH_STRING(s, "/", "/run", "/sys", "/sys/", "/proc", "/i-dont-exist", "/var", "/var/lib") { @@ -236,4 +258,9 @@ TEST(dir_is_empty) { assert_se(dir_is_empty_at(AT_FDCWD, empty_dir) > 0); } -DEFINE_CUSTOM_TEST_MAIN(LOG_INFO, log_show_color(true), /* no outro */); +static int intro(void) { + log_show_color(true); + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index 071b39136..93b674baa 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -865,7 +865,6 @@ TEST(strverscmp_improved) { "124", NULL, }; - const char * const *p, * const *q; STRV_FOREACH(p, versions) STRV_FOREACH(q, p + 1) diff --git a/src/test/test-strv.c b/src/test/test-strv.c index 94581fc83..dabb0cd97 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -204,7 +204,6 @@ static void test_strv_unquote_one(const char *quoted, char **list) { _cleanup_strv_free_ char **s; _cleanup_free_ char *j; unsigned i = 0; - char **t; int r; log_info("/* %s */", __func__); @@ -446,7 +445,6 @@ TEST(strv_split_colon_pairs) { TEST(strv_split_newlines) { unsigned i = 0; - char **s; _cleanup_strv_free_ char **l = NULL; const char str[] = "one\ntwo\nthree"; @@ -619,7 +617,6 @@ TEST(strv_extendf) { TEST(strv_foreach) { _cleanup_strv_free_ char **a; unsigned i = 0; - char **check; a = strv_new("one", "two", "three"); assert_se(a); @@ -631,7 +628,6 @@ TEST(strv_foreach) { TEST(strv_foreach_backwards) { _cleanup_strv_free_ char **a; unsigned i = 2; - char **check; a = strv_new("one", "two", "three"); @@ -643,13 +639,17 @@ TEST(strv_foreach_backwards) { STRV_FOREACH_BACKWARDS(check, (char**) NULL) assert_not_reached(); - STRV_FOREACH_BACKWARDS(check, (char**) { NULL }) + STRV_FOREACH_BACKWARDS(check, STRV_MAKE_EMPTY) assert_not_reached(); + + unsigned count = 0; + STRV_FOREACH_BACKWARDS(check, STRV_MAKE("ONE")) + count++; + assert_se(count == 1); } TEST(strv_foreach_pair) { _cleanup_strv_free_ char **a = NULL; - char **x, **y; a = strv_new("pair_one", "pair_one", "pair_two", "pair_two", @@ -924,12 +924,10 @@ TEST(foreach_string) { "waldo", NULL }; - const char *x; - unsigned i = 0; + unsigned i = 0; FOREACH_STRING(x, "foo", "bar", "waldo") assert_se(streq_ptr(t[i++], x)); - assert_se(i == 3); FOREACH_STRING(x, "zzz") diff --git a/src/test/test-strxcpyx.c b/src/test/test-strxcpyx.c index 4e2118979..dd8dbdea6 100644 --- a/src/test/test-strxcpyx.c +++ b/src/test/test-strxcpyx.c @@ -11,34 +11,86 @@ TEST(strpcpy) { char target[25]; char *s = target; size_t space_left; + bool truncated; space_left = sizeof(target); - space_left = strpcpy(&s, space_left, "12345"); - space_left = strpcpy(&s, space_left, "hey hey hey"); - space_left = strpcpy(&s, space_left, "waldo"); - space_left = strpcpy(&s, space_left, "ba"); - space_left = strpcpy(&s, space_left, "r"); - space_left = strpcpy(&s, space_left, "foo"); - + space_left = strpcpy_full(&s, space_left, "12345", &truncated); + assert_se(!truncated); + space_left = strpcpy_full(&s, space_left, "hey hey hey", &truncated); + assert_se(!truncated); + space_left = strpcpy_full(&s, space_left, "waldo", &truncated); + assert_se(!truncated); + space_left = strpcpy_full(&s, space_left, "ba", &truncated); + assert_se(!truncated); + space_left = strpcpy_full(&s, space_left, "r", &truncated); + assert_se(!truncated); + assert_se(space_left == 1); assert_se(streq(target, "12345hey hey heywaldobar")); + + space_left = strpcpy_full(&s, space_left, "", &truncated); + assert_se(!truncated); + assert_se(space_left == 1); + assert_se(streq(target, "12345hey hey heywaldobar")); + + space_left = strpcpy_full(&s, space_left, "f", &truncated); + assert_se(truncated); assert_se(space_left == 0); + assert_se(streq(target, "12345hey hey heywaldobar")); + + space_left = strpcpy_full(&s, space_left, "", &truncated); + assert_se(!truncated); + assert_se(space_left == 0); + assert_se(streq(target, "12345hey hey heywaldobar")); + + space_left = strpcpy_full(&s, space_left, "foo", &truncated); + assert_se(truncated); + assert_se(space_left == 0); + assert_se(streq(target, "12345hey hey heywaldobar")); } TEST(strpcpyf) { char target[25]; char *s = target; size_t space_left; + bool truncated; space_left = sizeof(target); - space_left = strpcpyf(&s, space_left, "space left: %zu. ", space_left); - space_left = strpcpyf(&s, space_left, "foo%s", "bar"); - - assert_se(streq(target, "space left: 25. foobar")); + space_left = strpcpyf_full(&s, space_left, &truncated, "space left: %zu. ", space_left); + assert_se(!truncated); + space_left = strpcpyf_full(&s, space_left, &truncated, "foo%s", "bar"); + assert_se(!truncated); assert_se(space_left == 3); + assert_se(streq(target, "space left: 25. foobar")); + + space_left = strpcpyf_full(&s, space_left, &truncated, "%i", 42); + assert_se(!truncated); + assert_se(space_left == 1); + assert_se(streq(target, "space left: 25. foobar42")); + + space_left = strpcpyf_full(&s, space_left, &truncated, "%s", ""); + assert_se(!truncated); + assert_se(space_left == 1); + assert_se(streq(target, "space left: 25. foobar42")); + + space_left = strpcpyf_full(&s, space_left, &truncated, "%c", 'x'); + assert_se(truncated); + assert_se(space_left == 0); + assert_se(streq(target, "space left: 25. foobar42")); + + space_left = strpcpyf_full(&s, space_left, &truncated, "%s", ""); + assert_se(!truncated); + assert_se(space_left == 0); + assert_se(streq(target, "space left: 25. foobar42")); + + space_left = strpcpyf_full(&s, space_left, &truncated, "abc%s", "hoge"); + assert_se(truncated); + assert_se(space_left == 0); + assert_se(streq(target, "space left: 25. foobar42")); /* test overflow */ s = target; - space_left = strpcpyf(&s, 12, "00 left: %i. ", 999); + space_left = strpcpyf_full(&s, 12, &truncated, "00 left: %i. ", 999); + assert_se(truncated); assert_se(streq(target, "00 left: 99")); assert_se(space_left == 0); assert_se(target[12] == '2'); @@ -48,21 +100,40 @@ TEST(strpcpyl) { char target[25]; char *s = target; size_t space_left; + bool truncated; space_left = sizeof(target); - space_left = strpcpyl(&s, space_left, "waldo", " test", " waldo. ", NULL); - space_left = strpcpyl(&s, space_left, "Banana", NULL); - - assert_se(streq(target, "waldo test waldo. Banana")); + space_left = strpcpyl_full(&s, space_left, &truncated, "waldo", " test", " waldo. ", NULL); + assert_se(!truncated); + space_left = strpcpyl_full(&s, space_left, &truncated, "Banana", NULL); + assert_se(!truncated); assert_se(space_left == 1); + assert_se(streq(target, "waldo test waldo. Banana")); + + space_left = strpcpyl_full(&s, space_left, &truncated, "", "", "", NULL); + assert_se(!truncated); + assert_se(space_left == 1); + assert_se(streq(target, "waldo test waldo. Banana")); + + space_left = strpcpyl_full(&s, space_left, &truncated, "", "x", "", NULL); + assert_se(truncated); + assert_se(space_left == 0); + assert_se(streq(target, "waldo test waldo. Banana")); + + space_left = strpcpyl_full(&s, space_left, &truncated, "hoge", NULL); + assert_se(truncated); + assert_se(space_left == 0); + assert_se(streq(target, "waldo test waldo. Banana")); } TEST(strscpy) { char target[25]; size_t space_left; + bool truncated; space_left = sizeof(target); - space_left = strscpy(target, space_left, "12345"); + space_left = strscpy_full(target, space_left, "12345", &truncated); + assert_se(!truncated); assert_se(streq(target, "12345")); assert_se(space_left == 20); @@ -71,9 +142,11 @@ TEST(strscpy) { TEST(strscpyl) { char target[25]; size_t space_left; + bool truncated; space_left = sizeof(target); - space_left = strscpyl(target, space_left, "12345", "waldo", "waldo", NULL); + space_left = strscpyl_full(target, space_left, &truncated, "12345", "waldo", "waldo", NULL); + assert_se(!truncated); assert_se(streq(target, "12345waldowaldo")); assert_se(space_left == 10); diff --git a/src/test/test-sysctl-util.c b/src/test/test-sysctl-util.c index 8bd3c2615..02180dc4b 100644 --- a/src/test/test-sysctl-util.c +++ b/src/test/test-sysctl-util.c @@ -27,7 +27,6 @@ static const char* const cases[] = { }; TEST(sysctl_normalize) { - const char **s, **expected; STRV_FOREACH_PAIR(s, expected, (const char**) cases) { _cleanup_free_ char *t; diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index 4d0131827..86a60604d 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -258,7 +258,6 @@ TEST(timezone_is_valid) { TEST(get_timezones) { _cleanup_strv_free_ char **zones = NULL; int r; - char **zone; r = get_timezones(&zones); assert_se(r == 0); @@ -325,6 +324,11 @@ TEST(format_timestamp) { assert_se(parse_timestamp(buf, &y) >= 0); assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); + assert_se(format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_UNIX)); + log_debug("%s", buf); + assert_se(parse_timestamp(buf, &y) >= 0); + assert_se(x / USEC_PER_SEC == y / USEC_PER_SEC); + assert_se(format_timestamp_style(buf, sizeof(buf), x, TIMESTAMP_UTC)); log_debug("%s", buf); assert_se(parse_timestamp(buf, &y) >= 0); @@ -518,25 +522,25 @@ TEST(usec_shift_clock) { rt = now(CLOCK_REALTIME); mn = now(CLOCK_MONOTONIC); - bt = now(clock_boottime_or_monotonic()); + bt = now(CLOCK_BOOTTIME); assert_se(usec_shift_clock(USEC_INFINITY, CLOCK_REALTIME, CLOCK_MONOTONIC) == USEC_INFINITY); assert_similar(usec_shift_clock(rt + USEC_PER_HOUR, CLOCK_REALTIME, CLOCK_MONOTONIC), mn + USEC_PER_HOUR); - assert_similar(usec_shift_clock(rt + 2*USEC_PER_HOUR, CLOCK_REALTIME, clock_boottime_or_monotonic()), bt + 2*USEC_PER_HOUR); + assert_similar(usec_shift_clock(rt + 2*USEC_PER_HOUR, CLOCK_REALTIME, CLOCK_BOOTTIME), bt + 2*USEC_PER_HOUR); assert_se(usec_shift_clock(rt + 3*USEC_PER_HOUR, CLOCK_REALTIME, CLOCK_REALTIME_ALARM) == rt + 3*USEC_PER_HOUR); assert_similar(usec_shift_clock(mn + 4*USEC_PER_HOUR, CLOCK_MONOTONIC, CLOCK_REALTIME_ALARM), rt + 4*USEC_PER_HOUR); - assert_similar(usec_shift_clock(mn + 5*USEC_PER_HOUR, CLOCK_MONOTONIC, clock_boottime_or_monotonic()), bt + 5*USEC_PER_HOUR); + assert_similar(usec_shift_clock(mn + 5*USEC_PER_HOUR, CLOCK_MONOTONIC, CLOCK_BOOTTIME), bt + 5*USEC_PER_HOUR); assert_se(usec_shift_clock(mn + 6*USEC_PER_HOUR, CLOCK_MONOTONIC, CLOCK_MONOTONIC) == mn + 6*USEC_PER_HOUR); - assert_similar(usec_shift_clock(bt + 7*USEC_PER_HOUR, clock_boottime_or_monotonic(), CLOCK_MONOTONIC), mn + 7*USEC_PER_HOUR); - assert_similar(usec_shift_clock(bt + 8*USEC_PER_HOUR, clock_boottime_or_monotonic(), CLOCK_REALTIME_ALARM), rt + 8*USEC_PER_HOUR); - assert_se(usec_shift_clock(bt + 9*USEC_PER_HOUR, clock_boottime_or_monotonic(), clock_boottime_or_monotonic()) == bt + 9*USEC_PER_HOUR); + assert_similar(usec_shift_clock(bt + 7*USEC_PER_HOUR, CLOCK_BOOTTIME, CLOCK_MONOTONIC), mn + 7*USEC_PER_HOUR); + assert_similar(usec_shift_clock(bt + 8*USEC_PER_HOUR, CLOCK_BOOTTIME, CLOCK_REALTIME_ALARM), rt + 8*USEC_PER_HOUR); + assert_se(usec_shift_clock(bt + 9*USEC_PER_HOUR, CLOCK_BOOTTIME, CLOCK_BOOTTIME) == bt + 9*USEC_PER_HOUR); if (mn > USEC_PER_MINUTE) { assert_similar(usec_shift_clock(rt - 30 * USEC_PER_SEC, CLOCK_REALTIME_ALARM, CLOCK_MONOTONIC), mn - 30 * USEC_PER_SEC); - assert_similar(usec_shift_clock(rt - 50 * USEC_PER_SEC, CLOCK_REALTIME, clock_boottime_or_monotonic()), bt - 50 * USEC_PER_SEC); + assert_similar(usec_shift_clock(rt - 50 * USEC_PER_SEC, CLOCK_REALTIME, CLOCK_BOOTTIME), bt - 50 * USEC_PER_SEC); } } @@ -588,13 +592,13 @@ TEST(map_clock_usec) { } } -static void setup_test(void) { +static int intro(void) { log_info("realtime=" USEC_FMT "\n" "monotonic=" USEC_FMT "\n" "boottime=" USEC_FMT "\n", now(CLOCK_REALTIME), now(CLOCK_MONOTONIC), - now(clock_boottime_or_monotonic())); + now(CLOCK_BOOTTIME)); /* Ensure time_t is signed */ assert_cc((time_t) -1 < (time_t) 1); @@ -603,6 +607,8 @@ static void setup_test(void) { uintmax_t x = TIME_T_MAX; x++; assert_se((time_t) x < 0); + + return EXIT_SUCCESS; } -DEFINE_CUSTOM_TEST_MAIN(LOG_INFO, setup_test(), /* no outro */); +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-udev.c b/src/test/test-udev.c index c0e779a81..3ca132db3 100644 --- a/src/test/test-udev.c +++ b/src/test/test-udev.c @@ -25,6 +25,35 @@ #include "udev-event.h" #include "version.h" +static int device_new_from_synthetic_event(sd_device **ret, const char *syspath, const char *action) { + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + sd_device_action_t a; + int r; + + assert(ret); + assert(syspath); + assert(action); + + a = device_action_from_string(action); + if (a < 0) + return a; + + r = sd_device_new_from_syspath(&dev, syspath); + if (r < 0) + return r; + + r = device_read_uevent_file(dev); + if (r < 0) + return r; + + r = device_set_action(dev, a); + if (r < 0) + return r; + + *ret = TAKE_PTR(dev); + return 0; +} + static int fake_filesystems(void) { static const struct fakefs { const char *src; diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index 0f8c25c21..dffa2822e 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -8,20 +8,20 @@ #include "unit-file.h" TEST(unit_validate_alias_symlink_and_warn) { - assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b.service") == 0); - assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b.socket") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b.foobar") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b@.service") == 0); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b@.socket") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@YYY.service") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@YYY.socket") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b@YYY.service") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@XXX.service") == 0); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@XXX.service", "/other/b@.service") == 0); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@.service", "/other/b.service") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a.service", "/other/b@.service") == -EXDEV); - assert_se(unit_validate_alias_symlink_and_warn("/path/a@.slice", "/other/b.slice") == -EINVAL); - assert_se(unit_validate_alias_symlink_and_warn("/path/a.slice", "/other/b.slice") == -EINVAL); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.service") == 0); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.socket") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b.foobar") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@.service") == 0); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@.socket") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@YYY.service") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@YYY.socket") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b@YYY.service") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@XXX.service") == 0); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@XXX.service", "/other/b@.service") == 0); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.service", "/other/b.service") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.service", "/other/b@.service") == -EXDEV); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a@.slice", "/other/b.slice") == -EINVAL); + assert_se(unit_validate_alias_symlink_or_warn(LOG_INFO, "/path/a.slice", "/other/b.slice") == -EINVAL); } TEST(unit_file_build_name_map) { @@ -35,7 +35,7 @@ TEST(unit_file_build_name_map) { ids = strv_skip(saved_argv, 1); - assert_se(lookup_paths_init(&lp, UNIT_FILE_SYSTEM, 0, NULL) >= 0); + assert_se(lookup_paths_init(&lp, LOOKUP_SCOPE_SYSTEM, 0, NULL) >= 0); assert_se(unit_file_build_name_map(&lp, &mtime, &unit_ids, &unit_names, NULL) == 1); @@ -55,7 +55,6 @@ TEST(unit_file_build_name_map) { if (r == 0) log_debug("Cache rebuild skipped based on mtime."); - char **id; STRV_FOREACH(id, ids) { const char *fragment, *name; _cleanup_set_free_free_ Set *names = NULL; @@ -102,4 +101,9 @@ TEST(runlevel_to_target) { assert_se(streq_ptr(runlevel_to_target("rd.rescue"), SPECIAL_RESCUE_TARGET)); } -DEFINE_CUSTOM_TEST_MAIN(LOG_DEBUG, log_show_color(true), /* no outro */); +static int intro(void) { + log_show_color(true); + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 6bde9e090..d4dd8dc91 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -23,6 +23,10 @@ #include "user-util.h" #include "util.h" +static char *runtime_dir = NULL; + +STATIC_DESTRUCTOR_REGISTER(runtime_dir, rm_rf_physical_and_freep); + static void test_unit_name_is_valid_one(const char *name, UnitNameFlags flags, bool expected) { log_info("%s ( %s%s%s ): %s", name, @@ -224,7 +228,7 @@ TEST_RET(unit_printf, .sd_booted = true) { assert_se(get_home_dir(&home) >= 0); assert_se(get_shell(&shell) >= 0); - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (manager_errno_skip_test(r)) return log_tests_skipped_errno(r, "manager_new"); assert_se(r == 0); @@ -844,15 +848,12 @@ TEST(unit_name_prefix_equal) { assert_se(!unit_name_prefix_equal("a", "a")); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_INFO, +static int intro(void) { + if (enter_cgroup_subroot(NULL) == -ENOMEDIUM) + return log_tests_skipped("cgroupfs not available"); - _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; - ({ - if (enter_cgroup_subroot(NULL) == -ENOMEDIUM) - return log_tests_skipped("cgroupfs not available"); + assert_se(runtime_dir = setup_fake_runtime_dir()); + return EXIT_SUCCESS; +} - assert_se(runtime_dir = setup_fake_runtime_dir()); - }), - - /* no outro */); +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-unit-serialize.c b/src/test/test-unit-serialize.c index 899fdc000..f84435f48 100644 --- a/src/test/test-unit-serialize.c +++ b/src/test/test-unit-serialize.c @@ -4,6 +4,10 @@ #include "service.h" #include "tests.h" +static char *runtime_dir = NULL; + +STATIC_DESTRUCTOR_REGISTER(runtime_dir, rm_rf_physical_and_freep); + #define EXEC_START_ABSOLUTE \ "ExecStart 0 /bin/sh \"sh\" \"-e\" \"-x\" \"-c\" \"systemctl --state=failed --no-legend --no-pager >/failed ; systemctl daemon-reload ; echo OK >/testok\"" #define EXEC_START_RELATIVE \ @@ -27,7 +31,7 @@ TEST(deserialize_exec_command) { _cleanup_(manager_freep) Manager *m = NULL; int r; - r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_MINIMAL, &m); + r = manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m); if (manager_errno_skip_test(r)) { log_notice_errno(r, "Skipping test: manager_new: %m"); return; @@ -48,15 +52,12 @@ TEST(deserialize_exec_command) { test_deserialize_exec_command_one(m, "control-command", "ExecWhat 11 /a/b c d e", -EINVAL); } -DEFINE_CUSTOM_TEST_MAIN( - LOG_DEBUG, +static int intro(void) { + if (enter_cgroup_subroot(NULL) == -ENOMEDIUM) + return log_tests_skipped("cgroupfs not available"); - _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; - ({ - if (enter_cgroup_subroot(NULL) == -ENOMEDIUM) - return log_tests_skipped("cgroupfs not available"); + assert_se(runtime_dir = setup_fake_runtime_dir()); + return EXIT_SUCCESS; +} - assert_se(runtime_dir = setup_fake_runtime_dir()); - }), - - /* no outro */); +DEFINE_TEST_MAIN_WITH_INTRO(LOG_DEBUG, intro); diff --git a/src/test/test-utf8.c b/src/test/test-utf8.c index a21fcd6fd..f070c171f 100644 --- a/src/test/test-utf8.c +++ b/src/test/test-utf8.c @@ -144,7 +144,6 @@ TEST(utf8_escape_non_printable) { } TEST(utf8_escape_non_printable_full) { - const char *s; FOREACH_STRING(s, "goo goo goo", /* ASCII */ "\001 \019\20\a", /* control characters */ @@ -210,8 +209,6 @@ TEST(utf8_console_width) { } TEST(utf8_to_utf16) { - const char *p; - FOREACH_STRING(p, "abc", "zażółcić gęślą jaźń", @@ -231,4 +228,9 @@ TEST(utf8_to_utf16) { } } -DEFINE_CUSTOM_TEST_MAIN(LOG_INFO, log_show_color(true), /* no outro */); +static int intro(void) { + log_show_color(true); + return EXIT_SUCCESS; +} + +DEFINE_TEST_MAIN_WITH_INTRO(LOG_INFO, intro); diff --git a/src/test/test-watch-pid.c b/src/test/test-watch-pid.c index 885ed802d..8c355c1d5 100644 --- a/src/test/test-watch-pid.c +++ b/src/test/test-watch-pid.c @@ -26,7 +26,7 @@ int main(int argc, char *argv[]) { assert_se(runtime_dir = setup_fake_runtime_dir()); - assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); + assert_se(manager_new(LOOKUP_SCOPE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); assert_se(manager_startup(m, NULL, NULL, NULL) >= 0); assert_se(a = unit_new(m, sizeof(Service))); diff --git a/src/test/test-watchdog.c b/src/test/test-watchdog.c index 074ed1e45..2b6d5b5b6 100644 --- a/src/test/test-watchdog.c +++ b/src/test/test-watchdog.c @@ -17,7 +17,7 @@ int main(int argc, char *argv[]) { slow = slow_tests_enabled(); - t = slow ? 10 * USEC_PER_SEC : 1 * USEC_PER_SEC; + t = slow ? 10 * USEC_PER_SEC : 2 * USEC_PER_SEC; count = slow ? 5 : 3; r = watchdog_setup(t); @@ -27,12 +27,13 @@ int main(int argc, char *argv[]) { t = 0; for (i = 0; i < count; i++) { + t = watchdog_runtime_wait(); + log_info("Sleeping " USEC_FMT " microseconds...", t); + usleep(t); log_info("Pinging..."); r = watchdog_ping(); if (r < 0) log_warning_errno(r, "Failed to ping watchdog: %m"); - - usleep(t/2); } watchdog_close(true); diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index 66b454269..c28615cf5 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -102,15 +102,18 @@ static void unit_status_info_clear(UnitStatusInfo *p) { p->active_state = mfree(p->active_state); } -static void unit_status_info_free(UnitStatusInfo *p) { - assert(p); +static UnitStatusInfo *unit_status_info_free(UnitStatusInfo *p) { + if (!p) + return NULL; unit_status_info_clear(p); free(p->name); free(p->path); - free(p); + return mfree(p); } +DEFINE_TRIVIAL_CLEANUP_FUNC(UnitStatusInfo*, unit_status_info_free); + static void context_clear(Context *c) { UnitStatusInfo *p; @@ -129,7 +132,11 @@ static void context_clear(Context *c) { } static int context_add_ntp_service(Context *c, const char *s, const char *source) { - UnitStatusInfo *u; + _cleanup_(unit_status_info_freep) UnitStatusInfo *unit = NULL; + + assert(c); + assert(s); + assert(source); if (!unit_name_is_valid(s, UNIT_NAME_PLAIN)) return -EINVAL; @@ -139,18 +146,17 @@ static int context_add_ntp_service(Context *c, const char *s, const char *source if (streq(u->name, s)) return 0; - u = new0(UnitStatusInfo, 1); - if (!u) + unit = new0(UnitStatusInfo, 1); + if (!unit) return -ENOMEM; - u->name = strdup(s); - if (!u->name) { - free(u); + unit->name = strdup(s); + if (!unit->name) return -ENOMEM; - } - LIST_APPEND(units, c->units, u); - log_unit_debug(u, "added from %s.", source); + LIST_APPEND(units, c->units, unit); + log_unit_debug(unit, "added from %s.", source); + TAKE_PTR(unit); return 0; } @@ -190,7 +196,6 @@ static int context_parse_ntp_services_from_environment(Context *c) { static int context_parse_ntp_services_from_disk(Context *c) { _cleanup_strv_free_ char **files = NULL; - char **f; int r; r = conf_files_list_strv(&files, ".list", NULL, CONF_FILES_FILTER_MASKED, UNIT_LIST_DIRS); @@ -244,7 +249,6 @@ static int context_parse_ntp_services(Context *c) { } static int context_ntp_service_is_active(Context *c) { - UnitStatusInfo *info; int count = 0; assert(c); @@ -258,7 +262,6 @@ static int context_ntp_service_is_active(Context *c) { } static int context_ntp_service_exists(Context *c) { - UnitStatusInfo *info; int count = 0; assert(c); @@ -403,7 +406,6 @@ static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m) { "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) }, {} }; - UnitStatusInfo *u; int r; assert(c); @@ -446,7 +448,6 @@ static int context_update_ntp_status(Context *c, sd_bus *bus, sd_bus_message *m) static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { Context *c = userdata; - UnitStatusInfo *u; const char *path; unsigned n = 0; int r; @@ -918,7 +919,6 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error _cleanup_(sd_bus_slot_unrefp) sd_bus_slot *slot = NULL; sd_bus *bus = sd_bus_message_get_bus(m); Context *c = userdata; - UnitStatusInfo *u; const UnitStatusInfo *selected = NULL; int enable, interactive, q, r; diff --git a/src/timesync/meson.build b/src/timesync/meson.build index 83dd7c8f6..8ecfbfab8 100644 --- a/src/timesync/meson.build +++ b/src/timesync/meson.build @@ -55,7 +55,7 @@ endif ############################################################ tests += [ - [['src/timesync/test-timesync.c'], + [files('test-timesync.c'), [libtimesyncd_core, libshared], [libm]], diff --git a/src/timesync/test-timesync.c b/src/timesync/test-timesync.c index 31e91e7b7..7993e4c12 100644 --- a/src/timesync/test-timesync.c +++ b/src/timesync/test-timesync.c @@ -7,7 +7,7 @@ #include "timesyncd-conf.h" #include "tests.h" -static void test_manager_parse_string(void) { +TEST(manager_parse_string) { /* Make sure that NTP_SERVERS is configured to something * that we can actually parse successfully. */ @@ -25,10 +25,4 @@ static void test_manager_parse_string(void) { assert_se(manager_parse_server_string(m, SERVER_LINK, "time1.foobar.com time2.foobar.com axrfav.,avf..ra 12345..123") == 0); } -int main(int argc, char **argv) { - test_setup_logging(LOG_DEBUG); - - test_manager_parse_string(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/timesync/timesyncd-bus.c b/src/timesync/timesyncd-bus.c index 1a14564e0..b738dfd3c 100644 --- a/src/timesync/timesyncd-bus.c +++ b/src/timesync/timesyncd-bus.c @@ -23,7 +23,7 @@ static int property_get_servers( void *userdata, sd_bus_error *error) { - ServerName *p, **s = userdata; + ServerName **s = userdata; int r; assert(s); diff --git a/src/timesync/timesyncd-conf.c b/src/timesync/timesyncd-conf.c index 8f34441e1..21fe7daec 100644 --- a/src/timesync/timesyncd-conf.c +++ b/src/timesync/timesyncd-conf.c @@ -24,7 +24,6 @@ int manager_parse_server_string(Manager *m, ServerType type, const char *string) for (;;) { _cleanup_free_ char *word = NULL; bool found = false; - ServerName *n; r = extract_first_word(&string, &word, NULL, 0); if (r < 0) diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c index 918da195d..ae906a631 100644 --- a/src/timesync/timesyncd-manager.c +++ b/src/timesync/timesyncd-manager.c @@ -11,9 +11,11 @@ #include #include "sd-daemon.h" +#include "sd-messages.h" #include "alloc-util.h" #include "dns-domain.h" +#include "event-util.h" #include "fd-util.h" #include "format-util.h" #include "fs-util.h" @@ -28,6 +30,7 @@ #include "time-util.h" #include "timesyncd-conf.h" #include "timesyncd-manager.h" +#include "user-util.h" #include "util.h" #ifndef ADJ_SETOFFSET @@ -59,7 +62,7 @@ static int manager_arm_timer(Manager *m, usec_t next); static int manager_clock_watch_setup(Manager *m); static int manager_listen_setup(Manager *m); static void manager_listen_stop(Manager *m); -static int manager_save_time_and_rearm(Manager *m); +static int manager_save_time_and_rearm(Manager *m, usec_t t); static double ntp_ts_short_to_d(const struct ntp_ts_short *ts) { return be16toh(ts->sec) + (be16toh(ts->frac) / 65536.0); @@ -127,7 +130,7 @@ static int manager_send_request(Manager *m) { * The actual value does not matter, We do not care about the correct * NTP UINT_MAX fraction; we just pass the plain nanosecond value. */ - assert_se(clock_gettime(clock_boottime_or_monotonic(), &m->trans_time_mon) >= 0); + assert_se(clock_gettime(CLOCK_BOOTTIME, &m->trans_time_mon) >= 0); assert_se(clock_gettime(CLOCK_REALTIME, &m->trans_time) >= 0); ntpmsg.trans_time.sec = htobe32(graceful_add_offset_1900_1970(m->trans_time.tv_sec)); ntpmsg.trans_time.frac = htobe32(m->trans_time.tv_nsec); @@ -158,8 +161,8 @@ static int manager_send_request(Manager *m) { r = sd_event_add_time( m->event, &m->event_timeout, - clock_boottime_or_monotonic(), - now(clock_boottime_or_monotonic()) + TIMEOUT_USEC, 0, + CLOCK_BOOTTIME, + now(CLOCK_BOOTTIME) + TIMEOUT_USEC, 0, manager_timeout, m); if (r < 0) return log_error_errno(r, "Failed to arm timeout timer: %m"); @@ -197,7 +200,7 @@ static int manager_arm_timer(Manager *m, usec_t next) { return sd_event_add_time_relative( m->event, &m->event_timer, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, next, 0, manager_timer, m); } @@ -229,14 +232,9 @@ static int manager_clock_watch_setup(Manager *m) { assert(m); - m->event_clock_watch = sd_event_source_unref(m->event_clock_watch); - safe_close(m->clock_watch_fd); + m->event_clock_watch = sd_event_source_disable_unref(m->event_clock_watch); - m->clock_watch_fd = time_change_fd(); - if (m->clock_watch_fd < 0) - return log_error_errno(m->clock_watch_fd, "Failed to create timerfd: %m"); - - r = sd_event_add_io(m->event, &m->event_clock_watch, m->clock_watch_fd, EPOLLIN, manager_clock_watch, m); + r = event_add_time_change(m->event, &m->event_clock_watch, manager_clock_watch, m); if (r < 0) return log_error_errno(r, "Failed to create clock watch event source: %m"); @@ -244,31 +242,29 @@ static int manager_clock_watch_setup(Manager *m) { } static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { - struct timex tmx = {}; - int r; + struct timex tmx; assert(m); - /* - * For small deltas, tell the kernel to gradually adjust the system - * clock to the NTP time, larger deltas are just directly set. - */ + /* For small deltas, tell the kernel to gradually adjust the system clock to the NTP time, larger + * deltas are just directly set. */ if (fabs(offset) < NTP_MAX_ADJUST) { - tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR; - tmx.status = STA_PLL; - tmx.offset = offset * NSEC_PER_SEC; - tmx.constant = log2i(m->poll_interval_usec / USEC_PER_SEC) - 4; - tmx.maxerror = 0; - tmx.esterror = 0; + tmx = (struct timex) { + .modes = ADJ_STATUS | ADJ_NANO | ADJ_OFFSET | ADJ_TIMECONST | ADJ_MAXERROR | ADJ_ESTERROR, + .status = STA_PLL, + .offset = offset * NSEC_PER_SEC, + .constant = log2i(m->poll_interval_usec / USEC_PER_SEC) - 4, + }; + log_debug(" adjust (slew): %+.3f sec", offset); } else { - tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET | ADJ_MAXERROR | ADJ_ESTERROR; + tmx = (struct timex) { + .modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET | ADJ_MAXERROR | ADJ_ESTERROR, - /* ADJ_NANO uses nanoseconds in the microseconds field */ - tmx.time.tv_sec = (long)offset; - tmx.time.tv_usec = (offset - tmx.time.tv_sec) * NSEC_PER_SEC; - tmx.maxerror = 0; - tmx.esterror = 0; + /* ADJ_NANO uses nanoseconds in the microseconds field */ + .time.tv_sec = (long)offset, + .time.tv_usec = (offset - (double) (long) offset) * NSEC_PER_SEC, + }; /* the kernel expects -0.3s as {-1, 7000.000.000} */ if (tmx.time.tv_usec < 0) { @@ -280,14 +276,11 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { log_debug(" adjust (jump): %+.3f sec", offset); } - /* - * An unset STA_UNSYNC will enable the kernel's 11-minute mode, - * which syncs the system time periodically to the RTC. + /* An unset STA_UNSYNC will enable the kernel's 11-minute mode, which syncs the system time + * periodically to the RTC. * - * In case the RTC runs in local time, never touch the RTC, - * we have no way to properly handle daylight saving changes and - * mobile devices moving between time zones. - */ + * In case the RTC runs in local time, never touch the RTC, we have no way to properly handle + * daylight saving changes and mobile devices moving between time zones. */ if (m->rtc_local_time) tmx.status |= STA_UNSYNC; @@ -300,17 +293,9 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { break; } - r = clock_adjtime(CLOCK_REALTIME, &tmx); - if (r < 0) + if (clock_adjtime(CLOCK_REALTIME, &tmx) < 0) return -errno; - r = manager_save_time_and_rearm(m); - if (r < 0) - return r; - - /* If touch fails, there isn't much we can do. Maybe it'll work next time. */ - (void) touch("/run/systemd/timesync/synchronized"); - m->drift_freq = tmx.freq; log_debug(" status : %04i %s\n" @@ -427,15 +412,12 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re .msg_name = &server_addr, .msg_namelen = sizeof(server_addr), }; - struct cmsghdr *cmsg; struct timespec *recv_time = NULL; + triple_timestamp dts; ssize_t len; - double origin, receive, trans, dest; - double delay, offset; - double root_distance; + double origin, receive, trans, dest, delay, offset, root_distance; bool spike; - int leap_sec; - int r; + int leap_sec, r; assert(source); assert(m); @@ -466,21 +448,9 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re return 0; } - CMSG_FOREACH(cmsg, &msghdr) { - if (cmsg->cmsg_level != SOL_SOCKET) - continue; - - switch (cmsg->cmsg_type) { - case SCM_TIMESTAMPNS: - assert(cmsg->cmsg_len == CMSG_LEN(sizeof(struct timespec))); - - recv_time = (struct timespec *) CMSG_DATA(cmsg); - break; - } - } + recv_time = CMSG_FIND_DATA(&msghdr, SOL_SOCKET, SCM_TIMESTAMPNS, struct timespec); if (!recv_time) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Invalid packet timestamp."); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Packet timestamp missing."); if (!m->pending) { log_debug("Unexpected reply. Ignoring."); @@ -597,10 +567,23 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re m->samples_jitter, spike ? " spike" : "", m->poll_interval_usec / USEC_PER_SEC); + /* Get current monotonic/realtime clocks immediately before adjusting the latter */ + triple_timestamp_get(&dts); + if (!spike) { + /* Fix up our idea of the time. */ + dts.realtime = (usec_t) (dts.realtime + offset * USEC_PER_SEC); + r = manager_adjust_clock(m, offset, leap_sec); if (r < 0) log_error_errno(r, "Failed to call clock_adjtime(): %m"); + + (void) manager_save_time_and_rearm(m, dts.realtime); + + /* If touch fails, there isn't much we can do. Maybe it'll work next time. */ + r = touch("/run/systemd/timesync/synchronized"); + if (r < 0) + log_debug_errno(r, "Failed to touch /run/systemd/timesync/synchronized, ignoring: %m"); } /* Save NTP response */ @@ -621,15 +604,26 @@ static int manager_receive_response(sd_event_source *source, int fd, uint32_t re "NTPMessage", NULL); - if (!m->good) { + if (!m->talking) { _cleanup_free_ char *pretty = NULL; - m->good = true; + m->talking = true; - server_address_pretty(m->current_server_address, &pretty); - /* "Initial", as further successful syncs will not be logged. */ - log_info("Initial synchronization to time server %s (%s).", strna(pretty), m->current_server_name->string); - sd_notifyf(false, "STATUS=Initial synchronization to time server %s (%s).", strna(pretty), m->current_server_name->string); + (void) server_address_pretty(m->current_server_address, &pretty); + + log_info("Contacted time server %s (%s).", strna(pretty), m->current_server_name->string); + (void) sd_notifyf(false, "STATUS=Contacted time server %s (%s).", strna(pretty), m->current_server_name->string); + } + + if (!spike && !m->synchronized) { + m->synchronized = true; + + log_struct(LOG_INFO, + LOG_MESSAGE("Initial clock synchronization to %s.", FORMAT_TIMESTAMP_STYLE(dts.realtime, TIMESTAMP_US)), + "MESSAGE_ID=" SD_MESSAGE_TIME_SYNC_STR, + "MONOTONIC_USEC=" USEC_FMT, dts.monotonic, + "REALTIME_USEC=" USEC_FMT, dts.realtime, + "BOOTIME_USEC=" USEC_FMT, dts.boottime); } r = manager_arm_timer(m, m->poll_interval_usec); @@ -686,14 +680,14 @@ static int manager_begin(Manager *m) { assert_return(m->current_server_name, -EHOSTUNREACH); assert_return(m->current_server_address, -EHOSTUNREACH); - m->good = false; + m->talking = false; m->missed_replies = NTP_MAX_MISSED_REPLIES; if (m->poll_interval_usec == 0) m->poll_interval_usec = m->poll_interval_min_usec; server_address_pretty(m->current_server_address, &pretty); log_debug("Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string); - sd_notifyf(false, "STATUS=Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string); + (void) sd_notifyf(false, "STATUS=Connecting to time server %s (%s).", strna(pretty), m->current_server_name->string); r = manager_clock_watch_setup(m); if (r < 0) @@ -805,7 +799,7 @@ int manager_connect(Manager *m) { if (!ratelimit_below(&m->ratelimit)) { log_debug("Delaying attempts to contact servers."); - r = sd_event_add_time_relative(m->event, &m->event_retry, clock_boottime_or_monotonic(), m->connection_retry_usec, + r = sd_event_add_time_relative(m->event, &m->event_retry, CLOCK_BOOTTIME, m->connection_retry_usec, 0, manager_retry_connect, m); if (r < 0) return log_error_errno(r, "Failed to create retry timer: %m"); @@ -818,7 +812,7 @@ int manager_connect(Manager *m) { if (m->current_server_address && m->current_server_address->addresses_next) manager_set_server_address(m, m->current_server_address->addresses_next); else { - struct addrinfo hints = { + static const struct addrinfo hints = { .ai_flags = AI_NUMERICSERV|AI_ADDRCONFIG, .ai_socktype = SOCK_DGRAM, }; @@ -860,7 +854,7 @@ int manager_connect(Manager *m) { if (restart && !m->exhausted_servers && m->poll_interval_usec) { log_debug("Waiting after exhausting servers."); - r = sd_event_add_time_relative(m->event, &m->event_retry, clock_boottime_or_monotonic(), m->poll_interval_usec, 0, manager_retry_connect, m); + r = sd_event_add_time_relative(m->event, &m->event_retry, CLOCK_BOOTTIME, m->poll_interval_usec, 0, manager_retry_connect, m); if (r < 0) return log_error_errno(r, "Failed to create retry timer: %m"); @@ -910,12 +904,11 @@ void manager_disconnect(Manager *m) { manager_listen_stop(m); - m->event_clock_watch = sd_event_source_unref(m->event_clock_watch); - m->clock_watch_fd = safe_close(m->clock_watch_fd); + m->event_clock_watch = sd_event_source_disable_unref(m->event_clock_watch); m->event_timeout = sd_event_source_unref(m->event_timeout); - sd_notify(false, "STATUS=Idle."); + (void) sd_notify(false, "STATUS=Idle."); } void manager_flush_server_names(Manager *m, ServerType t) { @@ -960,8 +953,6 @@ Manager* manager_free(Manager *m) { static int manager_network_read_link_servers(Manager *m) { _cleanup_strv_free_ char **ntp = NULL; - ServerName *n, *nx; - char **i; bool changed = false; int r; @@ -1009,7 +1000,7 @@ static int manager_network_read_link_servers(Manager *m) { } } - LIST_FOREACH_SAFE(names, n, nx, m->link_servers) + LIST_FOREACH(names, n, m->link_servers) if (n->marked) { server_name_free(n); changed = true; @@ -1098,21 +1089,26 @@ int manager_new(Manager **ret) { assert(ret); - m = new0(Manager, 1); + m = new(Manager, 1); if (!m) return -ENOMEM; - m->root_distance_max_usec = NTP_ROOT_DISTANCE_MAX_USEC; - m->poll_interval_min_usec = NTP_POLL_INTERVAL_MIN_USEC; - m->poll_interval_max_usec = NTP_POLL_INTERVAL_MAX_USEC; + *m = (Manager) { + .root_distance_max_usec = NTP_ROOT_DISTANCE_MAX_USEC, + .poll_interval_min_usec = NTP_POLL_INTERVAL_MIN_USEC, + .poll_interval_max_usec = NTP_POLL_INTERVAL_MAX_USEC, - m->connection_retry_usec = DEFAULT_CONNECTION_RETRY_USEC; + .connection_retry_usec = DEFAULT_CONNECTION_RETRY_USEC, - m->server_socket = m->clock_watch_fd = -1; + .server_socket = -1, - m->ratelimit = (RateLimit) { RATELIMIT_INTERVAL_USEC, RATELIMIT_BURST }; + .ratelimit = (RateLimit) { + RATELIMIT_INTERVAL_USEC, + RATELIMIT_BURST + }, - m->save_time_interval_usec = DEFAULT_SAVE_TIME_INTERVAL_USEC; + .save_time_interval_usec = DEFAULT_SAVE_TIME_INTERVAL_USEC, + }; r = sd_event_default(&m->event); if (r < 0) @@ -1123,6 +1119,12 @@ int manager_new(Manager **ret) { (void) sd_event_set_watchdog(m->event, true); + /* Load previous synchronization state */ + r = access("/run/systemd/timesync/synchronized", F_OK); + if (r < 0 && errno != ENOENT) + log_debug_errno(errno, "Failed to determine whether /run/systemd/timesync/synchronized exists, ignoring: %m"); + m->synchronized = r >= 0; + r = sd_resolve_default(&m->resolve); if (r < 0) return r; @@ -1147,7 +1149,7 @@ static int manager_save_time_handler(sd_event_source *s, uint64_t usec, void *us assert(m); - (void) manager_save_time_and_rearm(m); + (void) manager_save_time_and_rearm(m, USEC_INFINITY); return 0; } @@ -1163,7 +1165,7 @@ int manager_setup_save_time_event(Manager *m) { /* NB: we'll accumulate scheduling latencies here, but this doesn't matter */ r = sd_event_add_time_relative( m->event, &m->event_save_time, - clock_boottime_or_monotonic(), + CLOCK_BOOTTIME, m->save_time_interval_usec, 10 * USEC_PER_SEC, manager_save_time_handler, m); @@ -1175,12 +1177,16 @@ int manager_setup_save_time_event(Manager *m) { return 0; } -static int manager_save_time_and_rearm(Manager *m) { +static int manager_save_time_and_rearm(Manager *m, usec_t t) { int r; assert(m); - r = touch(CLOCK_FILE); + /* Updates the timestamp file to the specified time. If 't' is USEC_INFINITY uses the current system + * clock, but otherwise uses the specified timestamp. Note that whenever we acquire an NTP sync the + * specified timestamp value might be more accurate than the system clock, since the latter is + * subject to slow adjustments. */ + r = touch_file(CLOCK_FILE, false, t, UID_INVALID, GID_INVALID, MODE_INVALID); if (r < 0) log_debug_errno(r, "Failed to update " CLOCK_FILE ", ignoring: %m"); diff --git a/src/timesync/timesyncd-manager.h b/src/timesync/timesyncd-manager.h index aceb0098c..74917aa0e 100644 --- a/src/timesync/timesyncd-manager.h +++ b/src/timesync/timesyncd-manager.h @@ -61,7 +61,7 @@ struct Manager { int missed_replies; uint64_t packet_count; sd_event_source *event_timeout; - bool good; + bool talking; /* last sent packet */ struct timespec trans_time_mon; @@ -92,7 +92,6 @@ struct Manager { /* watch for time changes */ sd_event_source *event_clock_watch; - int clock_watch_fd; /* Retry connections */ sd_event_source *event_retry; @@ -105,6 +104,9 @@ struct Manager { struct timespec origin_time, dest_time; bool spike; + /* Indicates whether we ever managed to set the local clock from NTP */ + bool synchronized; + /* save time event */ sd_event_source *event_save_time; usec_t save_time_interval_usec; diff --git a/src/timesync/timesyncd-server.c b/src/timesync/timesyncd-server.c index 79dfd4726..dd1689173 100644 --- a/src/timesync/timesyncd-server.c +++ b/src/timesync/timesyncd-server.c @@ -16,16 +16,19 @@ int server_address_new( assert(socklen >= offsetof(struct sockaddr, sa_data)); assert(socklen <= sizeof(union sockaddr_union)); - a = new0(ServerAddress, 1); + a = new(ServerAddress, 1); if (!a) return -ENOMEM; + *a = (ServerAddress) { + .name = n, + .socklen = socklen, + }; + memcpy(&a->sockaddr, sockaddr, socklen); - a->socklen = socklen; LIST_FIND_TAIL(addresses, n->addresses, tail); LIST_INSERT_AFTER(addresses, n->addresses, tail, a); - a->name = n; if (ret) *ret = a; @@ -58,12 +61,16 @@ int server_name_new( assert(m); assert(string); - n = new0(ServerName, 1); + n = new(ServerName, 1); if (!n) return -ENOMEM; - n->type = type; - n->string = strdup(string); + *n = (ServerName) { + .manager = m, + .type = type, + .string = strdup(string), + }; + if (!n->string) { free(n); return -ENOMEM; @@ -81,8 +88,6 @@ int server_name_new( } else assert_not_reached(); - n->manager = m; - if (type != SERVER_FALLBACK && m->current_server_name && m->current_server_name->type == SERVER_FALLBACK) diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index 6f316746f..999d1d385 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -22,9 +22,8 @@ #include "user-util.h" static int load_clock_timestamp(uid_t uid, gid_t gid) { + usec_t min = TIME_EPOCH * USEC_PER_SEC, ct; _cleanup_close_ int fd = -1; - usec_t min = TIME_EPOCH * USEC_PER_SEC; - usec_t ct; int r; /* Let's try to make sure that the clock is always @@ -40,8 +39,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) { usec_t stamp; /* check if the recorded time is later than the compiled-in one */ - r = fstat(fd, &st); - if (r >= 0) { + if (fstat(fd, &st) >= 0) { stamp = timespec_load(&st.st_mtim); if (stamp > min) min = stamp; @@ -64,7 +62,7 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) { } /* create stamp file with the compiled-in date */ - r = touch_file(CLOCK_FILE, false, min, uid, gid, 0644); + r = touch_file(CLOCK_FILE, /* parents= */ false, min, uid, gid, 0644); if (r < 0) log_debug_errno(r, "Failed to create %s, ignoring: %m", CLOCK_FILE); } @@ -72,13 +70,12 @@ static int load_clock_timestamp(uid_t uid, gid_t gid) { settime: ct = now(CLOCK_REALTIME); if (ct < min) { - struct timespec ts; char date[FORMAT_TIMESTAMP_MAX]; log_info("System clock time unset or jumped backwards, restoring from recorded timestamp: %s", format_timestamp(date, sizeof(date), min)); - if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, min)) < 0) + if (clock_settime(CLOCK_REALTIME, TIMESPEC_STORE(min)) < 0) log_error_errno(errno, "Failed to restore system clock, ignoring: %m"); } diff --git a/src/timesync/timesyncd.conf.in b/src/timesync/timesyncd.conf.in index df02cb5bd..f34922927 100644 --- a/src/timesync/timesyncd.conf.in +++ b/src/timesync/timesyncd.conf.in @@ -18,4 +18,5 @@ #RootDistanceMaxSec=5 #PollIntervalMinSec=32 #PollIntervalMaxSec=2048 +#ConnectionRetrySec=30 #SaveIntervalSec=60 diff --git a/src/timesync/wait-sync.c b/src/timesync/wait-sync.c index f42e6496b..57e6cd340 100644 --- a/src/timesync/wait-sync.c +++ b/src/timesync/wait-sync.c @@ -80,7 +80,6 @@ static int inotify_handler(sd_event_source *s, sd_event *event = sd_event_source_get_event(s); ClockState *sp = userdata; union inotify_event_buffer buffer; - struct inotify_event *e; ssize_t l; l = read(fd, &buffer, sizeof(buffer)); @@ -90,7 +89,7 @@ static int inotify_handler(sd_event_source *s, return log_warning_errno(errno, "Lost access to inotify: %m"); } - FOREACH_INOTIFY_EVENT(e, buffer, l) + FOREACH_INOTIFY_EVENT_WARN(e, buffer, l) process_inotify_event(event, sp, e); return 0; diff --git a/src/tmpfiles/meson.build b/src/tmpfiles/meson.build index c72b386cd..cfa3d370a 100644 --- a/src/tmpfiles/meson.build +++ b/src/tmpfiles/meson.build @@ -6,7 +6,7 @@ systemd_tmpfiles_sources = files( 'offline-passwd.h') tests += [ - [['src/tmpfiles/test-offline-passwd.c', - 'src/tmpfiles/offline-passwd.c', - 'src/tmpfiles/offline-passwd.h']], + [files('test-offline-passwd.c', + 'offline-passwd.c', + 'offline-passwd.h')], ] diff --git a/src/tmpfiles/offline-passwd.c b/src/tmpfiles/offline-passwd.c index 8ba3fea98..c847266ed 100644 --- a/src/tmpfiles/offline-passwd.c +++ b/src/tmpfiles/offline-passwd.c @@ -39,7 +39,6 @@ static int populate_uid_cache(const char *root, Hashmap **ret) { /* The directory list is hardcoded here: /etc is the standard, and rpm-ostree uses /usr/lib. This * could be made configurable, but I don't see the point right now. */ - const char *fname; FOREACH_STRING(fname, "/etc/passwd", "/usr/lib/passwd") { _cleanup_fclose_ FILE *f = NULL; @@ -78,7 +77,6 @@ static int populate_gid_cache(const char *root, Hashmap **ret) { if (!cache) return -ENOMEM; - const char *fname; FOREACH_STRING(fname, "/etc/group", "/usr/lib/group") { _cleanup_fclose_ FILE *f = NULL; diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index fcab51c20..023207bc6 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -204,31 +204,6 @@ STATIC_DESTRUCTOR_REGISTER(arg_image, freep); static int specifier_machine_id_safe(char specifier, const void *data, const char *root, const void *userdata, char **ret); static int specifier_directory(char specifier, const void *data, const char *root, const void *userdata, char **ret); -static const Specifier specifier_table[] = { - { 'a', specifier_architecture, NULL }, - { 'b', specifier_boot_id, NULL }, - { 'B', specifier_os_build_id, NULL }, - { 'H', specifier_host_name, NULL }, - { 'l', specifier_short_host_name, NULL }, - { 'm', specifier_machine_id_safe, NULL }, - { 'o', specifier_os_id, NULL }, - { 'v', specifier_kernel_release, NULL }, - { 'w', specifier_os_version_id, NULL }, - { 'W', specifier_os_variant_id, NULL }, - - { 'h', specifier_user_home, NULL }, - - { 'C', specifier_directory, UINT_TO_PTR(DIRECTORY_CACHE) }, - { 'L', specifier_directory, UINT_TO_PTR(DIRECTORY_LOGS) }, - { 'S', specifier_directory, UINT_TO_PTR(DIRECTORY_STATE) }, - { 't', specifier_directory, UINT_TO_PTR(DIRECTORY_RUNTIME) }, - - COMMON_CREDS_SPECIFIERS, - - COMMON_TMP_SPECIFIERS, - {} -}; - static int specifier_machine_id_safe(char specifier, const void *data, const char *root, const void *userdata, char **ret) { int r; @@ -1044,8 +1019,6 @@ static int parse_xattrs_from_arg(Item *i) { } static int fd_set_xattrs(Item *i, int fd, const char *path, const struct stat *st) { - char **name, **value; - assert(i); assert(fd >= 0); assert(path); @@ -1964,7 +1937,6 @@ static int glob_item(Item *i, action_t action) { .gl_opendir = (void *(*)(const char *)) opendir_nomod, }; int r = 0, k; - char **fn; k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g); if (k < 0 && k != -ENOENT) @@ -1984,7 +1956,6 @@ static int glob_item_recursively(Item *i, fdaction_t action) { .gl_opendir = (void *(*)(const char *)) opendir_nomod, }; int r = 0, k; - char **fn; k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g); if (k < 0 && k != -ENOENT) @@ -2720,8 +2691,6 @@ static bool item_compatible(Item *a, Item *b) { } static bool should_include_path(const char *path) { - char **prefix; - STRV_FOREACH(prefix, arg_exclude_prefixes) if (path_startswith(path, *prefix)) { log_debug("Entry \"%s\" matches exclude prefix \"%s\", skipping.", @@ -2743,7 +2712,7 @@ static bool should_include_path(const char *path) { return false; } -static int specifier_expansion_from_arg(Item *i) { +static int specifier_expansion_from_arg(const Specifier *specifier_table, Item *i) { int r; assert(i); @@ -2771,8 +2740,7 @@ static int specifier_expansion_from_arg(Item *i) { return free_and_replace(i->argument, resolved); } case SET_XATTR: - case RECURSIVE_SET_XATTR: { - char **xattr; + case RECURSIVE_SET_XATTR: STRV_FOREACH(xattr, i->xattrs) { _cleanup_free_ char *resolved = NULL; @@ -2783,7 +2751,7 @@ static int specifier_expansion_from_arg(Item *i) { free_and_replace(*xattr, resolved); } return 0; - } + default: return 0; } @@ -2951,6 +2919,30 @@ static int parse_line( assert(line >= 1); assert(buffer); + const Specifier specifier_table[] = { + { 'a', specifier_architecture, NULL }, + { 'b', specifier_boot_id, NULL }, + { 'B', specifier_os_build_id, NULL }, + { 'H', specifier_host_name, NULL }, + { 'l', specifier_short_host_name, NULL }, + { 'm', specifier_machine_id_safe, NULL }, + { 'o', specifier_os_id, NULL }, + { 'v', specifier_kernel_release, NULL }, + { 'w', specifier_os_version_id, NULL }, + { 'W', specifier_os_variant_id, NULL }, + + { 'h', specifier_user_home, NULL }, + + { 'C', specifier_directory, UINT_TO_PTR(DIRECTORY_CACHE) }, + { 'L', specifier_directory, UINT_TO_PTR(DIRECTORY_LOGS) }, + { 'S', specifier_directory, UINT_TO_PTR(DIRECTORY_STATE) }, + { 't', specifier_directory, UINT_TO_PTR(DIRECTORY_RUNTIME) }, + + COMMON_CREDS_SPECIFIERS(arg_user ? LOOKUP_SCOPE_USER : LOOKUP_SCOPE_SYSTEM), + COMMON_TMP_SPECIFIERS, + {} + }; + r = extract_many_words( &buffer, NULL, @@ -3155,7 +3147,7 @@ static int parse_line( if (!should_include_path(i.path)) return 0; - r = specifier_expansion_from_arg(&i); + r = specifier_expansion_from_arg(specifier_table, &i); if (r == -ENXIO) return log_unresolvable_specifier(fname, line); if (r < 0) { @@ -3612,7 +3604,6 @@ static int read_config_file(char **config_dirs, const char *fn, bool ignore_enoe } static int parse_arguments(char **config_dirs, char **args, bool *invalid_config) { - char **arg; int r; STRV_FOREACH(arg, args) { @@ -3627,7 +3618,6 @@ static int parse_arguments(char **config_dirs, char **args, bool *invalid_config static int read_config_files(char **config_dirs, char **args, bool *invalid_config) { _cleanup_strv_free_ char **files = NULL; _cleanup_free_ char *p = NULL; - char **f; int r; r = conf_files_list_with_replacement(arg_root, config_dirs, arg_replace, &files, &p); @@ -3734,7 +3724,6 @@ static int run(int argc, char *argv[]) { if (DEBUG_LOGGING) { _cleanup_free_ char *t = NULL; - char **i; STRV_FOREACH(i, config_dirs) { _cleanup_free_ char *j = NULL; diff --git a/src/tty-ask-password-agent/tty-ask-password-agent.c b/src/tty-ask-password-agent/tty-ask-password-agent.c index 54a03af08..7fc0ed7c8 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -59,7 +59,7 @@ static int send_passwords(const char *socket_name, char **passwords) { union sockaddr_union sa; socklen_t sa_len; size_t packet_length = 1; - char **p, *d; + char *d; ssize_t n; int r; @@ -174,16 +174,16 @@ static int process_one_password_file(const char *filename) { _cleanup_free_ char *socket_name = NULL, *message = NULL; bool accept_cached = false, echo = false, silent = false; uint64_t not_after = 0; - unsigned pid = 0; + pid_t pid = 0; const ConfigTableItem items[] = { - { "Ask", "Socket", config_parse_string, 0, &socket_name }, - { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after }, - { "Ask", "Message", config_parse_string, 0, &message }, - { "Ask", "PID", config_parse_unsigned, 0, &pid }, - { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached }, - { "Ask", "Echo", config_parse_bool, 0, &echo }, - { "Ask", "Silent", config_parse_bool, 0, &silent }, + { "Ask", "Socket", config_parse_string, CONFIG_PARSE_STRING_SAFE, &socket_name }, + { "Ask", "NotAfter", config_parse_uint64, 0, ¬_after }, + { "Ask", "Message", config_parse_string, 0, &message }, + { "Ask", "PID", config_parse_pid, 0, &pid }, + { "Ask", "AcceptCached", config_parse_bool, 0, &accept_cached }, + { "Ask", "Echo", config_parse_bool, 0, &echo }, + { "Ask", "Silent", config_parse_bool, 0, &silent }, {} }; @@ -212,14 +212,14 @@ static int process_one_password_file(const char *filename) { switch (arg_action) { case ACTION_LIST: - printf("'%s' (PID %u)\n", strna(message), pid); + printf("'%s' (PID " PID_FMT ")\n", strna(message), pid); return 0; case ACTION_WALL: { _cleanup_free_ char *wall = NULL; if (asprintf(&wall, - "Password entry required for \'%s\' (PID %u).\r\n" + "Password entry required for \'%s\' (PID " PID_FMT ").\r\n" "Please enter password with the systemd-tty-ask-password-agent tool.", strna(message), pid) < 0) @@ -235,7 +235,7 @@ static int process_one_password_file(const char *filename) { if (access(socket_name, W_OK) < 0) { if (arg_action == ACTION_QUERY) - log_info("Not querying '%s' (PID %u), lacking privileges.", strna(message), pid); + log_info("Not querying '%s' (PID " PID_FMT "), lacking privileges.", strna(message), pid); return 0; } @@ -554,8 +554,6 @@ static int ask_on_this_console(const char *tty, pid_t *ret_pid, char **arguments if (r < 0) return r; if (r == 0) { - char **i; - assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0); STRV_FOREACH(i, arguments) { @@ -581,8 +579,6 @@ static int ask_on_this_console(const char *tty, pid_t *ret_pid, char **arguments } static void terminate_agents(Set *pids) { - struct timespec ts; - siginfo_t status = {}; sigset_t set; void *p; int r, signum; @@ -599,11 +595,10 @@ static void terminate_agents(Set *pids) { */ assert_se(sigemptyset(&set) >= 0); assert_se(sigaddset(&set, SIGCHLD) >= 0); - timespec_store(&ts, 50 * USEC_PER_MSEC); while (!set_isempty(pids)) { + siginfo_t status = {}; - zero(status); r = waitid(P_ALL, 0, &status, WEXITED|WNOHANG); if (r < 0 && errno == EINTR) continue; @@ -613,7 +608,7 @@ static void terminate_agents(Set *pids) { continue; } - signum = sigtimedwait(&set, NULL, &ts); + signum = sigtimedwait(&set, NULL, TIMESPEC_STORE(50 * USEC_PER_MSEC)); if (signum < 0) { if (errno != EAGAIN) log_error_errno(errno, "sigtimedwait() failed: %m"); @@ -635,7 +630,6 @@ static int ask_on_consoles(char *argv[]) { _cleanup_set_free_ Set *pids = NULL; _cleanup_strv_free_ char **consoles = NULL, **arguments = NULL; siginfo_t status = {}; - char **tty; pid_t pid; int r; diff --git a/src/udev/fido_id/test-fido-id-desc.c b/src/udev/fido_id/test-fido-id-desc.c index 6836bca28..36c777a09 100644 --- a/src/udev/fido_id/test-fido-id-desc.c +++ b/src/udev/fido_id/test-fido-id-desc.c @@ -5,8 +5,9 @@ #include "fido_id_desc.h" #include "macro.h" +#include "tests.h" -static void test_is_fido_security_token_desc__fido(void) { +TEST(is_fido_security_token_desc__fido) { static const uint8_t FIDO_HID_DESC_1[] = { 0x06, 0xd0, 0xf1, 0x09, 0x01, 0xa1, 0x01, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x40, 0x81, 0x02, 0x09, 0x21, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, @@ -27,7 +28,7 @@ static void test_is_fido_security_token_desc__fido(void) { assert_se(is_fido_security_token_desc(FIDO_HID_DESC_2, sizeof(FIDO_HID_DESC_2)) > 0); } -static void test_is_fido_security_token_desc__non_fido(void) { +TEST(is_fido_security_token_desc__non_fido) { /* Wrong usage page */ static const uint8_t NON_FIDO_HID_DESC_1[] = { 0x06, 0xd0, 0xf0, 0x09, 0x01, 0xa1, 0x01, 0x09, 0x20, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, @@ -54,7 +55,7 @@ static void test_is_fido_security_token_desc__non_fido(void) { assert_se(is_fido_security_token_desc(NON_FIDO_HID_DESC_3, sizeof(NON_FIDO_HID_DESC_3)) == 0); } -static void test_is_fido_security_token_desc__invalid(void) { +TEST(is_fido_security_token_desc__invalid) { /* Size coded on 1 byte, but no byte given */ static const uint8_t INVALID_HID_DESC_1[] = { 0x01 }; assert_se(is_fido_security_token_desc(INVALID_HID_DESC_1, sizeof(INVALID_HID_DESC_1)) < 0); @@ -76,10 +77,4 @@ static void test_is_fido_security_token_desc__invalid(void) { assert_se(is_fido_security_token_desc(INVALID_HID_DESC_5, sizeof(INVALID_HID_DESC_5)) < 0); } -int main(int argc, char *argv[]) { - test_is_fido_security_token_desc__fido(); - test_is_fido_security_token_desc__non_fido(); - test_is_fido_security_token_desc__invalid(); - - return EXIT_SUCCESS; -} +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/udev/meson.build b/src/udev/meson.build index d55e9073a..a4648be3b 100644 --- a/src/udev/meson.build +++ b/src/udev/meson.build @@ -152,7 +152,7 @@ foreach prog : udev_progs install : true, install_dir : udevlibexecdir) - udev_prog_paths += {name : exe.full_path()} + udev_prog_paths += {name : exe} endforeach if install_sysconfdir_samples @@ -174,49 +174,48 @@ if install_sysconfdir endif fuzzers += [ - [['src/udev/net/fuzz-link-parser.c', - 'src/fuzz/fuzz.h'], + [files('net/fuzz-link-parser.c'), [libudevd_core, libshared], [threads, libacl], udev_includes], - [['src/udev/fuzz-udev-rules.c'], + [files('fuzz-udev-rules.c'), [libudevd_core, libshared], [threads, libacl]], - [['src/udev/fuzz-udev-rule-parse-value.c']], + [files('fuzz-udev-rule-parse-value.c')], - [['src/udev/fido_id/fuzz-fido-id-desc.c', - 'src/udev/fido_id/fido_id_desc.c']], + [files('fido_id/fuzz-fido-id-desc.c', + 'fido_id/fido_id_desc.c')], ] tests += [ - [['src/udev/test-udev-event.c'], + [files('test-udev-event.c'), [libudevd_core, libshared], [threads, libacl]], - [['src/udev/test-udev-node.c'], + [files('test-udev-node.c'), [libudevd_core, libshared], [threads, libacl]], - [['src/udev/test-udev-builtin.c'], + [files('test-udev-builtin.c'), [libudevd_core, libshared], [threads, libacl]], - [['src/udev/test-udev-netlink.c', - 'src/udev/udev-netlink.c', - 'src/udev/udev-netlink.h']], + [files('test-udev-netlink.c', + 'udev-netlink.c', + 'udev-netlink.h')], - [['src/udev/fido_id/test-fido-id-desc.c', - 'src/udev/fido_id/fido_id_desc.c']], + [files('fido_id/test-fido-id-desc.c', + 'fido_id/fido_id_desc.c')], ] diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index ac7825b00..96280148c 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -8,6 +8,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "ethtool-util.h" #include "link-config.h" #include "net-condition.h" +#include "netif-sriov.h" #include "socket-util.h" %} struct ConfigPerfItem; @@ -27,12 +28,14 @@ Match.OriginalName, config_parse_match_ifnames, Match.Path, config_parse_match_strv, 0, offsetof(LinkConfig, match.path) Match.Driver, config_parse_match_strv, 0, offsetof(LinkConfig, match.driver) Match.Type, config_parse_match_strv, 0, offsetof(LinkConfig, match.iftype) +Match.Kind, config_parse_match_strv, 0, offsetof(LinkConfig, match.kind) Match.Property, config_parse_match_property, 0, offsetof(LinkConfig, match.property) Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(LinkConfig, conditions) Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(LinkConfig, conditions) Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(LinkConfig, conditions) Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(LinkConfig, conditions) Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(LinkConfig, conditions) +Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(LinkConfig, conditions) Link.Description, config_parse_string, 0, offsetof(LinkConfig, description) Link.MACAddressPolicy, config_parse_mac_address_policy, 0, offsetof(LinkConfig, mac_address_policy) Link.MACAddress, config_parse_hw_addr, 0, offsetof(LinkConfig, hw_addr) @@ -101,3 +104,14 @@ Link.RxMaxCoalescedHighFrames, config_parse_coalesce_u32, Link.TxCoalesceHighSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.tx_coalesce_usecs_high) Link.TxMaxCoalescedHighFrames, config_parse_coalesce_u32, 0, offsetof(LinkConfig, coalesce.tx_max_coalesced_frames_high) Link.CoalescePacketRateSampleIntervalSec, config_parse_coalesce_sec, 0, offsetof(LinkConfig, coalesce.rate_sample_interval) +Link.MDI, config_parse_mdi, 0, offsetof(LinkConfig, mdi) +Link.SR-IOVVirtualFunctions, config_parse_sr_iov_num_vfs, 0, offsetof(LinkConfig, sr_iov_num_vfs) +SR-IOV.VirtualFunction, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section) +SR-IOV.VLANId, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section) +SR-IOV.QualityOfService, config_parse_sr_iov_uint32, 0, offsetof(LinkConfig, sr_iov_by_section) +SR-IOV.VLANProtocol, config_parse_sr_iov_vlan_proto, 0, offsetof(LinkConfig, sr_iov_by_section) +SR-IOV.MACSpoofCheck, config_parse_sr_iov_boolean, 0, offsetof(LinkConfig, sr_iov_by_section) +SR-IOV.QueryReceiveSideScaling, config_parse_sr_iov_boolean, 0, offsetof(LinkConfig, sr_iov_by_section) +SR-IOV.Trust, config_parse_sr_iov_boolean, 0, offsetof(LinkConfig, sr_iov_by_section) +SR-IOV.LinkState, config_parse_sr_iov_link_state, 0, offsetof(LinkConfig, sr_iov_by_section) +SR-IOV.MACAddress, config_parse_sr_iov_mac, 0, offsetof(LinkConfig, sr_iov_by_section) diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 05f0f2e0a..3c0e887ad 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -22,6 +22,7 @@ #include "log-link.h" #include "memory-util.h" #include "net-condition.h" +#include "netif-sriov.h" #include "netif-util.h" #include "netlink-util.h" #include "parse-util.h" @@ -60,18 +61,18 @@ static LinkConfig* link_config_free(LinkConfig *config) { free(config->wol_password_file); erase_and_free(config->wol_password); + ordered_hashmap_free_with_destructor(config->sr_iov_by_section, sr_iov_free); + return mfree(config); } DEFINE_TRIVIAL_CLEANUP_FUNC(LinkConfig*, link_config_free); static void link_configs_free(LinkConfigContext *ctx) { - LinkConfig *config, *config_next; - if (!ctx) return; - LIST_FOREACH_SAFE(configs, config, config_next, ctx->configs) + LIST_FOREACH(configs, config, ctx->configs) link_config_free(config); } @@ -247,6 +248,8 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) { .txqueuelen = UINT32_MAX, .coalesce.use_adaptive_rx_coalesce = -1, .coalesce.use_adaptive_tx_coalesce = -1, + .mdi = ETH_TP_MDI_INVALID, + .sr_iov_num_vfs = UINT32_MAX, }; for (i = 0; i < ELEMENTSOF(config->features); i++) @@ -257,7 +260,9 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) { STRV_MAKE_CONST(filename), (const char* const*) CONF_PATHS_STRV("systemd/network"), dropin_dirname, - "Match\0Link\0", + "Match\0" + "Link\0" + "SR-IOV\0", config_item_perf_lookup, link_config_gperf_lookup, CONFIG_PARSE_WARN, config, NULL); if (r < 0) @@ -285,6 +290,10 @@ int link_load_one(LinkConfigContext *ctx, const char *filename) { if (r < 0) return r; + r = sr_iov_drop_invalid_sections(config->sr_iov_num_vfs, config->sr_iov_by_section); + if (r < 0) + return r; + log_debug("Parsed configuration file %s", filename); LIST_PREPEND(configs, ctx->configs, TAKE_PTR(config)); @@ -315,7 +324,6 @@ static int device_unsigned_attribute(sd_device *device, const char *attr, unsign int link_config_load(LinkConfigContext *ctx) { _cleanup_strv_free_ char **files = NULL; - char **f; int r; link_configs_free(ctx); @@ -350,6 +358,7 @@ Link *link_free(Link *link) { return NULL; sd_device_unref(link->device); + free(link->kind); free(link->driver); return mfree(link); } @@ -391,7 +400,8 @@ int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, Link if (r < 0) log_link_debug_errno(link, r, "Failed to get \"addr_assign_type\" attribute, ignoring: %m"); - r = rtnl_get_link_info(rtnl, link->ifindex, &link->iftype, &link->flags, &link->hw_addr, &link->permanent_hw_addr); + r = rtnl_get_link_info(rtnl, link->ifindex, &link->iftype, &link->flags, + &link->kind, &link->hw_addr, &link->permanent_hw_addr); if (r < 0) return r; @@ -410,7 +420,6 @@ int link_new(LinkConfigContext *ctx, sd_netlink **rtnl, sd_device *device, Link } int link_get_config(LinkConfigContext *ctx, Link *link) { - LinkConfig *config; int r; assert(ctx); @@ -428,6 +437,7 @@ int link_get_config(LinkConfigContext *ctx, Link *link) { &link->permanent_hw_addr, link->driver, link->iftype, + link->kind, link->ifname, /* alternative_names = */ NULL, /* wlan_iftype = */ 0, @@ -465,7 +475,7 @@ static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) { r = ethtool_set_glinksettings(ethtool_fd, name, config->autonegotiation, config->advertise, - config->speed, config->duplex, config->port); + config->speed, config->duplex, config->port, config->mdi); if (r < 0) { if (config->autonegotiation >= 0) log_link_warning_errno(link, r, "Could not %s auto negotiation, ignoring: %m", @@ -485,6 +495,10 @@ static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) { if (config->port >= 0) log_link_warning_errno(link, r, "Could not set port to '%s', ignoring: %m", port_to_string(config->port)); + + if (config->mdi != ETH_TP_MDI_INVALID) + log_link_warning_errno(link, r, "Could not set MDI-X to '%s', ignoring: %m", + mdi_to_string(config->mdi)); } r = ethtool_set_wol(ethtool_fd, name, config->wol, config->wol_password); @@ -604,10 +618,9 @@ static int link_generate_new_hw_addr(Link *link, struct hw_addr_data *ret) { if (link->config->mac_address_policy == MAC_ADDRESS_POLICY_RANDOM) /* We require genuine randomness here, since we want to make sure we won't collide with other - * systems booting up at the very same time. We do allow RDRAND however, since this is not - * cryptographic key material. */ + * systems booting up at the very same time. */ for (;;) { - r = genuine_random_bytes(p, len, RANDOM_ALLOW_RDRAND); + r = genuine_random_bytes(p, len, 0); if (r < 0) return log_link_warning_errno(link, r, "Failed to acquire random data to generate MAC address: %m"); @@ -816,7 +829,6 @@ static int link_apply_alternative_names(Link *link, sd_netlink **rtnl) { if (r < 0) log_link_debug_errno(link, r, "Failed to get alternative names, ignoring: %m"); - char **p; STRV_FOREACH(p, current_altnames) strv_remove(altnames, *p); @@ -830,6 +842,77 @@ static int link_apply_alternative_names(Link *link, sd_netlink **rtnl) { return 0; } +static int sr_iov_configure(Link *link, sd_netlink **rtnl, SRIOV *sr_iov) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; + int r; + + assert(link); + assert(rtnl); + assert(link->ifindex > 0); + + if (!*rtnl) { + r = sd_netlink_open(rtnl); + if (r < 0) + return r; + } + + r = sd_rtnl_message_new_link(*rtnl, &req, RTM_SETLINK, link->ifindex); + if (r < 0) + return r; + + r = sr_iov_set_netlink_message(sr_iov, req); + if (r < 0) + return r; + + r = sd_netlink_call(*rtnl, req, 0, NULL); + if (r < 0) + return r; + + return 0; +} + +static int link_apply_sr_iov_config(Link *link, sd_netlink **rtnl) { + SRIOV *sr_iov; + uint32_t n; + int r; + + assert(link); + assert(link->config); + assert(link->device); + + r = sr_iov_set_num_vfs(link->device, link->config->sr_iov_num_vfs, link->config->sr_iov_by_section); + if (r < 0) + log_link_warning_errno(link, r, "Failed to set the number of SR-IOV virtual functions, ignoring: %m"); + + if (ordered_hashmap_isempty(link->config->sr_iov_by_section)) + return 0; + + r = sr_iov_get_num_vfs(link->device, &n); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to get the number of SR-IOV virtual functions, ignoring [SR-IOV] sections: %m"); + return 0; + } + if (n == 0) { + log_link_warning(link, "No SR-IOV virtual function exists, ignoring [SR-IOV] sections: %m"); + return 0; + } + + ORDERED_HASHMAP_FOREACH(sr_iov, link->config->sr_iov_by_section) { + if (sr_iov->vf >= n) { + log_link_warning(link, "SR-IOV virtual function %"PRIu32" does not exist, ignoring.", sr_iov->vf); + continue; + } + + r = sr_iov_configure(link, rtnl, sr_iov); + if (r < 0) + log_link_warning_errno(link, r, + "Failed to configure SR-IOV virtual function %"PRIu32", ignoring: %m", + sr_iov->vf); + } + + return 0; +} + int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link) { int r; @@ -861,6 +944,10 @@ int link_apply_config(LinkConfigContext *ctx, sd_netlink **rtnl, Link *link) { if (r < 0) return r; + r = link_apply_sr_iov_config(link, rtnl); + if (r < 0) + return r; + return 0; } diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 3b2cd6962..ea9f560f4 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -7,6 +7,7 @@ #include "condition.h" #include "conf-parser.h" #include "ethtool-util.h" +#include "hashmap.h" #include "list.h" #include "net-condition.h" #include "netif-naming-scheme.h" @@ -31,6 +32,7 @@ typedef struct Link { sd_device *device; sd_device_action_t action; + char *kind; char *driver; uint16_t iftype; uint32_t flags; @@ -75,6 +77,10 @@ struct LinkConfig { int tx_flow_control; int autoneg_flow_control; netdev_coalesce_param coalesce; + uint8_t mdi; + + uint32_t sr_iov_num_vfs; + OrderedHashmap *sr_iov_by_section; LIST_FIELDS(LinkConfig, configs); }; diff --git a/src/udev/test-udev-builtin.c b/src/udev/test-udev-builtin.c index 21a8ea3fa..2ce7f196f 100644 --- a/src/udev/test-udev-builtin.c +++ b/src/udev/test-udev-builtin.c @@ -3,9 +3,7 @@ #include "tests.h" #include "udev-builtin.h" -static void test_udev_builtin_cmd_to_ptr(void) { - log_info("/* %s */", __func__); - +TEST(udev_builtin_cmd_to_ptr) { /* Those could have been static asserts, but ({}) is not allowed there. */ #if HAVE_BLKID assert_se(UDEV_BUILTIN_CMD_TO_PTR(UDEV_BUILTIN_BLKID)); @@ -19,8 +17,4 @@ static void test_udev_builtin_cmd_to_ptr(void) { assert_se(PTR_TO_UDEV_BUILTIN_CMD((void*) 10000) == _UDEV_BUILTIN_INVALID); } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_DEBUG); - - test_udev_builtin_cmd_to_ptr(); -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/udev/test-udev-event.c b/src/udev/test-udev-event.c index b1a631dea..b6b2c91b2 100644 --- a/src/udev/test-udev-event.c +++ b/src/udev/test-udev-event.c @@ -9,7 +9,7 @@ #define BUF_SIZE 1024 -static void test_event_spawn_core(bool with_pidfd, const char *cmd, char result_buf[BUF_SIZE]) { +static void test_event_spawn_core(bool with_pidfd, const char *cmd, char *result_buf, size_t buf_size) { _cleanup_(sd_device_unrefp) sd_device *dev = NULL; _cleanup_(udev_event_freep) UdevEvent *event = NULL; @@ -17,12 +17,12 @@ static void test_event_spawn_core(bool with_pidfd, const char *cmd, char result_ assert_se(sd_device_new_from_syspath(&dev, "/sys/class/net/lo") >= 0); assert_se(event = udev_event_new(dev, 0, NULL, LOG_DEBUG)); - assert_se(udev_event_spawn(event, 5 * USEC_PER_SEC, SIGKILL, false, cmd, result_buf, BUF_SIZE) == 0); + assert_se(udev_event_spawn(event, 5 * USEC_PER_SEC, SIGKILL, false, cmd, result_buf, buf_size, NULL) == 0); assert_se(unsetenv("SYSTEMD_PIDFD") >= 0); } -static void test_event_spawn_cat(bool with_pidfd) { +static void test_event_spawn_cat(bool with_pidfd, size_t buf_size) { _cleanup_strv_free_ char **lines = NULL; _cleanup_free_ char *cmd = NULL; char result_buf[BUF_SIZE]; @@ -32,13 +32,16 @@ static void test_event_spawn_cat(bool with_pidfd) { assert_se(find_executable("cat", &cmd) >= 0); assert_se(strextend_with_separator(&cmd, " ", "/sys/class/net/lo/uevent")); - test_event_spawn_core(with_pidfd, cmd, result_buf); + test_event_spawn_core(with_pidfd, cmd, result_buf, + buf_size >= BUF_SIZE ? BUF_SIZE : buf_size); assert_se(lines = strv_split_newlines(result_buf)); strv_print(lines); - assert_se(strv_contains(lines, "INTERFACE=lo")); - assert_se(strv_contains(lines, "IFINDEX=1")); + if (buf_size >= BUF_SIZE) { + assert_se(strv_contains(lines, "INTERFACE=lo")); + assert_se(strv_contains(lines, "IFINDEX=1")); + } } static void test_event_spawn_self(const char *self, const char *arg, bool with_pidfd) { @@ -50,7 +53,7 @@ static void test_event_spawn_self(const char *self, const char *arg, bool with_p assert_se(cmd = strjoin(self, " ", arg)); - test_event_spawn_core(with_pidfd, cmd, result_buf); + test_event_spawn_core(with_pidfd, cmd, result_buf, BUF_SIZE); assert_se(lines = strv_split_newlines(result_buf)); strv_print(lines); @@ -92,8 +95,10 @@ int main(int argc, char *argv[]) { assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0); - test_event_spawn_cat(true); - test_event_spawn_cat(false); + test_event_spawn_cat(true, SIZE_MAX); + test_event_spawn_cat(false, SIZE_MAX); + test_event_spawn_cat(true, 5); + test_event_spawn_cat(false, 5); assert_se(path_make_absolute_cwd(argv[0], &self) >= 0); path_simplify(self); diff --git a/src/udev/test-udev-netlink.c b/src/udev/test-udev-netlink.c index c1213b7ca..1fddb5efb 100644 --- a/src/udev/test-udev-netlink.c +++ b/src/udev/test-udev-netlink.c @@ -132,12 +132,10 @@ static void test_link_info_one(sd_netlink *rtnl, int ifindex) { } } -static void test_link_info_get(void) { +TEST(link_info_get) { _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - log_debug("/* %s */", __func__); - assert_se(sd_netlink_open(&rtnl) >= 0); assert_se(sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0) >= 0); @@ -156,10 +154,4 @@ static void test_link_info_get(void) { } } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_DEBUG); - - test_link_info_get(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/udev/test-udev-node.c b/src/udev/test-udev-node.c index 010c19acf..b5eaa0de5 100644 --- a/src/udev/test-udev-node.c +++ b/src/udev/test-udev-node.c @@ -13,7 +13,7 @@ static void test_udev_node_escape_path_one(const char *path, const char *expecte assert_se(streq(buf, expected)); } -static void test_udev_node_escape_path(void) { +TEST(udev_node_escape_path) { char a[NAME_MAX+1], b[NAME_MAX+1]; test_udev_node_escape_path_one("/disk/by-id/nvme-eui.1922908022470001001b448b44ccb9d6", "\\x2fdisk\\x2fby-id\\x2fnvme-eui.1922908022470001001b448b44ccb9d6"); @@ -47,10 +47,4 @@ static void test_udev_node_escape_path(void) { test_udev_node_escape_path_one(a, b); } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_INFO); - - test_udev_node_escape_path(); - - return 0; -} +DEFINE_TEST_MAIN(LOG_INFO); diff --git a/src/udev/udev-builtin-btrfs.c b/src/udev/udev-builtin-btrfs.c index a0093cb42..f9d4f1dd4 100644 --- a/src/udev/udev-builtin-btrfs.c +++ b/src/udev/udev-builtin-btrfs.c @@ -6,6 +6,7 @@ #include #include "device-util.h" +#include "errno-util.h" #include "fd-util.h" #include "string-util.h" #include "strxcpyx.h" @@ -22,7 +23,7 @@ static int builtin_btrfs(sd_device *dev, sd_netlink **rtnl, int argc, char *argv fd = open("/dev/btrfs-control", O_RDWR|O_CLOEXEC); if (fd < 0) { - if (IN_SET(errno, ENOENT, ENXIO, ENODEV)) { + if (ERRNO_IS_DEVICE_ABSENT(errno)) { /* Driver not installed? Then we aren't ready. This is useful in initrds that lack * btrfs.ko. After the host transition (where btrfs.ko will hopefully become * available) the device can be retriggered and will then be considered ready. */ diff --git a/src/udev/udev-builtin-input_id.c b/src/udev/udev-builtin-input_id.c index 92c0b7699..a34f6de61 100644 --- a/src/udev/udev-builtin-input_id.c +++ b/src/udev/udev-builtin-input_id.c @@ -111,20 +111,20 @@ static void get_cap_mask(sd_device *pdev, const char* attr, else log_device_debug(pdev, "Ignoring %s block %lX which is larger than maximum size", attr, val); - if (test) { - /* printf pattern with the right unsigned long number of hex chars */ - xsprintf(text, " bit %%4u: %%0%zulX\n", - 2 * sizeof(unsigned long)); + if (test && DEBUG_LOGGING) { log_device_debug(pdev, "%s decoded bit map:", attr); + val = bitmask_size / sizeof (unsigned long); - /* skip over leading zeros */ + /* skip trailing zeros */ while (bitmask[val-1] == 0 && val > 0) --val; - for (unsigned long j = 0; j < val; j++) { - DISABLE_WARNING_FORMAT_NONLITERAL; - log_device_debug(pdev, text, j * BITS_PER_LONG, bitmask[j]); - REENABLE_WARNING; - } + + /* IN_SET() cannot be used in assert_cc(). */ + assert_cc(sizeof(unsigned long) == 4 || sizeof(unsigned long) == 8); + for (unsigned long j = 0; j < val; j++) + log_device_debug(pdev, + sizeof(unsigned long) == 4 ? " bit %4lu: %08lX\n" : " bit %4lu: %016lX\n", + j * BITS_PER_LONG, bitmask[j]); } } diff --git a/src/udev/udev-builtin-net_id.c b/src/udev/udev-builtin-net_id.c index 65e003eb1..9e4297ada 100644 --- a/src/udev/udev-builtin-net_id.c +++ b/src/udev/udev-builtin-net_id.c @@ -217,7 +217,7 @@ static int dev_pci_onboard(sd_device *dev, const LinkInfo *info, NetNames *names names->pci_onboard[0] = '\0'; log_device_debug(dev, "Onboard index identifier: index=%lu phys_port=%s dev_port=%lu → %s", idx, strempty(info->phys_port_name), dev_port, - empty_to_na(names->pci_slot)); + empty_to_na(names->pci_onboard)); if (sd_device_get_sysattr_value(names->pcidev, "label", &names->pci_onboard_label) >= 0) log_device_debug(dev, "Onboard label from PCI device: %s", names->pci_onboard_label); diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index 179a1fdec..4913d0262 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -101,7 +101,7 @@ static void udev_ctrl_disconnect(UdevCtrl *uctrl) { if (!uctrl) return; - uctrl->event_source_connect = sd_event_source_unref(uctrl->event_source_connect); + uctrl->event_source_connect = sd_event_source_disable_unref(uctrl->event_source_connect); uctrl->sock_connect = safe_close(uctrl->sock_connect); } @@ -110,7 +110,7 @@ static UdevCtrl *udev_ctrl_free(UdevCtrl *uctrl) { udev_ctrl_disconnect(uctrl); - sd_event_source_unref(uctrl->event_source); + sd_event_source_disable_unref(uctrl->event_source); safe_close(uctrl->sock); sd_event_unref(uctrl->event); @@ -291,7 +291,7 @@ int udev_ctrl_start(UdevCtrl *uctrl, udev_ctrl_handler_t callback, void *userdat return 0; } -int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const char *buf) { +int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, const void *data) { UdevCtrlMessageWire ctrl_msg_wire = { .version = "udev-" STRINGIFY(PROJECT_VERSION), .magic = UDEV_CTRL_MAGIC, @@ -301,10 +301,11 @@ int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const if (uctrl->maybe_disconnected) return -ENOANO; /* to distinguish this from other errors. */ - if (buf) - strscpy(ctrl_msg_wire.value.buf, sizeof(ctrl_msg_wire.value.buf), buf); - else - ctrl_msg_wire.value.intval = intval; + if (type == UDEV_CTRL_SET_ENV) { + assert(data); + strscpy(ctrl_msg_wire.value.buf, sizeof(ctrl_msg_wire.value.buf), data); + } else if (IN_SET(type, UDEV_CTRL_SET_LOG_LEVEL, UDEV_CTRL_SET_CHILDREN_MAX)) + ctrl_msg_wire.value.intval = PTR_TO_INT(data); if (!uctrl->connected) { if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0) @@ -322,7 +323,7 @@ int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const } int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) { - _cleanup_(sd_event_source_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL; + _cleanup_(sd_event_source_disable_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL; int r; assert(uctrl); @@ -333,7 +334,7 @@ int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) { return 0; if (!uctrl->maybe_disconnected) { - r = udev_ctrl_send(uctrl, _UDEV_CTRL_END_MESSAGES, 0, NULL); + r = udev_ctrl_send(uctrl, _UDEV_CTRL_END_MESSAGES, NULL); if (r < 0) return r; } @@ -355,7 +356,7 @@ int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout) { if (timeout != USEC_INFINITY) { r = sd_event_add_time_relative( - uctrl->event, &source_timeout, clock_boottime_or_monotonic(), + uctrl->event, &source_timeout, CLOCK_BOOTTIME, timeout, 0, NULL, INT_TO_PTR(-ETIMEDOUT)); if (r < 0) diff --git a/src/udev/udev-ctrl.h b/src/udev/udev-ctrl.h index d4bc06812..11fc0b62d 100644 --- a/src/udev/udev-ctrl.h +++ b/src/udev/udev-ctrl.h @@ -42,37 +42,37 @@ sd_event_source *udev_ctrl_get_event_source(UdevCtrl *uctrl); int udev_ctrl_wait(UdevCtrl *uctrl, usec_t timeout); -int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, int intval, const char *buf); +int udev_ctrl_send(UdevCtrl *uctrl, UdevCtrlMessageType type, const void *data); static inline int udev_ctrl_send_set_log_level(UdevCtrl *uctrl, int priority) { - return udev_ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL); + return udev_ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, INT_TO_PTR(priority)); } static inline int udev_ctrl_send_stop_exec_queue(UdevCtrl *uctrl) { - return udev_ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL); + return udev_ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, NULL); } static inline int udev_ctrl_send_start_exec_queue(UdevCtrl *uctrl) { - return udev_ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL); + return udev_ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, NULL); } static inline int udev_ctrl_send_reload(UdevCtrl *uctrl) { - return udev_ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL); + return udev_ctrl_send(uctrl, UDEV_CTRL_RELOAD, NULL); } static inline int udev_ctrl_send_set_env(UdevCtrl *uctrl, const char *key) { - return udev_ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key); + return udev_ctrl_send(uctrl, UDEV_CTRL_SET_ENV, key); } static inline int udev_ctrl_send_set_children_max(UdevCtrl *uctrl, int count) { - return udev_ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL); + return udev_ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, INT_TO_PTR(count)); } static inline int udev_ctrl_send_ping(UdevCtrl *uctrl) { - return udev_ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL); + return udev_ctrl_send(uctrl, UDEV_CTRL_PING, NULL); } static inline int udev_ctrl_send_exit(UdevCtrl *uctrl) { - return udev_ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL); + return udev_ctrl_send(uctrl, UDEV_CTRL_EXIT, NULL); } DEFINE_TRIVIAL_CLEANUP_FUNC(UdevCtrl*, udev_ctrl_unref); diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index a60e4f294..d9af8bfd2 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -50,6 +50,7 @@ typedef struct Spawn { char *result; size_t result_size; size_t result_len; + bool truncated; } Spawn; UdevEvent *udev_event_new(sd_device *dev, usec_t exec_delay_usec, sd_netlink *rtnl, int log_level) { @@ -237,9 +238,12 @@ static ssize_t udev_event_subst_format( FormatSubstitutionType type, const char *attr, char *dest, - size_t l) { + size_t l, + bool *ret_truncated) { + sd_device *parent, *dev = event->dev; const char *val = NULL; + bool truncated = false; char *s = dest; int r; @@ -248,13 +252,13 @@ static ssize_t udev_event_subst_format( r = sd_device_get_devpath(dev, &val); if (r < 0) return r; - strpcpy(&s, l, val); + strpcpy_full(&s, l, val, &truncated); break; case FORMAT_SUBST_KERNEL: r = sd_device_get_sysname(dev, &val); if (r < 0) return r; - strpcpy(&s, l, val); + strpcpy_full(&s, l, val, &truncated); break; case FORMAT_SUBST_KERNEL_NUMBER: r = sd_device_get_sysnum(dev, &val); @@ -262,7 +266,7 @@ static ssize_t udev_event_subst_format( goto null_terminate; if (r < 0) return r; - strpcpy(&s, l, val); + strpcpy_full(&s, l, val, &truncated); break; case FORMAT_SUBST_ID: if (!event->dev_parent) @@ -270,7 +274,7 @@ static ssize_t udev_event_subst_format( r = sd_device_get_sysname(event->dev_parent, &val); if (r < 0) return r; - strpcpy(&s, l, val); + strpcpy_full(&s, l, val, &truncated); break; case FORMAT_SUBST_DRIVER: if (!event->dev_parent) @@ -280,7 +284,7 @@ static ssize_t udev_event_subst_format( goto null_terminate; if (r < 0) return r; - strpcpy(&s, l, val); + strpcpy_full(&s, l, val, &truncated); break; case FORMAT_SUBST_MAJOR: case FORMAT_SUBST_MINOR: { @@ -289,7 +293,7 @@ static ssize_t udev_event_subst_format( r = sd_device_get_devnum(dev, &devnum); if (r < 0 && r != -ENOENT) return r; - strpcpyf(&s, l, "%u", r < 0 ? 0 : type == FORMAT_SUBST_MAJOR ? major(devnum) : minor(devnum)); + strpcpyf_full(&s, l, &truncated, "%u", r < 0 ? 0 : type == FORMAT_SUBST_MAJOR ? major(devnum) : minor(devnum)); break; } case FORMAT_SUBST_RESULT: { @@ -308,7 +312,7 @@ static ssize_t udev_event_subst_format( } if (index == 0) - strpcpy(&s, l, event->program_result); + strpcpy_full(&s, l, event->program_result, &truncated); else { const char *start, *p; unsigned i; @@ -330,11 +334,11 @@ static ssize_t udev_event_subst_format( start = p; /* %c{2+} copies the whole string from the second part on */ if (has_plus) - strpcpy(&s, l, start); + strpcpy_full(&s, l, start, &truncated); else { while (*p && !strchr(WHITESPACE, *p)) p++; - strnpcpy(&s, l, start, p - start); + strnpcpy_full(&s, l, start, p - start, &truncated); } } break; @@ -342,6 +346,7 @@ static ssize_t udev_event_subst_format( case FORMAT_SUBST_ATTR: { char vbuf[UDEV_NAME_SIZE]; int count; + bool t; if (isempty(attr)) return -EINVAL; @@ -363,12 +368,13 @@ static ssize_t udev_event_subst_format( /* strip trailing whitespace, and replace unwanted characters */ if (val != vbuf) - strscpy(vbuf, sizeof(vbuf), val); + strscpy_full(vbuf, sizeof(vbuf), val, &truncated); delete_trailing_chars(vbuf, NULL); count = udev_replace_chars(vbuf, UDEV_ALLOWED_CHARS_INPUT); if (count > 0) log_device_debug(dev, "%i character(s) replaced", count); - strpcpy(&s, l, vbuf); + strpcpy_full(&s, l, vbuf, &t); + truncated = truncated || t; break; } case FORMAT_SUBST_PARENT: @@ -382,7 +388,7 @@ static ssize_t udev_event_subst_format( goto null_terminate; if (r < 0) return r; - strpcpy(&s, l, val + STRLEN("/dev/")); + strpcpy_full(&s, l, val + STRLEN("/dev/"), &truncated); break; case FORMAT_SUBST_DEVNODE: r = sd_device_get_devname(dev, &val); @@ -390,34 +396,37 @@ static ssize_t udev_event_subst_format( goto null_terminate; if (r < 0) return r; - strpcpy(&s, l, val); + strpcpy_full(&s, l, val, &truncated); break; case FORMAT_SUBST_NAME: if (event->name) - strpcpy(&s, l, event->name); + strpcpy_full(&s, l, event->name, &truncated); else if (sd_device_get_devname(dev, &val) >= 0) - strpcpy(&s, l, val + STRLEN("/dev/")); + strpcpy_full(&s, l, val + STRLEN("/dev/"), &truncated); else { r = sd_device_get_sysname(dev, &val); if (r < 0) return r; - strpcpy(&s, l, val); + strpcpy_full(&s, l, val, &truncated); } break; case FORMAT_SUBST_LINKS: - FOREACH_DEVICE_DEVLINK(dev, val) + FOREACH_DEVICE_DEVLINK(dev, val) { if (s == dest) - strpcpy(&s, l, val + STRLEN("/dev/")); + strpcpy_full(&s, l, val + STRLEN("/dev/"), &truncated); else - strpcpyl(&s, l, " ", val + STRLEN("/dev/"), NULL); + strpcpyl_full(&s, l, &truncated, " ", val + STRLEN("/dev/"), NULL); + if (truncated) + break; + } if (s == dest) goto null_terminate; break; case FORMAT_SUBST_ROOT: - strpcpy(&s, l, "/dev"); + strpcpy_full(&s, l, "/dev", &truncated); break; case FORMAT_SUBST_SYS: - strpcpy(&s, l, "/sys"); + strpcpy_full(&s, l, "/sys", &truncated); break; case FORMAT_SUBST_ENV: if (isempty(attr)) @@ -427,22 +436,34 @@ static ssize_t udev_event_subst_format( goto null_terminate; if (r < 0) return r; - strpcpy(&s, l, val); + strpcpy_full(&s, l, val, &truncated); break; default: assert_not_reached(); } + if (ret_truncated) + *ret_truncated = truncated; + return s - dest; null_terminate: + if (ret_truncated) + *ret_truncated = truncated; + *s = '\0'; return 0; } -size_t udev_event_apply_format(UdevEvent *event, - const char *src, char *dest, size_t size, - bool replace_whitespace) { +size_t udev_event_apply_format( + UdevEvent *event, + const char *src, + char *dest, + size_t size, + bool replace_whitespace, + bool *ret_truncated) { + + bool truncated = false; const char *s = src; int r; @@ -456,20 +477,24 @@ size_t udev_event_apply_format(UdevEvent *event, FormatSubstitutionType type; char attr[UDEV_PATH_SIZE]; ssize_t subst_len; + bool t; r = get_subst_type(&s, false, &type, attr); if (r < 0) { log_device_warning_errno(event->dev, r, "Invalid format string, ignoring: %s", src); break; } else if (r == 0) { - if (size < 2) /* need space for this char and the terminating NUL */ + if (size < 2) { + /* need space for this char and the terminating NUL */ + truncated = true; break; + } *dest++ = *s++; size--; continue; } - subst_len = udev_event_subst_format(event, type, attr, dest, size); + subst_len = udev_event_subst_format(event, type, attr, dest, size, &t); if (subst_len < 0) { log_device_warning_errno(event->dev, subst_len, "Failed to substitute variable '$%s' or apply format '%%%c', ignoring: %m", @@ -477,6 +502,8 @@ size_t udev_event_apply_format(UdevEvent *event, break; } + truncated = truncated || t; + /* FORMAT_SUBST_RESULT handles spaces itself */ if (replace_whitespace && type != FORMAT_SUBST_RESULT) /* udev_replace_whitespace can replace in-place, @@ -488,6 +515,10 @@ size_t udev_event_apply_format(UdevEvent *event, } assert(size >= 1); + + if (ret_truncated) + *ret_truncated = truncated; + *dest = '\0'; return size; } @@ -555,7 +586,7 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd size = sizeof(buf); } - l = read(fd, p, size - 1); + l = read(fd, p, size - (p == buf)); if (l < 0) { if (errno == EAGAIN) goto reenable; @@ -566,6 +597,13 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd return 0; } + if ((size_t) l == size) { + log_device_warning(spawn->device, "Truncating stdout of '%s' up to %zu byte.", + spawn->cmd, spawn->result_size); + l--; + spawn->truncated = true; + } + p[l] = '\0'; if (fd == spawn->fd_stdout && spawn->result) spawn->result_len += l; @@ -573,7 +611,6 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd /* Log output only if we watch stderr. */ if (l > 0 && spawn->fd_stderr >= 0) { _cleanup_strv_free_ char **v = NULL; - char **q; r = strv_split_newlines_full(&v, p, EXTRACT_RETAIN_ESCAPE); if (r < 0) @@ -586,7 +623,7 @@ static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userd fd == spawn->fd_stdout ? "out" : "err", *q); } - if (l == 0) + if (l == 0 || spawn->truncated) return 0; reenable: @@ -658,9 +695,9 @@ static int on_spawn_sigchld(sd_event_source *s, const siginfo_t *si, void *userd static int spawn_wait(Spawn *spawn) { _cleanup_(sd_event_unrefp) sd_event *e = NULL; - _cleanup_(sd_event_source_unrefp) sd_event_source *sigchld_source = NULL; - _cleanup_(sd_event_source_unrefp) sd_event_source *stdout_source = NULL; - _cleanup_(sd_event_source_unrefp) sd_event_source *stderr_source = NULL; + _cleanup_(sd_event_source_disable_unrefp) sd_event_source *sigchld_source = NULL; + _cleanup_(sd_event_source_disable_unrefp) sd_event_source *stdout_source = NULL; + _cleanup_(sd_event_source_disable_unrefp) sd_event_source *stderr_source = NULL; int r; assert(spawn); @@ -725,12 +762,16 @@ static int spawn_wait(Spawn *spawn) { return sd_event_loop(e); } -int udev_event_spawn(UdevEvent *event, - usec_t timeout_usec, - int timeout_signal, - bool accept_failure, - const char *cmd, - char *result, size_t ressize) { +int udev_event_spawn( + UdevEvent *event, + usec_t timeout_usec, + int timeout_signal, + bool accept_failure, + const char *cmd, + char *result, + size_t ressize, + bool *ret_truncated) { + _cleanup_close_pair_ int outpipe[2] = {-1, -1}, errpipe[2] = {-1, -1}; _cleanup_strv_free_ char **argv = NULL; char **envp = NULL; @@ -821,6 +862,9 @@ int udev_event_spawn(UdevEvent *event, if (result) result[spawn.result_len] = '\0'; + if (ret_truncated) + *ret_truncated = spawn.truncated; + return r; /* 0 for success, and positive if the program failed */ } @@ -855,7 +899,7 @@ static int rename_netif(UdevEvent *event) { return 0; } - r = rtnl_get_link_info(&event->rtnl, ifindex, NULL, &flags, NULL, NULL); + r = rtnl_get_link_info(&event->rtnl, ifindex, NULL, &flags, NULL, NULL, NULL); if (r < 0) return log_device_warning_errno(dev, r, "Failed to get link flags: %m"); @@ -1095,7 +1139,7 @@ void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec, int timeout_s log_device_debug(event->dev, "Running command \"%s\"", command); - r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0); + r = udev_event_spawn(event, timeout_usec, timeout_signal, false, command, NULL, 0, NULL); if (r < 0) log_device_warning_errno(event->dev, r, "Failed to execute '%s', ignoring: %m", command); else if (r > 0) /* returned value is positive when program fails */ diff --git a/src/udev/udev-event.h b/src/udev/udev-event.h index 2067909fd..d201fb580 100644 --- a/src/udev/udev-event.h +++ b/src/udev/udev-event.h @@ -56,7 +56,8 @@ size_t udev_event_apply_format( const char *src, char *dest, size_t size, - bool replace_whitespace); + bool replace_whitespace, + bool *ret_truncated); int udev_check_format(const char *value, size_t *offset, const char **hint); int udev_event_spawn( UdevEvent *event, @@ -65,7 +66,8 @@ int udev_event_spawn( bool accept_failure, const char *cmd, char *result, - size_t ressize); + size_t ressize, + bool *ret_truncated); int udev_event_execute_rules( UdevEvent *event, int inotify_fd, diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index 1a384d6b3..b46cb0f90 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -268,11 +268,9 @@ static void udev_rule_token_free(UdevRuleToken *token) { } static void udev_rule_line_clear_tokens(UdevRuleLine *rule_line) { - UdevRuleToken *i, *next; - assert(rule_line); - LIST_FOREACH_SAFE(tokens, i, next, rule_line->tokens) + LIST_FOREACH(tokens, i, rule_line->tokens) udev_rule_token_free(i); rule_line->tokens = NULL; @@ -298,12 +296,10 @@ static UdevRuleLine* udev_rule_line_free(UdevRuleLine *rule_line) { DEFINE_TRIVIAL_CLEANUP_FUNC(UdevRuleLine*, udev_rule_line_free); static void udev_rule_file_free(UdevRuleFile *rule_file) { - UdevRuleLine *i, *next; - if (!rule_file) return; - LIST_FOREACH_SAFE(rule_lines, i, next, rule_file->rule_lines) + LIST_FOREACH(rule_lines, i, rule_file->rule_lines) udev_rule_line_free(i); free(rule_file->filename); @@ -311,12 +307,10 @@ static void udev_rule_file_free(UdevRuleFile *rule_file) { } UdevRules *udev_rules_free(UdevRules *rules) { - UdevRuleFile *i, *next; - if (!rules) return NULL; - LIST_FOREACH_SAFE(rule_files, i, next, rules->rule_files) + LIST_FOREACH(rule_files, i, rules->rule_files) udev_rule_file_free(i); hashmap_free_free_key(rules->known_users); @@ -1070,7 +1064,7 @@ static void sort_tokens(UdevRuleLine *rule_line) { rule_line->current_token = NULL; while (!LIST_IS_EMPTY(head_old)) { - UdevRuleToken *t, *min_token = NULL; + UdevRuleToken *min_token = NULL; LIST_FOREACH(tokens, t, head_old) if (!min_token || min_token->type > t->type) @@ -1146,12 +1140,10 @@ static int rule_add_line(UdevRules *rules, const char *line_str, unsigned line_n } static void rule_resolve_goto(UdevRuleFile *rule_file) { - UdevRuleLine *line, *line_next, *i; - assert(rule_file); /* link GOTOs to LABEL rules in this file to be able to fast-forward */ - LIST_FOREACH_SAFE(rule_lines, line, line_next, rule_file->rule_lines) { + LIST_FOREACH(rule_lines, line, rule_file->rule_lines) { if (!FLAGS_SET(line->type, LINE_HAS_GOTO)) continue; @@ -1300,7 +1292,6 @@ UdevRules* udev_rules_new(ResolveNameTiming resolve_name_timing) { int udev_rules_load(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) { _cleanup_(udev_rules_freep) UdevRules *rules = NULL; _cleanup_strv_free_ char **files = NULL; - char **f; int r; rules = udev_rules_new(resolve_name_timing); @@ -1381,11 +1372,14 @@ static bool token_match_string(UdevRuleToken *token, const char *str) { return token->op == (match ? OP_MATCH : OP_NOMATCH); } -static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *event) { +static bool token_match_attr(UdevRules *rules, UdevRuleToken *token, sd_device *dev, UdevEvent *event) { char nbuf[UDEV_NAME_SIZE], vbuf[UDEV_NAME_SIZE]; const char *name, *value; + bool truncated; + assert(rules); assert(token); + assert(IN_SET(token->type, TK_M_ATTR, TK_M_PARENTS_ATTR)); assert(dev); assert(event); @@ -1393,7 +1387,15 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev switch (token->attr_subst_type) { case SUBST_TYPE_FORMAT: - (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false); + (void) udev_event_apply_format(event, name, nbuf, sizeof(nbuf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, + "The sysfs attribute name '%s' is truncated while substituting into '%s', " + "assuming the %s key does not match.", nbuf, name, + token->type == TK_M_ATTR ? "ATTR" : "ATTRS"); + return false; + } + name = nbuf; _fallthrough_; case SUBST_TYPE_PLAIN: @@ -1497,19 +1499,22 @@ static int attr_subst_subdir(char attr[static UDEV_PATH_SIZE]) { char buf[UDEV_PATH_SIZE], *p; const char *tail; size_t len, size; + bool truncated; assert(attr); tail = strstr(attr, "/*/"); if (!tail) - return 0; + return 0; len = tail - attr + 1; /* include slash at the end */ tail += 2; /* include slash at the beginning */ p = buf; size = sizeof(buf); - size -= strnpcpy(&p, size, attr, len); + size -= strnpcpy_full(&p, size, attr, len, &truncated); + if (truncated) + return -ENOENT; dir = opendir(buf); if (!dir) @@ -1519,7 +1524,10 @@ static int attr_subst_subdir(char attr[static UDEV_PATH_SIZE]) { if (de->d_name[0] == '.') continue; - strscpyl(p, size, de->d_name, tail, NULL); + strscpyl_full(p, size, &truncated, de->d_name, tail, NULL); + if (truncated) + continue; + if (faccessat(dirfd(dir), p, F_OK, 0) < 0) continue; @@ -1645,12 +1653,19 @@ static int udev_rule_apply_token_to_event( } case TK_M_ATTR: case TK_M_PARENTS_ATTR: - return token_match_attr(token, dev, event); + return token_match_attr(rules, token, dev, event); case TK_M_SYSCTL: { _cleanup_free_ char *value = NULL; char buf[UDEV_PATH_SIZE]; + bool truncated; + + (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The sysctl entry name '%s' is truncated while substituting into '%s', " + "assuming the SYSCTL key does not match.", buf, (const char*) token->data); + return false; + } - (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false); r = sysctl_read(sysctl_normalize(buf), &value); if (r < 0 && r != -ENOENT) return log_rule_error_errno(dev, rules, r, "Failed to read sysctl '%s': %m", buf); @@ -1661,9 +1676,15 @@ static int udev_rule_apply_token_to_event( mode_t mode = PTR_TO_MODE(token->data); char buf[UDEV_PATH_SIZE]; struct stat statbuf; - bool match; + bool match, truncated; + + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The file name '%s' is truncated while substituting into '%s', " + "assuming the TEST key does not match", buf, token->value); + return false; + } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); if (!path_is_absolute(buf) && udev_resolve_subsys_kernel(buf, buf, sizeof(buf), false) < 0) { char tmp[UDEV_PATH_SIZE]; @@ -1673,8 +1694,11 @@ static int udev_rule_apply_token_to_event( if (r < 0) return log_rule_error_errno(dev, rules, r, "Failed to get syspath: %m"); - strscpy(tmp, sizeof(tmp), buf); - strscpyl(buf, sizeof(buf), val, "/", tmp, NULL); + strscpy_full(tmp, sizeof(tmp), buf, &truncated); + assert(!truncated); + strscpyl_full(buf, sizeof(buf), &truncated, val, "/", tmp, NULL); + if (truncated) + return false; } r = attr_subst_subdir(buf); @@ -1694,13 +1718,20 @@ static int udev_rule_apply_token_to_event( } case TK_M_PROGRAM: { char buf[UDEV_PATH_SIZE], result[UDEV_LINE_SIZE]; + bool truncated; size_t count; event->program_result = mfree(event->program_result); - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The command '%s' is truncated while substituting into '%s', " + "assuming the PROGRAM key does not match.", buf, token->value); + return false; + } + log_rule_debug(dev, rules, "Running PROGRAM '%s'", buf); - r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result)); + r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof(result), NULL); if (r != 0) { if (r < 0) log_rule_warning_errno(dev, rules, r, "Failed to execute \"%s\": %m", buf); @@ -1721,8 +1752,15 @@ static int udev_rule_apply_token_to_event( case TK_M_IMPORT_FILE: { _cleanup_fclose_ FILE *f = NULL; char buf[UDEV_PATH_SIZE]; + bool truncated; + + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The file name '%s' to be imported is truncated while substituting into '%s', " + "assuming the IMPORT key does not match.", buf, token->value); + return false; + } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); log_rule_debug(dev, rules, "Importing properties from '%s'", buf); f = fopen(buf, "re"); @@ -1767,12 +1805,19 @@ static int udev_rule_apply_token_to_event( } case TK_M_IMPORT_PROGRAM: { _cleanup_strv_free_ char **lines = NULL; - char buf[UDEV_PATH_SIZE], result[UDEV_LINE_SIZE], **line; + char buf[UDEV_PATH_SIZE], result[UDEV_LINE_SIZE]; + bool truncated; + + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The command '%s' is truncated while substituting into '%s', " + "assuming the IMPORT key does not match.", buf, token->value); + return false; + } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); log_rule_debug(dev, rules, "Importing properties from results of '%s'", buf); - r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result); + r = udev_event_spawn(event, timeout_usec, timeout_signal, true, buf, result, sizeof result, &truncated); if (r != 0) { if (r < 0) log_rule_warning_errno(dev, rules, r, "Failed to execute '%s', ignoring: %m", buf); @@ -1781,10 +1826,26 @@ static int udev_rule_apply_token_to_event( return token->op == OP_NOMATCH; } + if (truncated) { + bool found = false; + + /* Drop the last line. */ + for (char *p = PTR_SUB1(buf + strlen(buf), buf); p; p = PTR_SUB1(p, buf)) + if (strchr(NEWLINE, *p)) { + *p = '\0'; + found = true; + } else if (found) + break; + } + r = strv_split_newlines_full(&lines, result, EXTRACT_RETAIN_ESCAPE); - if (r < 0) + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { log_rule_warning_errno(dev, rules, r, "Failed to extract lines from result of command \"%s\", ignoring: %m", buf); + return false; + } STRV_FOREACH(line, lines) { char *key, *value; @@ -1813,6 +1874,7 @@ static int udev_rule_apply_token_to_event( assert(cmd >= 0 && cmd < _UDEV_BUILTIN_MAX); unsigned mask = 1U << (int) cmd; char buf[UDEV_PATH_SIZE]; + bool truncated; if (udev_builtin_run_once(cmd)) { /* check if we ran already */ @@ -1826,7 +1888,13 @@ static int udev_rule_apply_token_to_event( event->builtin_run |= mask; } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The builtin command '%s' is truncated while substituting into '%s', " + "assuming the IMPORT key does not match", buf, token->value); + return false; + } + log_rule_debug(dev, rules, "Importing properties from results of builtin command '%s'", buf); r = udev_builtin_run(dev, &event->rtnl, cmd, buf, false); @@ -1875,8 +1943,15 @@ static int udev_rule_apply_token_to_event( } case TK_M_IMPORT_PARENT: { char buf[UDEV_PATH_SIZE]; + bool truncated; + + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_debug(dev, rules, "The property name '%s' is truncated while substituting into '%s', " + "assuming the IMPORT key does not match.", buf, token->value); + return false; + } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); r = import_parent_into_properties(dev, buf); if (r < 0) return log_rule_error_errno(dev, rules, r, @@ -1925,13 +2000,20 @@ static int udev_rule_apply_token_to_event( case TK_A_OWNER: { char owner[UDEV_NAME_SIZE]; const char *ow = owner; + bool truncated; if (event->owner_final) break; if (token->op == OP_ASSIGN_FINAL) event->owner_final = true; - (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false); + (void) udev_event_apply_format(event, token->value, owner, sizeof(owner), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The user name '%s' is truncated while substituting into '%s', " + "refusing to apply the OWNER key.", owner, token->value); + break; + } + r = get_user_creds(&ow, &event->uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); if (r < 0) log_unknown_owner(dev, rules, r, "user", owner); @@ -1942,13 +2024,20 @@ static int udev_rule_apply_token_to_event( case TK_A_GROUP: { char group[UDEV_NAME_SIZE]; const char *gr = group; + bool truncated; if (event->group_final) break; if (token->op == OP_ASSIGN_FINAL) event->group_final = true; - (void) udev_event_apply_format(event, token->value, group, sizeof(group), false); + (void) udev_event_apply_format(event, token->value, group, sizeof(group), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The group name '%s' is truncated while substituting into '%s', " + "refusing to apply the GROUP key.", group, token->value); + break; + } + r = get_group_creds(&gr, &event->gid, USER_CREDS_ALLOW_MISSING); if (r < 0) log_unknown_owner(dev, rules, r, "group", group); @@ -1958,13 +2047,20 @@ static int udev_rule_apply_token_to_event( } case TK_A_MODE: { char mode_str[UDEV_NAME_SIZE]; + bool truncated; if (event->mode_final) break; if (token->op == OP_ASSIGN_FINAL) event->mode_final = true; - (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false); + (void) udev_event_apply_format(event, token->value, mode_str, sizeof(mode_str), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The mode '%s' is truncated while substituting into %s, " + "refusing to apply the MODE key.", mode_str, token->value); + break; + } + r = parse_mode(mode_str, &event->mode); if (r < 0) log_rule_error_errno(dev, rules, r, "Failed to parse mode '%s', ignoring: %m", mode_str); @@ -2005,12 +2101,19 @@ static int udev_rule_apply_token_to_event( case TK_A_SECLABEL: { _cleanup_free_ char *name = NULL, *label = NULL; char label_str[UDEV_LINE_SIZE] = {}; + bool truncated; name = strdup(token->data); if (!name) return log_oom(); - (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false); + (void) udev_event_apply_format(event, token->value, label_str, sizeof(label_str), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The security label '%s' is truncated while substituting into '%s', " + "refusing to apply the SECLABEL key.", label_str, token->value); + break; + } + if (!isempty(label_str)) label = strdup(label_str); else @@ -2037,6 +2140,7 @@ static int udev_rule_apply_token_to_event( const char *val, *name = token->data; char value_new[UDEV_NAME_SIZE], *p = value_new; size_t count, l = sizeof(value_new); + bool truncated; if (isempty(token->value)) { if (token->op == OP_ADD) @@ -2048,10 +2152,22 @@ static int udev_rule_apply_token_to_event( } if (token->op == OP_ADD && - sd_device_get_property_value(dev, name, &val) >= 0) - l = strpcpyl(&p, l, val, " ", NULL); + sd_device_get_property_value(dev, name, &val) >= 0) { + l = strpcpyl_full(&p, l, &truncated, val, " ", NULL); + if (truncated) { + log_rule_warning(dev, rules, "The buffer for the property '%s' is full, " + "refusing to append the new value '%s'.", name, token->value); + break; + } + } + + (void) udev_event_apply_format(event, token->value, p, l, false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The property value '%s' is truncated while substituting into '%s', " + "refusing to add property '%s'.", p, token->value, name); + break; + } - (void) udev_event_apply_format(event, token->value, p, l, false); if (event->esc == ESCAPE_REPLACE) { count = udev_replace_chars(p, NULL); if (count > 0) @@ -2066,8 +2182,16 @@ static int udev_rule_apply_token_to_event( } case TK_A_TAG: { char buf[UDEV_PATH_SIZE]; + bool truncated; + + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The tag name '%s' is truncated while substituting into '%s'," + "refusing to %s the tag.", buf, token->value, + token->op == OP_REMOVE ? "remove" : "add"); + break; + } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); if (token->op == OP_ASSIGN) device_cleanup_tags(dev); @@ -2086,6 +2210,7 @@ static int udev_rule_apply_token_to_event( } case TK_A_NAME: { char buf[UDEV_PATH_SIZE]; + bool truncated; size_t count; if (event->name_final) @@ -2100,7 +2225,13 @@ static int udev_rule_apply_token_to_event( break; } - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The network interface name '%s' is truncated while substituting into '%s', " + "refusing to set the name.", buf, token->value); + break; + } + if (IN_SET(event->esc, ESCAPE_UNSET, ESCAPE_REPLACE)) { if (naming_scheme_has(NAMING_REPLACE_STRICTLY)) count = udev_replace_ifname(buf); @@ -2119,6 +2250,7 @@ static int udev_rule_apply_token_to_event( } case TK_A_DEVLINK: { char buf[UDEV_PATH_SIZE], *p; + bool truncated; size_t count; if (event->devlink_final) @@ -2131,7 +2263,13 @@ static int udev_rule_apply_token_to_event( device_cleanup_devlinks(dev); /* allow multiple symlinks separated by spaces */ - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), event->esc != ESCAPE_NONE); + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), event->esc != ESCAPE_NONE, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The symbolic link path '%s' is truncated while substituting into '%s', " + "refusing to add the device symbolic link.", buf, token->value); + break; + } + if (event->esc == ESCAPE_UNSET) count = udev_replace_chars(buf, "/ "); else if (event->esc == ESCAPE_REPLACE) @@ -2152,7 +2290,10 @@ static int udev_rule_apply_token_to_event( next = skip_leading_chars(next, NULL); } - strscpyl(filename, sizeof(filename), "/dev/", p, NULL); + strscpyl_full(filename, sizeof(filename), &truncated, "/dev/", p, NULL); + if (truncated) + continue; + r = device_add_devlink(dev, filename); if (r < 0) return log_rule_error_errno(dev, rules, r, "Failed to add devlink '%s': %m", filename); @@ -2165,17 +2306,30 @@ static int udev_rule_apply_token_to_event( case TK_A_ATTR: { char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE]; const char *val, *key_name = token->data; + bool truncated; if (udev_resolve_subsys_kernel(key_name, buf, sizeof(buf), false) < 0 && - sd_device_get_syspath(dev, &val) >= 0) - strscpyl(buf, sizeof(buf), val, "/", key_name, NULL); + sd_device_get_syspath(dev, &val) >= 0) { + strscpyl_full(buf, sizeof(buf), &truncated, val, "/", key_name, NULL); + if (truncated) { + log_rule_warning(dev, rules, + "The path to the attribute '%s/%s' is too long, refusing to set the attribute.", + val, key_name); + break; + } + } r = attr_subst_subdir(buf); if (r < 0) { log_rule_error_errno(dev, rules, r, "Could not find file matches '%s', ignoring: %m", buf); break; } - (void) udev_event_apply_format(event, token->value, value, sizeof(value), false); + (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The attribute value '%s' is truncated while substituting into '%s', " + "refusing to set the attribute '%s'", value, token->value, buf); + break; + } log_rule_debug(dev, rules, "ATTR '%s' writing '%s'", buf, value); r = write_string_file(buf, value, @@ -2189,9 +2343,22 @@ static int udev_rule_apply_token_to_event( } case TK_A_SYSCTL: { char buf[UDEV_PATH_SIZE], value[UDEV_NAME_SIZE]; + bool truncated; + + (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The sysctl entry name '%s' is truncated while substituting into '%s', " + "refusing to set the sysctl entry.", buf, (const char*) token->data); + break; + } + + (void) udev_event_apply_format(event, token->value, value, sizeof(value), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The sysctl value '%s' is truncated while substituting into '%s', " + "refusing to set the sysctl entry '%s'", value, token->value, buf); + break; + } - (void) udev_event_apply_format(event, token->data, buf, sizeof(buf), false); - (void) udev_event_apply_format(event, token->value, value, sizeof(value), false); sysctl_normalize(buf); log_rule_debug(dev, rules, "SYSCTL '%s' writing '%s'", buf, value); r = sysctl_write(buf, value); @@ -2203,6 +2370,7 @@ static int udev_rule_apply_token_to_event( case TK_A_RUN_PROGRAM: { _cleanup_free_ char *cmd = NULL; char buf[UDEV_PATH_SIZE]; + bool truncated; if (event->run_final) break; @@ -2212,7 +2380,12 @@ static int udev_rule_apply_token_to_event( if (IN_SET(token->op, OP_ASSIGN, OP_ASSIGN_FINAL)) ordered_hashmap_clear_free_key(event->run_list); - (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false, &truncated); + if (truncated) { + log_rule_warning(dev, rules, "The command '%s' is truncated while substituting into '%s', " + "refusing to invoke the command.", buf, token->value); + break; + } cmd = strdup(buf); if (!cmd) @@ -2256,9 +2429,12 @@ static int udev_rule_apply_parent_token_to_event( head = rules->current_file->current_line->current_token; event->dev_parent = event->dev; for (;;) { - LIST_FOREACH(tokens, line->current_token, head) { - if (!token_is_for_parents(line->current_token)) + line->current_token = NULL; + LIST_FOREACH(tokens, token, head) { + if (!token_is_for_parents(token)) return true; /* All parent tokens match. */ + + line->current_token = token; r = udev_rule_apply_token_to_event(rules, event->dev_parent, event, 0, timeout_signal, NULL); if (r < 0) return r; @@ -2286,7 +2462,6 @@ static int udev_rule_apply_line_to_event( UdevRuleLine *line = rules->current_file->current_line; UdevRuleLineType mask = LINE_HAS_GOTO | LINE_UPDATE_SOMETHING; - UdevRuleToken *token, *next_token; bool parents_done = false; sd_device_action_t action; int r; @@ -2310,7 +2485,7 @@ static int udev_rule_apply_line_to_event( DEVICE_TRACE_POINT(rules_apply_line, event->dev, line->rule_file->filename, line->line_number); - LIST_FOREACH_SAFE(tokens, token, next_token, line->tokens) { + LIST_FOREACH(tokens, token, line->tokens) { line->current_token = token; if (token_is_for_parents(token)) { @@ -2343,8 +2518,6 @@ int udev_rules_apply_to_event( int timeout_signal, Hashmap *properties_list) { - UdevRuleFile *file; - UdevRuleLine *next_line; int r; assert(rules); @@ -2352,7 +2525,8 @@ int udev_rules_apply_to_event( LIST_FOREACH(rule_files, file, rules->rule_files) { rules->current_file = file; - LIST_FOREACH_SAFE(rule_lines, file->current_line, next_line, file->rule_lines) { + LIST_FOREACH_WITH_NEXT(rule_lines, line, next_line, file->rule_lines) { + file->current_line = line; r = udev_rule_apply_line_to_event(rules, event, timeout_usec, timeout_signal, properties_list, &next_line); if (r < 0) return r; @@ -2366,7 +2540,6 @@ static int apply_static_dev_perms(const char *devnode, uid_t uid, gid_t gid, mod char device_node[UDEV_PATH_SIZE], tags_dir[UDEV_PATH_SIZE], tag_symlink[UDEV_PATH_SIZE]; _cleanup_free_ char *unescaped_filename = NULL; struct stat stats; - char **t; int r; assert(devnode); @@ -2430,7 +2603,6 @@ static int apply_static_dev_perms(const char *devnode, uid_t uid, gid_t gid, mod } static int udev_rule_line_apply_static_dev_perms(UdevRuleLine *rule_line) { - UdevRuleToken *token; _cleanup_strv_free_ char **tags = NULL; uid_t uid = UID_INVALID; gid_t gid = GID_INVALID; @@ -2463,8 +2635,6 @@ static int udev_rule_line_apply_static_dev_perms(UdevRuleLine *rule_line) { } int udev_rules_apply_static_dev_perms(UdevRules *rules) { - UdevRuleFile *file; - UdevRuleLine *line; int r; assert(rules); diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index 018a52ada..66ddf3a77 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -16,6 +16,7 @@ #include "device-private.h" #include "device-util.h" #include "dirent-util.h" +#include "errno-util.h" #include "fd-util.h" #include "sort-util.h" #include "static-destruct.h" @@ -47,6 +48,8 @@ static const char *arg_export_prefix = NULL; static usec_t arg_wait_for_initialization_timeout = 0; static bool skip_attribute(const char *name) { + assert(name); + /* Those are either displayed separately or should not be shown at all. */ return STR_IN_SET(name, "uevent", @@ -66,6 +69,9 @@ typedef struct SysAttr { STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep); static int sysattr_compare(const SysAttr *a, const SysAttr *b) { + assert(a); + assert(b); + return strcmp(a->name, b->name); } @@ -75,6 +81,8 @@ static int print_all_attributes(sd_device *device, bool is_parent) { size_t n_items = 0; int r; + assert(device); + value = NULL; (void) sd_device_get_devpath(device, &value); printf(" looking at %sdevice '%s':\n", is_parent ? "parent " : "", strempty(value)); @@ -110,8 +118,8 @@ static int print_all_attributes(sd_device *device, bool is_parent) { if (len > 0) continue; - } else if (r == -EPERM) - value = "(write-only)"; + } else if (ERRNO_IS_PRIVILEGE(r)) + value = "(not readable)"; else continue; @@ -139,6 +147,8 @@ static int print_device_chain(sd_device *device) { sd_device *child, *parent; int r; + assert(device); + printf("\n" "Udevadm info starts with the device specified by the devpath and then\n" "walks up the chain of parent devices. It prints for every device\n" @@ -164,6 +174,8 @@ static int print_record(sd_device *device) { const char *str, *val; int i; + assert(device); + (void) sd_device_get_devpath(device, &str); printf("P: %s\n", str); @@ -190,6 +202,8 @@ static int print_record(sd_device *device) { static int stat_device(const char *name, bool export, const char *prefix) { struct stat statbuf; + assert(name); + if (stat(name, &statbuf) != 0) return -errno; @@ -229,11 +243,11 @@ static int export_devices(void) { } static void cleanup_dir(DIR *dir, mode_t mask, int depth) { + assert(dir); + if (depth <= 0) return; - assert(dir); - FOREACH_DIRENT_ALL(dent, dir, break) { struct stat stats; @@ -261,38 +275,27 @@ static void cleanup_dir(DIR *dir, mode_t mask, int depth) { * entries for devices in /run/udev/data (such as "b8:16"), and removes * all files except those that haven't been deleted in /run/udev/data * (i.e. they were skipped during db cleanup because of the db_persist flag). - * Returns true if the directory is empty after cleanup. */ -static bool cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) { - unsigned int kept = 0; - - assert(dir && datadir); +static void cleanup_dir_after_db_cleanup(DIR *dir, DIR *datadir) { + assert(dir); + assert(datadir); FOREACH_DIRENT_ALL(dent, dir, break) { - struct stat data_stats, link_stats; - if (dot_or_dot_dot(dent->d_name)) continue; - if (fstatat(dirfd(dir), dent->d_name, &link_stats, AT_SYMLINK_NOFOLLOW) < 0) { - if (errno != ENOENT) - kept++; + + if (faccessat(dirfd(datadir), dent->d_name, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) + /* The corresponding udev database file still exists. + * Assuming the parsistent flag is set for the database. */ continue; - } - if (fstatat(dirfd(datadir), dent->d_name, &data_stats, 0) < 0) - (void) unlinkat(dirfd(dir), dent->d_name, - S_ISDIR(link_stats.st_mode) ? AT_REMOVEDIR : 0); - else - /* The entry still exists under /run/udev/data */ - kept++; + (void) unlinkat(dirfd(dir), dent->d_name, 0); } - - return kept == 0; } static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) { - - assert(dir && datadir); + assert(dir); + assert(datadir); FOREACH_DIRENT_ALL(dent, dir, break) { struct stat stats; @@ -305,15 +308,17 @@ static void cleanup_dirs_after_db_cleanup(DIR *dir, DIR *datadir) { _cleanup_closedir_ DIR *dir2 = NULL; dir2 = fdopendir(openat(dirfd(dir), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); - if (dir2 && cleanup_dir_after_db_cleanup(dir2, datadir)) - (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR); + if (dir2) + cleanup_dir_after_db_cleanup(dir2, datadir); + + (void) unlinkat(dirfd(dir), dent->d_name, AT_REMOVEDIR); } else (void) unlinkat(dirfd(dir), dent->d_name, 0); } } static void cleanup_db(void) { - _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL, *dir5 = NULL; + _cleanup_closedir_ DIR *dir1 = NULL, *dir2 = NULL, *dir3 = NULL, *dir4 = NULL; dir1 = opendir("/run/udev/data"); if (dir1) @@ -331,9 +336,8 @@ static void cleanup_db(void) { if (dir4) cleanup_dir(dir4, 0, 2); - dir5 = opendir("/run/udev/watch"); - if (dir5) - cleanup_dir_after_db_cleanup(dir5, dir1); + /* Do not remove /run/udev/watch. It will be handled by udevd well on restart. + * And should not be removed by external program when udevd is running. */ } static int query_device(QueryType query, sd_device* device) { @@ -399,10 +403,10 @@ static int query_device(QueryType query, sd_device* device) { case QUERY_ALL: return print_record(device); - } - assert_not_reached(); - return 0; + default: + assert_not_reached(); + } } static int help(void) { @@ -580,7 +584,6 @@ int info_main(int argc, char *argv[], void *userdata) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "-x/--export or -P/--export-prefix cannot be used with --value"); - char **p; STRV_FOREACH(p, devices) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; diff --git a/src/udev/udevadm-test-builtin.c b/src/udev/udevadm-test-builtin.c index c266e1684..81b633611 100644 --- a/src/udev/udevadm-test-builtin.c +++ b/src/udev/udevadm-test-builtin.c @@ -66,7 +66,7 @@ static int parse_argv(int argc, char *argv[]) { arg_syspath = argv[optind++]; if (!arg_syspath) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "syspath missing."); + "device is missing."); return 1; } diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index 01057e125..8adebbc83 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -135,8 +135,11 @@ int test_main(int argc, char *argv[], void *userdata) { ORDERED_HASHMAP_FOREACH_KEY(val, cmd, event->run_list) { char program[UDEV_PATH_SIZE]; + bool truncated; - (void) udev_event_apply_format(event, cmd, program, sizeof(program), false); + (void) udev_event_apply_format(event, cmd, program, sizeof(program), false, &truncated); + if (truncated) + log_warning("The command '%s' is truncated while substituting into '%s'.", program, cmd); printf("run: '%s'\n", program); } diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index 7c121c369..56921e2cc 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -11,6 +11,7 @@ #include "device-util.h" #include "fd-util.h" #include "fileio.h" +#include "parse-util.h" #include "path-util.h" #include "process-util.h" #include "set.h" @@ -226,10 +227,14 @@ static int help(void) { " -y --sysname-match=NAME Trigger devices with this /sys path\n" " --name-match=NAME Trigger devices with this /dev name\n" " -b --parent-match=NAME Trigger devices with that parent device\n" + " --initialized-match Trigger devices that are already initialized\n" + " --initialized-nomatch Trigger devices that are not initialized yet\n" " -w --settle Wait for the triggered events to complete\n" " --wait-daemon[=SECONDS] Wait for udevd daemon to be initialized\n" " before triggering uevents\n" - " --uuid Print synthetic uevent UUID\n", + " --uuid Print synthetic uevent UUID\n" + " --prioritized-subsystem=SUBSYSTEM[,SUBSYSTEM…]\n" + " Trigger devices from a matching subsystem first\n", program_invocation_short_name); return 0; @@ -240,33 +245,40 @@ int trigger_main(int argc, char *argv[], void *userdata) { ARG_NAME = 0x100, ARG_PING, ARG_UUID, + ARG_PRIORITIZED_SUBSYSTEM, + ARG_INITIALIZED_MATCH, + ARG_INITIALIZED_NOMATCH, }; static const struct option options[] = { - { "verbose", no_argument, NULL, 'v' }, - { "dry-run", no_argument, NULL, 'n' }, - { "quiet", no_argument, NULL, 'q' }, - { "type", required_argument, NULL, 't' }, - { "action", required_argument, NULL, 'c' }, - { "subsystem-match", required_argument, NULL, 's' }, - { "subsystem-nomatch", required_argument, NULL, 'S' }, - { "attr-match", required_argument, NULL, 'a' }, - { "attr-nomatch", required_argument, NULL, 'A' }, - { "property-match", required_argument, NULL, 'p' }, - { "tag-match", required_argument, NULL, 'g' }, - { "sysname-match", required_argument, NULL, 'y' }, - { "name-match", required_argument, NULL, ARG_NAME }, - { "parent-match", required_argument, NULL, 'b' }, - { "settle", no_argument, NULL, 'w' }, - { "wait-daemon", optional_argument, NULL, ARG_PING }, - { "version", no_argument, NULL, 'V' }, - { "help", no_argument, NULL, 'h' }, - { "uuid", no_argument, NULL, ARG_UUID }, + { "verbose", no_argument, NULL, 'v' }, + { "dry-run", no_argument, NULL, 'n' }, + { "quiet", no_argument, NULL, 'q' }, + { "type", required_argument, NULL, 't' }, + { "action", required_argument, NULL, 'c' }, + { "subsystem-match", required_argument, NULL, 's' }, + { "subsystem-nomatch", required_argument, NULL, 'S' }, + { "attr-match", required_argument, NULL, 'a' }, + { "attr-nomatch", required_argument, NULL, 'A' }, + { "property-match", required_argument, NULL, 'p' }, + { "tag-match", required_argument, NULL, 'g' }, + { "sysname-match", required_argument, NULL, 'y' }, + { "name-match", required_argument, NULL, ARG_NAME }, + { "parent-match", required_argument, NULL, 'b' }, + { "initialized-match", no_argument, NULL, ARG_INITIALIZED_MATCH }, + { "initialized-nomatch", no_argument, NULL, ARG_INITIALIZED_NOMATCH }, + { "settle", no_argument, NULL, 'w' }, + { "wait-daemon", optional_argument, NULL, ARG_PING }, + { "version", no_argument, NULL, 'V' }, + { "help", no_argument, NULL, 'h' }, + { "uuid", no_argument, NULL, ARG_UUID }, + { "prioritized-subsystem", required_argument, NULL, ARG_PRIORITIZED_SUBSYSTEM }, {} }; enum { TYPE_DEVICES, TYPE_SUBSYSTEMS, + TYPE_ALL, } device_type = TYPE_DEVICES; sd_device_action_t action = SD_DEVICE_CHANGE; _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL; @@ -309,6 +321,8 @@ int trigger_main(int argc, char *argv[], void *userdata) { device_type = TYPE_DEVICES; else if (streq(optarg, "subsystems")) device_type = TYPE_SUBSYSTEMS; + else if (streq(optarg, "all")) + device_type = TYPE_ALL; else return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg); break; @@ -382,7 +396,7 @@ int trigger_main(int argc, char *argv[], void *userdata) { case ARG_NAME: { _cleanup_(sd_device_unrefp) sd_device *dev = NULL; - r = find_device(optarg, "/dev/", &dev); + r = find_device(optarg, "/dev", &dev); if (r < 0) return log_error_errno(r, "Failed to open the device '%s': %m", optarg); @@ -405,6 +419,26 @@ int trigger_main(int argc, char *argv[], void *userdata) { arg_uuid = true; break; + case ARG_PRIORITIZED_SUBSYSTEM: { + _cleanup_strv_free_ char **subsystems = NULL; + + subsystems = strv_split(optarg, ","); + if (!subsystems) + return log_error_errno(r, "Failed to parse prioritized subsystem '%s': %m", optarg); + + STRV_FOREACH(p, subsystems) { + r = device_enumerator_add_prioritized_subsystem(e, *p); + if (r < 0) + return log_error_errno(r, "Failed to add prioritized subsystem '%s': %m", *p); + } + break; + } + case ARG_INITIALIZED_MATCH: + case ARG_INITIALIZED_NOMATCH: + r = device_enumerator_add_match_is_initialized(e, c == ARG_INITIALIZED_MATCH ? MATCH_INITIALIZED_YES : MATCH_INITIALIZED_NO); + if (r < 0) + return log_error_errno(r, "Failed to set initialized filter: %m"); + break; case 'V': return print_version(); case 'h': @@ -477,6 +511,11 @@ int trigger_main(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to scan devices: %m"); break; + case TYPE_ALL: + r = device_enumerator_scan_devices_and_subsystems(e); + if (r < 0) + return log_error_errno(r, "Failed to scan devices and subsystems: %m"); + break; default: assert_not_reached(); } diff --git a/src/udev/udevadm-util.c b/src/udev/udevadm-util.c index 9649e5a20..52a726709 100644 --- a/src/udev/udevadm-util.c +++ b/src/udev/udevadm-util.c @@ -71,44 +71,49 @@ static int find_device_from_unit(const char *unit_name, sd_device **ret) { } int find_device(const char *id, const char *prefix, sd_device **ret) { - _cleanup_free_ char *path = NULL; - int r; - assert(id); assert(ret); - if (prefix) { - if (!path_startswith(id, prefix)) { - id = path = path_join(prefix, id); - if (!path) - return -ENOMEM; - } - } else { - /* In cases where the argument is generic (no prefix specified), - * check if the argument looks like a device unit name. */ - r = find_device_from_unit(id, ret); - if (r >= 0) - return r; + if (find_device_from_path(id, ret) >= 0) + return 0; + + if (prefix && !path_startswith(id, prefix)) { + _cleanup_free_ char *path = NULL; + + path = path_join(prefix, id); + if (!path) + return -ENOMEM; + + if (find_device_from_path(path, ret) >= 0) + return 0; } - return find_device_from_path(id, ret); + /* Check if the argument looks like a device unit name. */ + return find_device_from_unit(id, ret); } int find_device_with_action(const char *id, sd_device_action_t action, sd_device **ret) { - _cleanup_free_ char *path = NULL; + _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + int r; assert(id); assert(ret); assert(action >= 0 && action < _SD_DEVICE_ACTION_MAX); - if (!path_startswith(id, "/sys")) { - path = path_join("/sys", id); - if (!path) - return -ENOMEM; - id = path; - } + r = find_device(id, "/sys", &dev); + if (r < 0) + return r; - return device_new_from_synthetic_event(ret, id, device_action_to_string(action)); + r = device_read_uevent_file(dev); + if (r < 0) + return r; + + r = device_set_action(dev, action); + if (r < 0) + return r; + + *ret = TAKE_PTR(dev); + return 0; } int parse_device_action(const char *str, sd_device_action_t *action) { diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 8380d674c..045e5d131 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -28,12 +28,14 @@ #include "sd-event.h" #include "alloc-util.h" +#include "cgroup-setup.h" #include "cgroup-util.h" #include "cpu-set-util.h" #include "dev-setup.h" #include "device-monitor-private.h" #include "device-private.h" #include "device-util.h" +#include "errno-list.h" #include "event-util.h" #include "fd-util.h" #include "fileio.h" @@ -48,6 +50,7 @@ #include "mkdir.h" #include "netlink-util.h" #include "parse-util.h" +#include "path-util.h" #include "pretty-print.h" #include "proc-cmdline.h" #include "process-util.h" @@ -68,6 +71,8 @@ #include "version.h" #define WORKER_NUM_MAX 2048U +#define EVENT_RETRY_INTERVAL_USEC (200 * USEC_PER_MSEC) +#define EVENT_RETRY_TIMEOUT_USEC (3 * USEC_PER_MINUTE) static bool arg_debug = false; static int arg_daemonize = false; @@ -85,7 +90,7 @@ typedef struct Manager { sd_event *event; Hashmap *workers; LIST_HEAD(Event, events); - const char *cgroup; + char *cgroup; pid_t pid; /* the process that originally allocated the manager object */ int log_level; @@ -122,10 +127,12 @@ typedef struct Event { EventState state; sd_device *dev; - sd_device *dev_kernel; /* clone of originally received device */ + sd_device_action_t action; uint64_t seqnum; uint64_t blocker_seqnum; + usec_t retry_again_next_usec; + usec_t retry_again_timeout_usec; sd_event_source *timeout_warning_event; sd_event_source *timeout_event; @@ -150,8 +157,17 @@ typedef struct Worker { } Worker; /* passed from worker to main process */ -typedef struct WorkerMessage { -} WorkerMessage; +typedef enum EventResult { + EVENT_RESULT_NERRNO_MIN = -ERRNO_MAX, + EVENT_RESULT_NERRNO_MAX = -1, + EVENT_RESULT_EXIT_STATUS_BASE = 0, + EVENT_RESULT_EXIT_STATUS_MAX = 255, + EVENT_RESULT_TRY_AGAIN = 256, /* when the block device is locked by another process. */ + EVENT_RESULT_SIGNAL_BASE = 257, + EVENT_RESULT_SIGNAL_MAX = EVENT_RESULT_SIGNAL_BASE + _NSIG, + _EVENT_RESULT_MAX, + _EVENT_RESULT_INVALID = -EINVAL, +} EventResult; static Event *event_free(Event *event) { if (!event) @@ -161,27 +177,18 @@ static Event *event_free(Event *event) { LIST_REMOVE(event, event->manager->events, event); sd_device_unref(event->dev); - sd_device_unref(event->dev_kernel); - sd_event_source_unref(event->timeout_warning_event); - sd_event_source_unref(event->timeout_event); + sd_event_source_disable_unref(event->timeout_warning_event); + sd_event_source_disable_unref(event->timeout_event); if (event->worker) event->worker->event = NULL; - /* only clean up the queue from the process that created it */ - if (LIST_IS_EMPTY(event->manager->events) && - event->manager->pid == getpid_cached()) - if (unlink("/run/udev/queue") < 0 && errno != ENOENT) - log_warning_errno(errno, "Failed to unlink /run/udev/queue, ignoring: %m"); - return mfree(event); } static void event_queue_cleanup(Manager *manager, EventState match_state) { - Event *event, *tmp; - - LIST_FOREACH_SAFE(event, event, tmp, manager->events) { + LIST_FOREACH(event, event, manager->events) { if (match_state != EVENT_UNDEF && match_state != event->state) continue; @@ -208,6 +215,8 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(worker_hash_op, void, trivial_hash static void manager_clear_for_worker(Manager *manager) { assert(manager); + /* Do not use sd_event_source_disable_unref() here, as this is called by both workers and the + * main process. */ manager->inotify_event = sd_event_source_unref(manager->inotify_event); manager->kill_workers_event = sd_event_source_unref(manager->kill_workers_event); @@ -238,6 +247,7 @@ static Manager* manager_free(Manager *manager) { safe_close(manager->inotify_fd); safe_close_pair(manager->worker_watch); + free(manager->cgroup); return mfree(manager); } @@ -305,7 +315,7 @@ static void manager_exit(Manager *manager) { /* close sources of new events and discard buffered events */ manager->ctrl = udev_ctrl_unref(manager->ctrl); - manager->inotify_event = sd_event_source_unref(manager->inotify_event); + manager->inotify_event = sd_event_source_disable_unref(manager->inotify_event); manager->inotify_fd = safe_close(manager->inotify_fd); manager->monitor = sd_device_monitor_unref(manager->monitor); @@ -351,10 +361,100 @@ static int on_kill_workers_event(sd_event_source *s, uint64_t usec, void *userda return 1; } -static int worker_send_message(int fd) { - WorkerMessage message = {}; +static void device_broadcast(sd_device_monitor *monitor, sd_device *dev, int result) { + int r; - return loop_write(fd, &message, sizeof(message), false); + assert(dev); + + /* On exit, manager->monitor is already NULL. */ + if (!monitor) + return; + + if (result != 0) { + (void) device_add_property(dev, "UDEV_WORKER_FAILED", "1"); + + switch (result) { + case EVENT_RESULT_NERRNO_MIN ... EVENT_RESULT_NERRNO_MAX: + (void) device_add_propertyf(dev, "UDEV_WORKER_ERRNO", "%i", -result); + (void) device_add_propertyf(dev, "UDEV_WORKER_ERRNO_NAME", "%s", strna(errno_to_name(result))); + break; + + case EVENT_RESULT_EXIT_STATUS_BASE ... EVENT_RESULT_EXIT_STATUS_MAX: + (void) device_add_propertyf(dev, "UDEV_WORKER_EXIT_STATUS", "%i", result - EVENT_RESULT_EXIT_STATUS_BASE); + break; + + case EVENT_RESULT_TRY_AGAIN: + assert_not_reached(); + break; + + case EVENT_RESULT_SIGNAL_BASE ... EVENT_RESULT_SIGNAL_MAX: + (void) device_add_propertyf(dev, "UDEV_WORKER_SIGNAL", "%i", result - EVENT_RESULT_SIGNAL_BASE); + (void) device_add_propertyf(dev, "UDEV_WORKER_SIGNAL_NAME", "%s", strna(signal_to_string(result - EVENT_RESULT_SIGNAL_BASE))); + break; + + default: + log_device_warning(dev, "Unknown event result \"%i\", ignoring.", result); + } + } + + r = device_monitor_send_device(monitor, NULL, dev); + if (r < 0) + log_device_warning_errno(dev, r, + "Failed to broadcast event to libudev listeners, ignoring: %m"); +} + +static int worker_send_result(Manager *manager, int result) { + assert(manager); + assert(manager->worker_watch[WRITE_END] >= 0); + + return loop_write(manager->worker_watch[WRITE_END], &result, sizeof(result), false); +} + +static int device_get_block_device(sd_device *dev, const char **ret) { + const char *val; + int r; + + assert(dev); + assert(ret); + + if (device_for_action(dev, SD_DEVICE_REMOVE)) + goto irrelevant; + + r = sd_device_get_subsystem(dev, &val); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get subsystem: %m"); + + if (!streq(val, "block")) + goto irrelevant; + + r = sd_device_get_sysname(dev, &val); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get sysname: %m"); + + if (STARTSWITH_SET(val, "dm-", "md", "drbd")) + goto irrelevant; + + r = sd_device_get_devtype(dev, &val); + if (r < 0 && r != -ENOENT) + return log_device_debug_errno(dev, r, "Failed to get devtype: %m"); + if (r >= 0 && streq(val, "partition")) { + r = sd_device_get_parent(dev, &dev); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get parent device: %m"); + } + + r = sd_device_get_devname(dev, &val); + if (r == -ENOENT) + goto irrelevant; + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to get devname: %m"); + + *ret = val; + return 1; + +irrelevant: + *ret = NULL; + return 0; } static int worker_lock_block_device(sd_device *dev, int *ret_fd) { @@ -370,42 +470,21 @@ static int worker_lock_block_device(sd_device *dev, int *ret_fd) { * event handling; in the case udev acquired the lock, the external process can block until udev has * finished its event handling. */ - if (device_for_action(dev, SD_DEVICE_REMOVE)) - return 0; - - r = sd_device_get_subsystem(dev, &val); + r = device_get_block_device(dev, &val); if (r < 0) - return log_device_debug_errno(dev, r, "Failed to get subsystem: %m"); - - if (!streq(val, "block")) - return 0; - - r = sd_device_get_sysname(dev, &val); - if (r < 0) - return log_device_debug_errno(dev, r, "Failed to get sysname: %m"); - - if (STARTSWITH_SET(val, "dm-", "md", "drbd")) - return 0; - - r = sd_device_get_devtype(dev, &val); - if (r < 0 && r != -ENOENT) - return log_device_debug_errno(dev, r, "Failed to get devtype: %m"); - if (r >= 0 && streq(val, "partition")) { - r = sd_device_get_parent(dev, &dev); - if (r < 0) - return log_device_debug_errno(dev, r, "Failed to get parent device: %m"); - } - - r = sd_device_get_devname(dev, &val); - if (r == -ENOENT) - return 0; - if (r < 0) - return log_device_debug_errno(dev, r, "Failed to get devname: %m"); + return r; + if (r == 0) + goto nolock; fd = open(val, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NONBLOCK); if (fd < 0) { - log_device_debug_errno(dev, errno, "Failed to open '%s', ignoring: %m", val); - return 0; + bool ignore = ERRNO_IS_DEVICE_ABSENT(errno); + + log_device_debug_errno(dev, errno, "Failed to open '%s'%s: %m", val, ignore ? ", ignoring" : ""); + if (!ignore) + return -errno; + + goto nolock; } if (flock(fd, LOCK_SH|LOCK_NB) < 0) @@ -413,6 +492,10 @@ static int worker_lock_block_device(sd_device *dev, int *ret_fd) { *ret_fd = TAKE_FD(fd); return 1; + +nolock: + *ret_fd = -1; + return 0; } static int worker_mark_block_device_read_only(sd_device *dev) { @@ -479,44 +562,14 @@ static int worker_process_device(Manager *manager, sd_device *dev) { if (!udev_event) return -ENOMEM; + /* If this is a block device and the device is locked currently via the BSD advisory locks, + * someone else is using it exclusively. We don't run our udev rules now to not interfere. + * Instead of processing the event, we requeue the event and will try again after a delay. + * + * The user-facing side of this: https://systemd.io/BLOCK_DEVICE_LOCKING */ r = worker_lock_block_device(dev, &fd_lock); - if (r == -EAGAIN) { - /* So this is a block device and the device is locked currently via the BSD advisory locks — - * someone else is exclusively using it. This means we don't run our udev rules now, to not - * interfere. However we want to know when the device is unlocked again, and retrigger the - * device again then, so that the rules are run eventually. For that we use IN_CLOSE_WRITE - * inotify watches (which isn't exactly the same as waiting for the BSD locks to release, but - * not totally off, as long as unlock+close() is done together, as it usually is). - * - * (The user-facing side of this: https://systemd.io/BLOCK_DEVICE_LOCKING) - * - * There's a bit of a chicken and egg problem here for this however: inotify watching is - * supposed to be enabled via an option set via udev rules (OPTIONS+="watch"). If we skip the - * udev rules here however (as we just said we do), we would thus never see that specific - * udev rule, and thus never turn on inotify watching. But in order to catch up eventually - * and run them we we need the inotify watching: hence a classic chicken and egg problem. - * - * Our way out here: if we see the block device locked, unconditionally watch the device via - * inotify, regardless of any explicit request via OPTIONS+="watch". Thus, a device that is - * currently locked via the BSD file locks will be treated as if we ran a single udev rule - * only for it: the one that turns on inotify watching for it. If we eventually see the - * inotify IN_CLOSE_WRITE event, and then run the rules after all and we then realize that - * this wasn't actually requested (i.e. no OPTIONS+="watch" set) we'll simply turn off the - * watching again (see below). Effectively this means: inotify watching is now enabled either - * a) when the udev rules say so, or b) while the device is locked. - * - * Worst case scenario hence: in the (unlikely) case someone locked the device and we clash - * with that we might do inotify watching for a brief moment for a device where we actually - * weren't supposed to. But that shouldn't be too bad, in particular as BSD locks being taken - * on a block device is kinda an indication that the inotify logic is desired too, to some - * degree — they go hand-in-hand after all. */ - - log_device_debug(dev, "Block device is currently locked, installing watch to wait until the lock is released."); - (void) udev_watch_begin(manager->inotify_fd, dev); - - /* Now the watch is installed, let's lock the device again, maybe in the meantime things changed */ - r = worker_lock_block_device(dev, &fd_lock); - } + if (r == -EAGAIN) + return EVENT_RESULT_TRY_AGAIN; if (r < 0) return r; @@ -555,21 +608,19 @@ static int worker_device_monitor_handler(sd_device_monitor *monitor, sd_device * assert(manager); r = worker_process_device(manager, dev); - if (r == -EAGAIN) - /* if we couldn't acquire the flock(), then proceed quietly */ - log_device_debug_errno(dev, r, "Device currently locked, not processing."); + if (r == EVENT_RESULT_TRY_AGAIN) + /* if we couldn't acquire the flock(), then requeue the event */ + log_device_debug(dev, "Block device is currently locked, requeueing the event."); else { if (r < 0) log_device_warning_errno(dev, r, "Failed to process device, ignoring: %m"); /* send processed event back to libudev listeners */ - r = device_monitor_send_device(monitor, NULL, dev); - if (r < 0) - log_device_warning_errno(dev, r, "Failed to send device, ignoring: %m"); + device_broadcast(monitor, dev, r); } /* send udevd the result of the event execution */ - r = worker_send_message(manager->worker_watch[WRITE_END]); + r = worker_send_result(manager, r); if (r < 0) log_device_warning_errno(dev, r, "Failed to send signal to main daemon, ignoring: %m"); @@ -763,10 +814,6 @@ static int event_run(Event *event) { /* Re-enable the debug message for the next batch of events */ log_children_max_reached = true; - /* fork with up-to-date SELinux label database, so the child inherits the up-to-date db - * and, until the next SELinux policy changes, we safe further reloads in future children */ - mac_selinux_maybe_reload(); - /* start new worker and pass initial device */ r = worker_spawn(manager, event); if (r < 0) @@ -778,7 +825,7 @@ static int event_run(Event *event) { static int event_is_blocked(Event *event) { const char *subsystem, *devpath, *devpath_old = NULL; dev_t devnum = makedev(0, 0); - Event *loop_event; + Event *loop_event = NULL; size_t devpath_len; int r, ifindex = 0; bool is_block; @@ -789,11 +836,24 @@ static int event_is_blocked(Event *event) { assert(event->manager); assert(event->blocker_seqnum <= event->seqnum); + if (event->retry_again_next_usec > 0) { + usec_t now_usec; + + r = sd_event_now(event->manager->event, CLOCK_BOOTTIME, &now_usec); + if (r < 0) + return r; + + if (event->retry_again_next_usec <= now_usec) + return true; + } + if (event->blocker_seqnum == event->seqnum) /* we have checked previously and no blocker found */ return false; - LIST_FOREACH(event, loop_event, event->manager->events) { + LIST_FOREACH(event, e, event->manager->events) { + loop_event = e; + /* we already found a later event, earlier cannot block us, no need to check again */ if (loop_event->seqnum < event->blocker_seqnum) continue; @@ -839,10 +899,12 @@ static int event_is_blocked(Event *event) { return r; /* check if queue contains events we depend on */ - LIST_FOREACH(event, loop_event, loop_event) { + LIST_FOREACH(event, e, loop_event) { size_t loop_devpath_len, common; const char *loop_devpath; + loop_event = e; + /* found ourself, no later event can block us */ if (loop_event->seqnum >= event->seqnum) goto no_blocker; @@ -912,7 +974,6 @@ no_blocker: } static int event_queue_start(Manager *manager) { - Event *event, *event_next; usec_t usec; int r; @@ -945,40 +1006,108 @@ static int event_queue_start(Manager *manager) { return log_warning_errno(r, "Failed to read udev rules: %m"); } - LIST_FOREACH_SAFE(event, event, event_next, manager->events) { + /* fork with up-to-date SELinux label database, so the child inherits the up-to-date db + * and, until the next SELinux policy changes, we safe further reloads in future children */ + mac_selinux_maybe_reload(); + + LIST_FOREACH(event, event, manager->events) { if (event->state != EVENT_QUEUED) continue; /* do not start event if parent or child event is still running or queued */ r = event_is_blocked(event); - if (r < 0) { - sd_device_action_t a = _SD_DEVICE_ACTION_INVALID; - - (void) sd_device_get_action(event->dev, &a); - log_device_warning_errno(event->dev, r, - "Failed to check event dependency, " - "skipping event (SEQNUM=%"PRIu64", ACTION=%s)", - event->seqnum, - strna(device_action_to_string(a))); - - event_free(event); - return r; - } if (r > 0) continue; + if (r < 0) + log_device_warning_errno(event->dev, r, + "Failed to check dependencies for event (SEQNUM=%"PRIu64", ACTION=%s), " + "assuming there is no blocking event, ignoring: %m", + event->seqnum, + strna(device_action_to_string(event->action))); r = event_run(event); - if (r <= 0) + if (r <= 0) /* 0 means there are no idle workers. Let's escape from the loop. */ return r; } return 0; } +static int event_requeue(Event *event) { + usec_t now_usec; + int r; + + assert(event); + assert(event->manager); + assert(event->manager->event); + + event->timeout_warning_event = sd_event_source_disable_unref(event->timeout_warning_event); + event->timeout_event = sd_event_source_disable_unref(event->timeout_event); + + /* add a short delay to suppress busy loop */ + r = sd_event_now(event->manager->event, CLOCK_BOOTTIME, &now_usec); + if (r < 0) + return log_device_warning_errno(event->dev, r, + "Failed to get current time, " + "skipping event (SEQNUM=%"PRIu64", ACTION=%s): %m", + event->seqnum, strna(device_action_to_string(event->action))); + + if (event->retry_again_timeout_usec > 0 && event->retry_again_timeout_usec <= now_usec) + return log_device_warning_errno(event->dev, SYNTHETIC_ERRNO(ETIMEDOUT), + "The underlying block device is locked by a process more than %s, " + "skipping event (SEQNUM=%"PRIu64", ACTION=%s).", + FORMAT_TIMESPAN(EVENT_RETRY_TIMEOUT_USEC, USEC_PER_MINUTE), + event->seqnum, strna(device_action_to_string(event->action))); + + event->retry_again_next_usec = usec_add(now_usec, EVENT_RETRY_INTERVAL_USEC); + if (event->retry_again_timeout_usec == 0) + event->retry_again_timeout_usec = usec_add(now_usec, EVENT_RETRY_TIMEOUT_USEC); + + if (event->worker && event->worker->event == event) + event->worker->event = NULL; + event->worker = NULL; + + event->state = EVENT_QUEUED; + return 0; +} + +static int event_queue_assume_block_device_unlocked(Manager *manager, sd_device *dev) { + const char *devname; + int r; + + /* When a new event for a block device is queued or we get an inotify event, assume that the + * device is not locked anymore. The assumption may not be true, but that should not cause any + * issues, as in that case events will be requeued soon. */ + + r = device_get_block_device(dev, &devname); + if (r <= 0) + return r; + + LIST_FOREACH(event, event, manager->events) { + const char *event_devname; + + if (event->state != EVENT_QUEUED) + continue; + + if (event->retry_again_next_usec == 0) + continue; + + if (device_get_block_device(event->dev, &event_devname) <= 0) + continue; + + if (!streq(devname, event_devname)) + continue; + + event->retry_again_next_usec = 0; + } + + return 0; +} + static int event_queue_insert(Manager *manager, sd_device *dev) { - _cleanup_(sd_device_unrefp) sd_device *clone = NULL; - Event *event; + sd_device_action_t action; uint64_t seqnum; + Event *event; int r; assert(manager); @@ -992,12 +1121,7 @@ static int event_queue_insert(Manager *manager, sd_device *dev) { if (r < 0) return r; - /* Save original device to restore the state on failures. */ - r = device_shallow_clone(dev, &clone); - if (r < 0) - return r; - - r = device_copy_properties(clone, dev); + r = sd_device_get_action(dev, &action); if (r < 0) return r; @@ -1008,8 +1132,8 @@ static int event_queue_insert(Manager *manager, sd_device *dev) { *event = (Event) { .manager = manager, .dev = sd_device_ref(dev), - .dev_kernel = TAKE_PTR(clone), .seqnum = seqnum, + .action = action, .state = EVENT_QUEUED, }; @@ -1042,6 +1166,8 @@ static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) return 1; } + (void) event_queue_assume_block_device_unlocked(manager, dev); + /* we have fresh events, try to schedule them */ event_queue_start(manager); @@ -1054,11 +1180,8 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat assert(manager); for (;;) { - WorkerMessage msg; - struct iovec iovec = { - .iov_base = &msg, - .iov_len = sizeof(msg), - }; + int result; + struct iovec iovec = IOVEC_MAKE(&result, sizeof(result)); CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct ucred))) control; struct msghdr msghdr = { .msg_iov = &iovec, @@ -1081,7 +1204,7 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat cmsg_close_all(&msghdr); - if (size != sizeof(WorkerMessage)) { + if (size != sizeof(result)) { log_warning("Ignoring worker message with invalid size %zi bytes", size); continue; } @@ -1106,6 +1229,11 @@ static int on_worker(sd_event_source *s, int fd, uint32_t revents, void *userdat worker->state = WORKER_IDLE; /* worker returned */ + if (result == EVENT_RESULT_TRY_AGAIN && + event_requeue(worker->event) < 0) + device_broadcast(manager->monitor, worker->event->dev, -ETIMEDOUT); + + /* When event_requeue() succeeds, worker->event is NULL, and event_free() handles NULL gracefully. */ event_free(worker->event); } @@ -1337,7 +1465,6 @@ static int synthesize_change(sd_device *dev) { static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userdata) { Manager *manager = userdata; union inotify_event_buffer buffer; - struct inotify_event *e; ssize_t l; int r; @@ -1355,7 +1482,7 @@ static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userda return log_error_errno(errno, "Failed to read inotify fd: %m"); } - FOREACH_INOTIFY_EVENT(e, buffer, l) { + FOREACH_INOTIFY_EVENT_WARN(e, buffer, l) { _cleanup_(sd_device_unrefp) sd_device *dev = NULL; const char *devnode; @@ -1369,8 +1496,10 @@ static int on_inotify(sd_event_source *s, int fd, uint32_t revents, void *userda continue; log_device_debug(dev, "Inotify event: %x for %s", e->mask, devnode); - if (e->mask & IN_CLOSE_WRITE) + if (e->mask & IN_CLOSE_WRITE) { + (void) event_queue_assume_block_device_unlocked(manager, dev); (void) synthesize_change(dev); + } /* Do not handle IN_IGNORED here. It should be handled by worker in 'remove' uevent; * udev_event_execute_rules() -> event_execute_rules_on_remove() -> udev_watch_end(). */ @@ -1443,12 +1572,10 @@ static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *si, voi device_delete_db(worker->event->dev); device_tag_index(worker->event->dev, NULL, false); - if (manager->monitor) { - /* forward kernel event without amending it */ - r = device_monitor_send_device(manager->monitor, NULL, worker->event->dev_kernel); - if (r < 0) - log_device_error_errno(worker->event->dev_kernel, r, "Failed to send back device to kernel: %m"); - } + /* Forward kernel event to libudev listeners */ + device_broadcast(manager->monitor, worker->event->dev, + WIFEXITED(status) ? EVENT_RESULT_EXIT_STATUS_BASE + WEXITSTATUS(status): + WIFSIGNALED(status) ? EVENT_RESULT_SIGNAL_BASE + WTERMSIG(status) : 0); } worker_free(worker); @@ -1472,10 +1599,23 @@ static int on_post(sd_event_source *s, void *userdata) { assert(manager); - if (!LIST_IS_EMPTY(manager->events)) + if (!LIST_IS_EMPTY(manager->events)) { + /* Try to process pending events if idle workers exist. Why is this necessary? + * When a worker finished an event and became idle, even if there was a pending event, + * the corresponding device might have been locked and the processing of the event + * delayed for a while, preventing the worker from processing the event immediately. + * Now, the device may be unlocked. Let's try again! */ + event_queue_start(manager); return 1; + } - /* There are no pending events. Let's cleanup idle process. */ + /* There are no queued events. Let's remove /run/udev/queue and clean up the idle processes. */ + + if (unlink("/run/udev/queue") < 0) { + if (errno != ENOENT) + log_warning_errno(errno, "Failed to unlink /run/udev/queue, ignoring: %m"); + } else + log_debug("No events are queued, removing /run/udev/queue."); if (!hashmap_isempty(manager->workers)) { /* There are idle workers */ @@ -1722,12 +1862,63 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cgroup) { +static int create_subcgroup(char **ret) { + _cleanup_free_ char *cgroup = NULL, *subcgroup = NULL; + int r; + + if (getppid() != 1) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not invoked by PID1."); + + r = sd_booted(); + if (r < 0) + return log_debug_errno(r, "Failed to check if systemd is running: %m"); + if (r == 0) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "systemd is not running."); + + /* Get our own cgroup, we regularly kill everything udev has left behind. + * We only do this on systemd systems, and only if we are directly spawned + * by PID1. Otherwise we are not guaranteed to have a dedicated cgroup. */ + + r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); + if (r < 0) { + if (IN_SET(r, -ENOENT, -ENOMEDIUM)) + return log_debug_errno(r, "Dedicated cgroup not found: %m"); + return log_debug_errno(r, "Failed to get cgroup: %m"); + } + + r = cg_get_xattr_bool(SYSTEMD_CGROUP_CONTROLLER, cgroup, "trusted.delegate"); + if (IN_SET(r, 0, -ENODATA)) + return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "The cgroup %s is not delegated to us.", cgroup); + if (r < 0) + return log_debug_errno(r, "Failed to read trusted.delegate attribute: %m"); + + /* We are invoked with our own delegated cgroup tree, let's move us one level down, so that we + * don't collide with the "no processes in inner nodes" rule of cgroups, when the service + * manager invokes the ExecReload= job in the .control/ subcgroup. */ + + subcgroup = path_join(cgroup, "/udev"); + if (!subcgroup) + return log_oom_debug(); + + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, subcgroup, 0); + if (r < 0) + return log_debug_errno(r, "Failed to create %s subcgroup: %m", subcgroup); + + log_debug("Created %s subcgroup.", subcgroup); + if (ret) + *ret = TAKE_PTR(subcgroup); + return 0; +} + +static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent) { _cleanup_(manager_freep) Manager *manager = NULL; + _cleanup_free_ char *cgroup = NULL; int r; assert(ret); + (void) create_subcgroup(&cgroup); + manager = new(Manager, 1); if (!manager) return log_oom(); @@ -1735,7 +1926,7 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg *manager = (Manager) { .inotify_fd = -1, .worker_watch = { -1, -1 }, - .cgroup = cgroup, + .cgroup = TAKE_PTR(cgroup), }; r = udev_ctrl_new_from_fd(&manager->ctrl, fd_ctrl); @@ -1880,7 +2071,6 @@ static int main_loop(Manager *manager) { } int run_udevd(int argc, char *argv[]) { - _cleanup_free_ char *cgroup = NULL; _cleanup_(manager_freep) Manager *manager = NULL; int fd_ctrl = -1, fd_uevent = -1; int r; @@ -1937,24 +2127,11 @@ int run_udevd(int argc, char *argv[]) { if (r < 0 && r != -EEXIST) return log_error_errno(r, "Failed to create /run/udev: %m"); - if (getppid() == 1 && sd_booted() > 0) { - /* Get our own cgroup, we regularly kill everything udev has left behind. - * We only do this on systemd systems, and only if we are directly spawned - * by PID1. Otherwise we are not guaranteed to have a dedicated cgroup. */ - r = cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, 0, &cgroup); - if (r < 0) { - if (IN_SET(r, -ENOENT, -ENOMEDIUM)) - log_debug_errno(r, "Dedicated cgroup not found: %m"); - else - log_warning_errno(r, "Failed to get cgroup: %m"); - } - } - r = listen_fds(&fd_ctrl, &fd_uevent); if (r < 0) return log_error_errno(r, "Failed to listen on fds: %m"); - r = manager_new(&manager, fd_ctrl, fd_uevent, cgroup); + r = manager_new(&manager, fd_ctrl, fd_uevent); if (r < 0) return log_error_errno(r, "Failed to create manager: %m"); diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c index c2acd8574..c3d61f005 100644 --- a/src/userdb/userdbctl.c +++ b/src/userdb/userdbctl.c @@ -39,6 +39,29 @@ static bool arg_chain = false; STATIC_DESTRUCTOR_REGISTER(arg_services, strv_freep); +static const char *user_disposition_to_color(UserDisposition d) { + assert(d >= 0); + assert(d < _USER_DISPOSITION_MAX); + + switch (d) { + case USER_INTRINSIC: + return ansi_red(); + + case USER_SYSTEM: + case USER_DYNAMIC: + return ansi_green(); + + case USER_CONTAINER: + return ansi_cyan(); + + case USER_RESERVED: + return ansi_red(); + + default: + return NULL; + } +} + static int show_user(UserRecord *ur, Table *table) { int r; @@ -74,23 +97,29 @@ static int show_user(UserRecord *ur, Table *table) { break; - case OUTPUT_TABLE: + case OUTPUT_TABLE: { + UserDisposition d; + assert(table); + d = user_record_disposition(ur); r = table_add_many( table, + TABLE_STRING, "", TABLE_STRING, ur->user_name, - TABLE_STRING, user_disposition_to_string(user_record_disposition(ur)), + TABLE_SET_COLOR, user_disposition_to_color(d), + TABLE_STRING, user_disposition_to_string(d), TABLE_UID, ur->uid, TABLE_GID, user_record_gid(ur), TABLE_STRING, empty_to_null(ur->real_name), TABLE_STRING, user_record_home_directory(ur), TABLE_STRING, user_record_shell(ur), - TABLE_INT, (int) user_record_disposition(ur)); + TABLE_INT, 0); if (r < 0) return table_log_add_error(r); break; + } default: assert_not_reached(); @@ -99,6 +128,115 @@ static int show_user(UserRecord *ur, Table *table) { return 0; } +static const struct { + uid_t first, last; + const char *name; + UserDisposition disposition; +} uid_range_table[] = { + { + .first = 1, + .last = SYSTEM_UID_MAX, + .name = "system", + .disposition = USER_SYSTEM, + }, + { + .first = DYNAMIC_UID_MIN, + .last = DYNAMIC_UID_MAX, + .name = "dynamic system", + .disposition = USER_DYNAMIC, + }, + { + .first = CONTAINER_UID_BASE_MIN, + .last = CONTAINER_UID_BASE_MAX, + .name = "container", + .disposition = USER_CONTAINER, + }, +#if ENABLE_HOMED + { + .first = HOME_UID_MIN, + .last = HOME_UID_MAX, + .name = "systemd-homed", + .disposition = USER_REGULAR, + }, +#endif + { + .first = MAP_UID_MIN, + .last = MAP_UID_MAX, + .name = "mapped", + .disposition = USER_REGULAR, + }, +}; + +static int table_add_uid_boundaries(Table *table) { + int r; + + assert(table); + + for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) { + _cleanup_free_ char *name = NULL, *comment = NULL; + + name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN), + " begin ", uid_range_table[i].name, " users ", + special_glyph(SPECIAL_GLYPH_ARROW_DOWN)); + if (!name) + return log_oom(); + + comment = strjoin("First ", uid_range_table[i].name, " user"); + if (!comment) + return log_oom(); + + r = table_add_many( + table, + TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_TOP), + TABLE_STRING, name, + TABLE_SET_COLOR, ansi_grey(), + TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition), + TABLE_SET_COLOR, ansi_grey(), + TABLE_UID, uid_range_table[i].first, + TABLE_SET_COLOR, ansi_grey(), + TABLE_EMPTY, + TABLE_STRING, comment, + TABLE_SET_COLOR, ansi_grey(), + TABLE_EMPTY, + TABLE_EMPTY, + TABLE_INT, -1); /* sort before an other entry with the same UID */ + if (r < 0) + return table_log_add_error(r); + + free(name); + name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_UP), + " end ", uid_range_table[i].name, " users ", + special_glyph(SPECIAL_GLYPH_ARROW_UP)); + if (!name) + return log_oom(); + + free(comment); + comment = strjoin("Last ", uid_range_table[i].name, " user"); + if (!comment) + return log_oom(); + + r = table_add_many( + table, + TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT), + TABLE_STRING, name, + TABLE_SET_COLOR, ansi_grey(), + TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition), + TABLE_SET_COLOR, ansi_grey(), + TABLE_UID, uid_range_table[i].last, + TABLE_SET_COLOR, ansi_grey(), + TABLE_EMPTY, + TABLE_STRING, comment, + TABLE_SET_COLOR, ansi_grey(), + TABLE_EMPTY, + TABLE_EMPTY, + TABLE_INT, 1); /* sort after an other entry with the same UID */ + if (r < 0) + return table_log_add_error(r); + } + + return ELEMENTSOF(uid_range_table) * 2; +} + static int display_user(int argc, char *argv[], void *userdata) { _cleanup_(table_unrefp) Table *table = NULL; bool draw_separator = false; @@ -108,20 +246,18 @@ static int display_user(int argc, char *argv[], void *userdata) { arg_output = argc > 1 ? OUTPUT_FRIENDLY : OUTPUT_TABLE; if (arg_output == OUTPUT_TABLE) { - table = table_new("name", "disposition", "uid", "gid", "realname", "home", "shell", "disposition-numeric"); + table = table_new(" ", "name", "disposition", "uid", "gid", "realname", "home", "shell", "order"); if (!table) return log_oom(); - (void) table_set_align_percent(table, table_get_cell(table, 0, 2), 100); (void) table_set_align_percent(table, table_get_cell(table, 0, 3), 100); + (void) table_set_align_percent(table, table_get_cell(table, 0, 4), 100); (void) table_set_empty_string(table, "-"); - (void) table_set_sort(table, (size_t) 7, (size_t) 2); - (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 5, (size_t) 6); + (void) table_set_sort(table, (size_t) 3, (size_t) 8); + (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4, (size_t) 5, (size_t) 6, (size_t) 7); } - if (argc > 1) { - char **i; - + if (argc > 1) STRV_FOREACH(i, argv + 1) { _cleanup_(user_record_unrefp) UserRecord *ur = NULL; uid_t uid; @@ -151,7 +287,7 @@ static int display_user(int argc, char *argv[], void *userdata) { draw_separator = true; } } - } else { + else { _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; r = userdb_all(arg_userdb_flags, &iterator); @@ -186,6 +322,12 @@ static int display_user(int argc, char *argv[], void *userdata) { } if (table) { + int boundary_lines; + + boundary_lines = table_add_uid_boundaries(table); + if (boundary_lines < 0) + return boundary_lines; + if (table_get_rows(table) > 1) { r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend); if (r < 0) @@ -194,7 +336,7 @@ static int display_user(int argc, char *argv[], void *userdata) { if (arg_legend) { if (table_get_rows(table) > 1) - printf("\n%zu users listed.\n", table_get_rows(table) - 1); + printf("\n%zu users listed.\n", table_get_rows(table) - 1 - boundary_lines); else printf("No users.\n"); } @@ -241,20 +383,26 @@ static int show_group(GroupRecord *gr, Table *table) { break; - case OUTPUT_TABLE: + case OUTPUT_TABLE: { + UserDisposition d; + assert(table); + d = group_record_disposition(gr); r = table_add_many( table, + TABLE_STRING, "", TABLE_STRING, gr->group_name, - TABLE_STRING, user_disposition_to_string(group_record_disposition(gr)), + TABLE_SET_COLOR, user_disposition_to_color(d), + TABLE_STRING, user_disposition_to_string(d), TABLE_GID, gr->gid, TABLE_STRING, gr->description, - TABLE_INT, (int) group_record_disposition(gr)); + TABLE_INT, 0); if (r < 0) return table_log_add_error(r); break; + } default: assert_not_reached(); @@ -263,6 +411,69 @@ static int show_group(GroupRecord *gr, Table *table) { return 0; } +static int table_add_gid_boundaries(Table *table) { + int r; + + assert(table); + + for (size_t i = 0; i < ELEMENTSOF(uid_range_table); i++) { + _cleanup_free_ char *name = NULL, *comment = NULL; + + name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_DOWN), + " begin ", uid_range_table[i].name, " groups ", + special_glyph(SPECIAL_GLYPH_ARROW_DOWN)); + if (!name) + return log_oom(); + + comment = strjoin("First ", uid_range_table[i].name, " group"); + if (!comment) + return log_oom(); + + r = table_add_many( + table, + TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_TOP), + TABLE_STRING, name, + TABLE_SET_COLOR, ansi_grey(), + TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition), + TABLE_SET_COLOR, ansi_grey(), + TABLE_GID, uid_range_table[i].first, + TABLE_SET_COLOR, ansi_grey(), + TABLE_STRING, comment, + TABLE_SET_COLOR, ansi_grey(), + TABLE_INT, -1); /* sort before an other entry with the same GID */ + if (r < 0) + return table_log_add_error(r); + + free(name); + name = strjoin(special_glyph(SPECIAL_GLYPH_ARROW_UP), + " end ", uid_range_table[i].name, " groups ", + special_glyph(SPECIAL_GLYPH_ARROW_UP)); + if (!name) + return log_oom(); + + free(comment); + comment = strjoin("Last ", uid_range_table[i].name, " group"); + if (!comment) + return log_oom(); + + r = table_add_many( + table, + TABLE_STRING, special_glyph(SPECIAL_GLYPH_TREE_RIGHT), + TABLE_STRING, name, + TABLE_SET_COLOR, ansi_grey(), + TABLE_STRING, user_disposition_to_string(uid_range_table[i].disposition), + TABLE_SET_COLOR, ansi_grey(), + TABLE_GID, uid_range_table[i].last, + TABLE_SET_COLOR, ansi_grey(), + TABLE_STRING, comment, + TABLE_SET_COLOR, ansi_grey(), + TABLE_INT, 1); /* sort after an other entry with the same GID */ + if (r < 0) + return table_log_add_error(r); + } + + return ELEMENTSOF(uid_range_table) * 2; +} static int display_group(int argc, char *argv[], void *userdata) { _cleanup_(table_unrefp) Table *table = NULL; @@ -273,19 +484,17 @@ static int display_group(int argc, char *argv[], void *userdata) { arg_output = argc > 1 ? OUTPUT_FRIENDLY : OUTPUT_TABLE; if (arg_output == OUTPUT_TABLE) { - table = table_new("name", "disposition", "gid", "description", "disposition-numeric"); + table = table_new(" ", "name", "disposition", "gid", "description", "order"); if (!table) return log_oom(); - (void) table_set_align_percent(table, table_get_cell(table, 0, 2), 100); + (void) table_set_align_percent(table, table_get_cell(table, 0, 3), 100); (void) table_set_empty_string(table, "-"); - (void) table_set_sort(table, (size_t) 3, (size_t) 2); - (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3); + (void) table_set_sort(table, (size_t) 3, (size_t) 5); + (void) table_set_display(table, (size_t) 0, (size_t) 1, (size_t) 2, (size_t) 3, (size_t) 4); } - if (argc > 1) { - char **i; - + if (argc > 1) STRV_FOREACH(i, argv + 1) { _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; gid_t gid; @@ -315,8 +524,7 @@ static int display_group(int argc, char *argv[], void *userdata) { draw_separator = true; } } - - } else { + else { _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; r = groupdb_all(arg_userdb_flags, &iterator); @@ -351,6 +559,12 @@ static int display_group(int argc, char *argv[], void *userdata) { } if (table) { + int boundary_lines; + + boundary_lines = table_add_gid_boundaries(table); + if (boundary_lines < 0) + return boundary_lines; + if (table_get_rows(table) > 1) { r = table_print_with_pager(table, arg_json_format_flags, arg_pager_flags, arg_legend); if (r < 0) @@ -359,7 +573,7 @@ static int display_group(int argc, char *argv[], void *userdata) { if (arg_legend) { if (table_get_rows(table) > 1) - printf("\n%zu groups listed.\n", table_get_rows(table) - 1); + printf("\n%zu groups listed.\n", table_get_rows(table) - 1 - boundary_lines); else printf("No groups.\n"); } @@ -435,9 +649,7 @@ static int display_memberships(int argc, char *argv[], void *userdata) { (void) table_set_sort(table, (size_t) 0, (size_t) 1); } - if (argc > 1) { - char **i; - + if (argc > 1) STRV_FOREACH(i, argv + 1) { _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; @@ -468,7 +680,7 @@ static int display_memberships(int argc, char *argv[], void *userdata) { return r; } } - } else { + else { _cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL; r = membershipdb_all(arg_userdb_flags, &iterator); @@ -630,12 +842,9 @@ static int ssh_authorized_keys(int argc, char *argv[], void *userdata) { else { if (strv_isempty(ur->ssh_authorized_keys)) log_debug("User record for %s has no public SSH keys.", argv[1]); - else { - char **i; - + else STRV_FOREACH(i, ur->ssh_authorized_keys) printf("%s\n", *i); - } if (ur->incomplete) { fflush(stdout); diff --git a/src/userdb/userdbd-manager.c b/src/userdb/userdbd-manager.c index 0564840db..aabf8070d 100644 --- a/src/userdb/userdbd-manager.c +++ b/src/userdb/userdbd-manager.c @@ -251,7 +251,6 @@ static int start_workers(Manager *m, bool explicit_request) { } int manager_startup(Manager *m) { - struct timeval ts; int n, r; assert(m); @@ -300,7 +299,7 @@ int manager_startup(Manager *m) { /* Let's make sure every accept() call on this socket times out after 25s. This allows workers to be * GC'ed on idle */ - if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, timeval_store(&ts, LISTEN_TIMEOUT_USEC), sizeof(ts)) < 0) + if (setsockopt(m->listen_fd, SOL_SOCKET, SO_RCVTIMEO, TIMEVAL_STORE(LISTEN_TIMEOUT_USEC), sizeof(struct timeval)) < 0) return log_error_errno(errno, "Failed to se SO_RCVTIMEO: %m"); return start_workers(m, false); diff --git a/src/veritysetup/veritysetup-generator.c b/src/veritysetup/veritysetup-generator.c index e38bd0415..3481362e5 100644 --- a/src/veritysetup/veritysetup-generator.c +++ b/src/veritysetup/veritysetup-generator.c @@ -213,7 +213,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat return log_oom(); } else if (proc_cmdline_key_streq(key, "usrhash")) { - + if (proc_cmdline_value_missing(key, value)) return 0; @@ -285,7 +285,7 @@ static int determine_device( if (!*data_what) { memcpy(&data_uuid, m, sizeof(data_uuid)); - *data_what = path_join("/dev/disk/by-partuuid", ID128_TO_UUID_STRING(data_uuid)); + *data_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(data_uuid)); if (!*data_what) return log_oom(); } @@ -293,7 +293,7 @@ static int determine_device( if (!*hash_what) { memcpy(&verity_uuid, (uint8_t*) m + l - sizeof(verity_uuid), sizeof(verity_uuid)); - *hash_what = path_join("/dev/disk/by-partuuid", ID128_TO_UUID_STRING(verity_uuid)); + *hash_what = path_join("/dev/disk/by-partuuid", SD_ID128_TO_UUID_STRING(verity_uuid)); if (!*hash_what) return log_oom(); } diff --git a/src/veritysetup/veritysetup.c b/src/veritysetup/veritysetup.c index 61973bf37..1536bb2b8 100644 --- a/src/veritysetup/veritysetup.c +++ b/src/veritysetup/veritysetup.c @@ -151,7 +151,7 @@ static int run(int argc, char *argv[]) { size_t l; if (argc < 6) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least two arguments."); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "attach requires at least four arguments."); r = unhexmem(argv[5], strlen(argv[5]), &m, &l); if (r < 0) diff --git a/src/xdg-autostart-generator/meson.build b/src/xdg-autostart-generator/meson.build index aa722f7f3..cdce66b6f 100644 --- a/src/xdg-autostart-generator/meson.build +++ b/src/xdg-autostart-generator/meson.build @@ -6,13 +6,13 @@ systemd_xdg_autostart_generator_sources = files( 'xdg-autostart-service.h') tests += [ - [['src/xdg-autostart-generator/test-xdg-autostart.c', - 'src/xdg-autostart-generator/xdg-autostart-service.c', - 'src/xdg-autostart-generator/xdg-autostart-service.h']], + [files('test-xdg-autostart.c', + 'xdg-autostart-service.c', + 'xdg-autostart-service.h')], ] fuzzers += [ - [['src/xdg-autostart-generator/fuzz-xdg-desktop.c', - 'src/xdg-autostart-generator/xdg-autostart-service.c', - 'src/xdg-autostart-generator/xdg-autostart-service.h']], + [files('fuzz-xdg-desktop.c', + 'xdg-autostart-service.c', + 'xdg-autostart-service.h')], ] diff --git a/src/xdg-autostart-generator/test-xdg-autostart.c b/src/xdg-autostart-generator/test-xdg-autostart.c index c7a816bc2..a357cf50f 100644 --- a/src/xdg-autostart-generator/test-xdg-autostart.c +++ b/src/xdg-autostart-generator/test-xdg-autostart.c @@ -9,7 +9,7 @@ #include "tmpfile-util.h" #include "xdg-autostart-service.h" -static void test_translate_name(void) { +TEST(translate_name) { _cleanup_free_ char *t; assert_se(t = xdg_autostart_service_translate_name("a-b.blub.desktop")); @@ -24,14 +24,14 @@ static void test_xdg_format_exec_start_one(const char *exec, const char *expecte assert_se(streq(out, expected)); } -static void test_xdg_format_exec_start(void) { - test_xdg_format_exec_start_one("/bin/sleep 100", "/bin/sleep \"100\""); +TEST(xdg_format_exec_start) { + test_xdg_format_exec_start_one("/bin/sleep 100", "/bin/sleep 100"); /* All standardised % identifiers are stripped. */ test_xdg_format_exec_start_one("/bin/sleep %f \"%F\" %u %U %d %D\t%n %N %i %c %k %v %m", "/bin/sleep"); /* Unknown % identifier currently remain, but are escaped. */ - test_xdg_format_exec_start_one("/bin/sleep %X \"%Y\"", "/bin/sleep \"%%X\" \"%%Y\""); + test_xdg_format_exec_start_one("/bin/sleep %X \"%Y\"", "/bin/sleep %%X %%Y"); test_xdg_format_exec_start_one("/bin/sleep \";\\\"\"", "/bin/sleep \";\\\"\""); } @@ -50,7 +50,7 @@ static const char* const xdg_desktop_file[] = { "Hidden=\t true\n"), }; -static void test_xdg_desktop_parse(unsigned i, const char *s) { +static void test_xdg_desktop_parse_one(unsigned i, const char *s) { _cleanup_(unlink_tempfilep) char name[] = "/tmp/test-xdg-autostart-parser.XXXXXX"; _cleanup_fclose_ FILE *f = NULL; _cleanup_(xdg_autostart_service_freep) XdgAutostartService *service = NULL; @@ -80,14 +80,9 @@ static void test_xdg_desktop_parse(unsigned i, const char *s) { } } -int main(int argc, char *argv[]) { - test_setup_logging(LOG_DEBUG); - - test_translate_name(); - test_xdg_format_exec_start(); - +TEST(xdg_desktop_parse) { for (size_t i = 0; i < ELEMENTSOF(xdg_desktop_file); i++) - test_xdg_desktop_parse(i, xdg_desktop_file[i]); - - return 0; + test_xdg_desktop_parse_one(i, xdg_desktop_file[i]); } + +DEFINE_TEST_MAIN(LOG_DEBUG); diff --git a/src/xdg-autostart-generator/xdg-autostart-condition.c b/src/xdg-autostart-generator/xdg-autostart-condition.c index c4485cf62..9ceea6154 100644 --- a/src/xdg-autostart-generator/xdg-autostart-condition.c +++ b/src/xdg-autostart-generator/xdg-autostart-condition.c @@ -13,7 +13,6 @@ static int run(int argc, char *argv[]) { _cleanup_strv_free_ char **only_show_in = NULL, **not_show_in = NULL, **desktops = NULL; const char *xdg_current_desktop; - char **d; if (argc != 3) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), diff --git a/src/xdg-autostart-generator/xdg-autostart-generator.c b/src/xdg-autostart-generator/xdg-autostart-generator.c index c5c6b54fd..91dda27d4 100644 --- a/src/xdg-autostart-generator/xdg-autostart-generator.c +++ b/src/xdg-autostart-generator/xdg-autostart-generator.c @@ -24,7 +24,6 @@ static int enumerate_xdg_autostart(Hashmap *all_services) { _cleanup_strv_free_ char **config_dirs = NULL; _unused_ _cleanup_strv_free_ char **data_dirs = NULL; _cleanup_free_ char *user_config_autostart_dir = NULL; - char **path; int r; r = xdg_user_config_dir(&user_config_autostart_dir, "/autostart"); @@ -44,38 +43,42 @@ static int enumerate_xdg_autostart(Hashmap *all_services) { STRV_FOREACH(path, autostart_dirs) { _cleanup_closedir_ DIR *d = NULL; + log_debug("Scanning autostart directory \"%s\"…", *path); d = opendir(*path); if (!d) { - if (errno != ENOENT) - log_warning_errno(errno, "Opening %s failed, ignoring: %m", *path); + log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno, + "Opening %s failed, ignoring: %m", *path); continue; } FOREACH_DIRENT(de, d, log_warning_errno(errno, "Failed to enumerate directory %s, ignoring: %m", *path)) { - _cleanup_free_ char *fpath = NULL, *name = NULL; - _cleanup_(xdg_autostart_service_freep) XdgAutostartService *service = NULL; struct stat st; - if (fstatat(dirfd(d), de->d_name, &st, 0) < 0) { - log_warning_errno(errno, "stat() failed on %s/%s, ignoring: %m", *path, de->d_name); + log_warning_errno(errno, "%s/%s: stat() failed, ignoring: %m", *path, de->d_name); continue; } - if (!S_ISREG(st.st_mode)) + if (!S_ISREG(st.st_mode)) { + log_debug("%s/%s: not a regular file, ignoring.", *path, de->d_name); continue; + } - name = xdg_autostart_service_translate_name(de->d_name); + _cleanup_free_ char *name = xdg_autostart_service_translate_name(de->d_name); if (!name) return log_oom(); - if (hashmap_contains(all_services, name)) + if (hashmap_contains(all_services, name)) { + log_debug("%s/%s: we have already seen \"%s\", ignoring.", + *path, de->d_name, name); continue; + } - fpath = path_join(*path, de->d_name); + _cleanup_free_ char *fpath = path_join(*path, de->d_name); if (!fpath) return log_oom(); - service = xdg_autostart_service_parse_desktop(fpath); + _cleanup_(xdg_autostart_service_freep) XdgAutostartService *service = + xdg_autostart_service_parse_desktop(fpath); if (!service) return log_oom(); service->name = TAKE_PTR(name); diff --git a/src/xdg-autostart-generator/xdg-autostart-service.c b/src/xdg-autostart-generator/xdg-autostart-service.c index c60a9d81a..9c4a7f240 100644 --- a/src/xdg-autostart-generator/xdg-autostart-service.c +++ b/src/xdg-autostart-generator/xdg-autostart-service.c @@ -288,22 +288,22 @@ static int xdg_config_item_table_lookup( const void *table, const char *section, const char *lvalue, - ConfigParserCallback *func, - int *ltype, - void **data, + ConfigParserCallback *ret_func, + int *ret_ltype, + void **ret_data, void *userdata) { assert(lvalue); /* Ignore any keys with [] as those are translations. */ if (strchr(lvalue, '[')) { - *func = NULL; - *ltype = 0; - *data = NULL; + *ret_func = NULL; + *ret_ltype = 0; + *ret_data = NULL; return 1; } - return config_item_table_lookup(table, section, lvalue, func, ltype, data, userdata); + return config_item_table_lookup(table, section, lvalue, ret_func, ret_ltype, ret_data, userdata); } XdgAutostartService *xdg_autostart_service_parse_desktop(const char *path) { @@ -396,7 +396,7 @@ int xdg_autostart_format_exec_start( first_arg = true; for (i = n = 0; exec_split[i]; i++) { - _cleanup_free_ char *c = NULL, *raw = NULL, *p = NULL, *escaped = NULL, *quoted = NULL; + _cleanup_free_ char *c = NULL, *raw = NULL, *percent = NULL; ssize_t l; l = cunescape(exec_split[i], 0, &c); @@ -412,11 +412,7 @@ int xdg_autostart_format_exec_start( if (r < 0) return log_info_errno(r, "Exec binary '%s' does not exist: %m", c); - escaped = cescape(executable); - if (!escaped) - return log_oom(); - - free_and_replace(exec_split[n++], escaped); + free_and_replace(exec_split[n++], executable); continue; } @@ -445,23 +441,16 @@ int xdg_autostart_format_exec_start( raw = strreplace(c, "%%", "%"); if (!raw) return log_oom(); - p = strreplace(raw, "%", "%%"); - if (!p) - return log_oom(); - escaped = cescape(p); - if (!escaped) + percent = strreplace(raw, "%", "%%"); + if (!percent) return log_oom(); - quoted = strjoin("\"", escaped, "\""); - if (!quoted) - return log_oom(); - - free_and_replace(exec_split[n++], quoted); + free_and_replace(exec_split[n++], percent); } for (; exec_split[n]; n++) exec_split[n] = mfree(exec_split[n]); - res = strv_join(exec_split, " "); + res = quote_command_line(exec_split, SHELL_ESCAPE_EMPTY); if (!res) return log_oom(); @@ -470,6 +459,7 @@ int xdg_autostart_format_exec_start( } static int xdg_autostart_generate_desktop_condition( + const XdgAutostartService *service, FILE *f, const char *test_binary, const char *condition) { @@ -483,7 +473,8 @@ static int xdg_autostart_generate_desktop_condition( r = find_executable(test_binary, &gnome_autostart_condition_path); if (r < 0) { log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, - "%s not found: %m", test_binary); + "%s: ExecCondition executable %s not found, unit will not be started automatically: %m", + service->path, test_binary); fprintf(f, "# ExecCondition using %s skipped due to missing binary.\n", test_binary); return 0; } @@ -492,6 +483,9 @@ static int xdg_autostart_generate_desktop_condition( if (!e_autostart_condition) return log_oom(); + log_debug("%s: ExecCondition converted to %s --condition \"%s\"…", + service->path, gnome_autostart_condition_path, e_autostart_condition); + fprintf(f, "ExecCondition=%s --condition \"%s\"\n", gnome_autostart_condition_path, @@ -502,7 +496,7 @@ static int xdg_autostart_generate_desktop_condition( } int xdg_autostart_service_generate_unit( - XdgAutostartService *service, + const XdgAutostartService *service, const char *dest) { _cleanup_free_ char *path_escaped = NULL, *exec_start = NULL, *unit = NULL; @@ -513,23 +507,23 @@ int xdg_autostart_service_generate_unit( /* Nothing to do for hidden services. */ if (service->hidden) { - log_debug("Not generating service for XDG autostart %s, it is hidden.", service->name); + log_debug("%s: not generating unit, entry is hidden.", service->path); return 0; } if (service->systemd_skip) { - log_debug("Not generating service for XDG autostart %s, should be skipped by generator.", service->name); + log_debug("%s: not generating unit, marked as skipped by generator.", service->path); return 0; } /* Nothing to do if type is not Application. */ if (!streq_ptr(service->type, "Application")) { - log_debug("Not generating service for XDG autostart %s, only Type=Application is supported.", service->name); + log_debug("%s: not generating unit, Type=%s is not supported.", service->path, service->type); return 0; } if (!service->exec_string) { - log_warning("Not generating service for XDG autostart %s, it is has no Exec= line.", service->name); + log_warning("%s: not generating unit, no Exec= line.", service->path); return 0; } @@ -539,24 +533,21 @@ int xdg_autostart_service_generate_unit( r = find_executable(service->try_exec, NULL); if (r < 0) { log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_WARNING, r, - "Not generating service for XDG autostart %s, could not find TryExec= binary %s: %m", - service->name, service->try_exec); + "%s: not generating unit, could not find TryExec= binary %s: %m", + service->path, service->try_exec); return 0; } } r = xdg_autostart_format_exec_start(service->exec_string, &exec_start); if (r < 0) { - log_warning_errno(r, - "Not generating service for XDG autostart %s, error parsing Exec= line: %m", - service->name); + log_warning_errno(r, "%s: not generating unit, error parsing Exec= line: %m", service->path); return 0; } if (service->gnome_autostart_phase) { /* There is no explicit value for the "Application" phase. */ - log_debug("Not generating service for XDG autostart %s, startup phases are not supported.", - service->name); + log_debug("%s: not generating unit, startup phases are not supported.", service->path); return 0; } @@ -570,7 +561,7 @@ int xdg_autostart_service_generate_unit( f = fopen(unit, "wxe"); if (!f) - return log_error_errno(errno, "Failed to create unit file %s: %m", unit); + return log_error_errno(errno, "%s: failed to create unit file %s: %m", service->path, unit); fprintf(f, "# Automatically generated by systemd-xdg-autostart-generator\n\n" @@ -635,19 +626,18 @@ int xdg_autostart_service_generate_unit( e_not_show_in); } - r = xdg_autostart_generate_desktop_condition(f, + r = xdg_autostart_generate_desktop_condition(service, f, "gnome-systemd-autostart-condition", service->autostart_condition); if (r < 0) return r; - r = xdg_autostart_generate_desktop_condition(f, + r = xdg_autostart_generate_desktop_condition(service, f, "kde-systemd-start-condition", service->kde_autostart_condition); if (r < 0) return r; - (void) generator_add_symlink(dest, "xdg-desktop-autostart.target", "wants", service->name); - - return 0; + log_debug("%s: symlinking %s in xdg-desktop-autostart.target/.wants…", service->path, service->name); + return generator_add_symlink(dest, "xdg-desktop-autostart.target", "wants", service->name); } diff --git a/src/xdg-autostart-generator/xdg-autostart-service.h b/src/xdg-autostart-generator/xdg-autostart-service.h index 264171889..61a4a7304 100644 --- a/src/xdg-autostart-generator/xdg-autostart-service.h +++ b/src/xdg-autostart-generator/xdg-autostart-service.h @@ -26,7 +26,6 @@ typedef struct XdgAutostartService { } XdgAutostartService; - XdgAutostartService * xdg_autostart_service_free(XdgAutostartService *s); DEFINE_TRIVIAL_CLEANUP_FUNC(XdgAutostartService*, xdg_autostart_service_free); @@ -34,4 +33,4 @@ char *xdg_autostart_service_translate_name(const char *name); int xdg_autostart_format_exec_start(const char *exec, char **ret_exec_start); XdgAutostartService *xdg_autostart_service_parse_desktop(const char *path); -int xdg_autostart_service_generate_unit(XdgAutostartService *service, const char *dest); +int xdg_autostart_service_generate_unit(const XdgAutostartService *service, const char *dest); diff --git a/test/README.testsuite b/test/README.testsuite index 2f10c7370..24b98f78c 100644 --- a/test/README.testsuite +++ b/test/README.testsuite @@ -163,3 +163,102 @@ places: For infrastructure help, reaching out to Canonical via the #ubuntu-devel channel on libera.chat is an effective way to receive support in general. + +Manually running a part of the Ubuntu CI test suite +=================================================== + +In some situations one may want/need to run one of the tests run by Ubuntu CI +locally for debugging purposes. For this, you need a machine (or a VM) with +the same Ubuntu release as is used by Ubuntu CI (Focal ATTOW). + +First of all, clone the Debian systemd repository and sync it with the code of +the PR (set by the $UPSTREAM_PULL_REQUEST env variable) you'd like to debug: + +# git clone https://salsa.debian.org/systemd-team/systemd.git +# cd systemd +# git checkout upstream-ci +# TEST_UPSTREAM=1 UPSTREAM_PULL_REQUEST=12345 ./debian/extra/checkout-upstream + +Now install necessary build & test dependencies: + +## PPA with some newer Ubuntu packages required by upstream systemd +# add-apt-repository -y ppa:upstream-systemd-ci/systemd-ci +# apt build-dep -y systemd +# apt install -y autopkgtest debhelper genisoimage git qemu-system-x86 \ + libzstd-dev libfdisk-dev libtss2-dev libfido2-dev libssl-dev \ + python3-jinja2 zstd + +Build systemd deb packages with debug info: + +# DEB_BUILD_OPTIONS="nocheck nostrip" dpkg-buildpackage -us -uc +# cd .. + +Prepare a testbed image for autopkgtest (tweak the release as necessary): + +# autopkgtest-buildvm-ubuntu-cloud -v -a amd64 -r focal + +And finally run the autopkgtest itself: + +# autopkgtest -o logs *.deb systemd/ \ + --timeout-factor=3 \ + --test-name=boot-and-services \ + --shell-fail \ + -- autopkgtest-virt-qemu autopkgtest-focal-amd64.img + +where --test-name= is the name of the test you want to run/debug. The +--shell-fail option will pause the execution in case the test fails and shows +you the information how to connect to the testbed for further debugging. + +Manually running LGTM/CodeQL analysis +===================================== + +This is mostly useful for debugging various CodeQL/LGTM quirks. + +Download the CodeQL Bundle from https://github.com/github/codeql-action/releases +and unpack it somewhere. From now the 'tutorial' assumes you have the `codeql` +binary from the unpacked archive in $PATH for brevity. + +Switch to the systemd repository if not already: + +$ cd + +Create an initial CodeQL database: + +$ CCACHE_DISABLE=1 codeql database create codeqldb --language=cpp -vvv + +Disabling ccache is important, otherwise you might see CodeQL complaining: + +No source code was seen and extracted to /home/mrc0mmand/repos/@ci-incubator/systemd/codeqldb. +This can occur if the specified build commands failed to compile or process any code. + - Confirm that there is some source code for the specified language in the project. + - For codebases written in Go, JavaScript, TypeScript, and Python, do not specify + an explicit --command. + - For other languages, the --command must specify a "clean" build which compiles + all the source code files without reusing existing build artefacts. + +If you want to run all queries systemd uses in LGTM/CodeQL, run: + +$ codeql database analyze codeqldb/ --format csv --output results.csv .github/codeql-custom.qls .lgtm/cpp-queries/*.ql -vvv + +Note: this will take a while. + +If you're interested in a specific check, the easiest way (without hunting down +the specific CodeQL query file) is to create a custom query suite. For example: + +$ cat >test.qls </dev/null 2>&1 || exit 0 + +test_append_files() { + ( + instmods overlay =overlayfs + generate_module_dependencies + inst_binary unsquashfs + install_verity_minimal + ) +} do_test "$@" diff --git a/test/TEST-56-EXIT-TYPE/test.sh b/test/TEST-56-EXIT-TYPE/test.sh index 0f84dca1b..37475e817 100755 --- a/test/TEST-56-EXIT-TYPE/test.sh +++ b/test/TEST-56-EXIT-TYPE/test.sh @@ -6,4 +6,9 @@ TEST_DESCRIPTION="test ExitType=cgroup" # shellcheck source=test/test-functions . "${TEST_BASE_DIR:?}/test-functions" +if [[ "$(get_cgroup_hierarchy)" != unified ]]; then + echo "This test requires unified cgroup hierarchy, skipping..." + exit 0 +fi + do_test "$@" diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh index 0f26eaafe..04632952d 100755 --- a/test/TEST-64-UDEV-STORAGE/test.sh +++ b/test/TEST-64-UDEV-STORAGE/test.sh @@ -166,6 +166,7 @@ testcase_megasas2_basic() { return 77 fi + local i local qemu_opts=( "-device megasas-gen2,id=scsi0" "-device megasas-gen2,id=scsi1" @@ -192,6 +193,9 @@ testcase_nvme_basic() { return 77 fi + local i + local qemu_opts=() + for i in {0..27}; do qemu_opts+=( "-device nvme,drive=nvme$i,serial=deadbeef$i,num_queues=8" @@ -215,7 +219,7 @@ testcase_virtio_scsi_identically_named_partitions() { # and attach them to a virtio-scsi controller local qemu_opts=("-device virtio-scsi-pci,id=scsi0,num_queues=4") local diskpath="${TESTDIR:?}/namedpart0.img" - local lodev qemu_timeout + local i lodev qemu_timeout dd if=/dev/zero of="$diskpath" bs=1M count=18 lodev="$(losetup --show -f -P "$diskpath")" @@ -325,7 +329,7 @@ testcase_lvm_basic() { fi local qemu_opts=("-device ahci,id=ahci0") - local diskpath + local diskpath i # Attach 4 SATA disks to the VM (and set their model and serial fields # to something predictable, so we can refer to them later) diff --git a/test/TEST-68-PROPAGATE-EXIT-STATUS/Makefile b/test/TEST-68-PROPAGATE-EXIT-STATUS/Makefile new file mode 120000 index 000000000..e9f93b110 --- /dev/null +++ b/test/TEST-68-PROPAGATE-EXIT-STATUS/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-68-PROPAGATE-EXIT-STATUS/test.sh b/test/TEST-68-PROPAGATE-EXIT-STATUS/test.sh new file mode 100755 index 000000000..eecd56959 --- /dev/null +++ b/test/TEST-68-PROPAGATE-EXIT-STATUS/test.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -e + +TEST_DESCRIPTION="Test propagation of exit status to On{Failure,Success}= dependencies" +TEST_NO_QEMU=1 + +# shellcheck source=test/test-functions +. "$TEST_BASE_DIR/test-functions" + +do_test "$@" diff --git a/test/TEST-69-SHUTDOWN/Makefile b/test/TEST-69-SHUTDOWN/Makefile new file mode 120000 index 000000000..e9f93b110 --- /dev/null +++ b/test/TEST-69-SHUTDOWN/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-69-SHUTDOWN/test.sh b/test/TEST-69-SHUTDOWN/test.sh new file mode 100755 index 000000000..c7f78fe3c --- /dev/null +++ b/test/TEST-69-SHUTDOWN/test.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -e + +TEST_DESCRIPTION="shutdown testing" +IMAGE_NAME="shutdown" +TEST_NO_QEMU=1 + +# shellcheck source=test/test-functions +. "${TEST_BASE_DIR:?}/test-functions" + +_ORIG_NSPAWN="${SYSTEMD_NSPAWN:?}" +SYSTEMD_NSPAWN="${STATEDIR:?}/run-nspawn" + +setup_nspawn_root_hook() { + cat >"${STATEDIR:?}/run-nspawn" <"$workspace/etc/systemd/system/end.service.d/99-override.conf" <>"$workspace/root/.bashrc" + echo 'startup_message off' >"$workspace/etc/screenrc" + echo 'bell_msg ""' >>"$workspace/etc/screenrc" +} + +do_test "$@" diff --git a/test/TEST-70-TPM2/Makefile b/test/TEST-70-TPM2/Makefile new file mode 100644 index 000000000..9f65d4ca4 --- /dev/null +++ b/test/TEST-70-TPM2/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +all setup run clean clean-again: + @TEST_BASE_DIR=../ ./test.sh --$@ + +.PHONY: all setup run clean clean-again diff --git a/test/TEST-70-TPM2/test.sh b/test/TEST-70-TPM2/test.sh new file mode 100755 index 000000000..d716614bc --- /dev/null +++ b/test/TEST-70-TPM2/test.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -e + +TEST_DESCRIPTION="cryptenroll/cryptsetup with TPM2 devices" +IMAGE_NAME="tpm2" +TEST_NO_NSPAWN=1 +TEST_REQUIRE_INSTALL_TESTS=0 + +# shellcheck source=test/test-functions +. "${TEST_BASE_DIR:?}/test-functions" + +command -v swtpm >/dev/null 2>&1 || exit 0 +command -v tpm2_pcrextend >/dev/null 2>&1 || exit 0 + +test_append_files() { + ( + local workspace="${1:?}" + + instmods tpm tpm_tis tpm_ibmvtpm + install_dmevent + generate_module_dependencies + inst_binary tpm2_pcrextend + ) +} + +machine="$(uname -m)" +tpmdevice="tpm-tis" +if [ "$machine" = "ppc64le" ]; then + # tpm-spapr support was introduced in qemu 5.0.0. Skip test for old qemu versions. + qemu_min_version "5.0.0" || exit 0 + tpmdevice="tpm-spapr" +fi + +tpmstate=$(mktemp -d) +swtpm socket --tpm2 --tpmstate dir="$tpmstate" --ctrl type=unixio,path="$tpmstate/sock" & +trap 'kill %%; rm -rf $tpmstate' SIGINT EXIT +QEMU_OPTIONS="-chardev socket,id=chrtpm,path=$tpmstate/sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device $tpmdevice,tpmdev=tpm0" + +do_test "$@" diff --git a/test/TEST-72-SYSUPDATE/Makefile b/test/TEST-72-SYSUPDATE/Makefile new file mode 120000 index 000000000..e9f93b110 --- /dev/null +++ b/test/TEST-72-SYSUPDATE/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-72-SYSUPDATE/test.sh b/test/TEST-72-SYSUPDATE/test.sh new file mode 100755 index 000000000..471b02e9a --- /dev/null +++ b/test/TEST-72-SYSUPDATE/test.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -e + +TEST_DESCRIPTION="test sysupdate" + +# shellcheck source=test/test-functions +. "${TEST_BASE_DIR:?}/test-functions" + +test_append_files() { + inst_binary sha256sum +} + +do_test "$@" diff --git a/test/fuzz/fuzz-catalog/clusterfuzz-testcase-minimized-fuzz-catalog-5674475278827520 b/test/fuzz/fuzz-catalog/language-too-short similarity index 100% rename from test/fuzz/fuzz-catalog/clusterfuzz-testcase-minimized-fuzz-catalog-5674475278827520 rename to test/fuzz/fuzz-catalog/language-too-short diff --git a/test/fuzz/fuzz-dhcp-client/parse-memleak b/test/fuzz/fuzz-dhcp-client/parse-memleak new file mode 100644 index 000000000..87345bf0e Binary files /dev/null and b/test/fuzz/fuzz-dhcp-client/parse-memleak differ diff --git a/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980 b/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980 new file mode 100644 index 000000000..a51cf70dd Binary files /dev/null and b/test/fuzz/fuzz-dhcp-client/timeout-ed34161922c7075c4773f2ada3dee8685d220980 differ diff --git a/test/fuzz/fuzz-dhcp-server-relay/7d924e16295cd14e12a01a5631ea94a3d11d1b52 b/test/fuzz/fuzz-dhcp-server-relay/7d924e16295cd14e12a01a5631ea94a3d11d1b52 new file mode 100644 index 000000000..117fbe0b2 Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server-relay/7d924e16295cd14e12a01a5631ea94a3d11d1b52 differ diff --git a/test/fuzz/fuzz-dhcp-server-relay/fe4344e65d495388540dc1bf8eae70c46f8b867c b/test/fuzz/fuzz-dhcp-server-relay/fe4344e65d495388540dc1bf8eae70c46f8b867c new file mode 100644 index 000000000..0d2b0c891 Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server-relay/fe4344e65d495388540dc1bf8eae70c46f8b867c differ diff --git a/test/fuzz/fuzz-dhcp-server-relay/too-large-packet b/test/fuzz/fuzz-dhcp-server-relay/too-large-packet new file mode 100644 index 000000000..e902b6989 Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server-relay/too-large-packet differ diff --git a/test/fuzz/fuzz-dhcp-server/crash-64c861ad981838e509920e8538640ef46feaae66 b/test/fuzz/fuzz-dhcp-server/crash-64c861ad981838e509920e8538640ef46feaae66 new file mode 100644 index 000000000..a61ee0bc9 Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server/crash-64c861ad981838e509920e8538640ef46feaae66 differ diff --git a/test/fuzz/fuzz-dhcp-server/crash-e13ccbc34d0fa5add0ee1eb69ed630dd2cdfb1ca b/test/fuzz/fuzz-dhcp-server/crash-e13ccbc34d0fa5add0ee1eb69ed630dd2cdfb1ca new file mode 100644 index 000000000..1bd216c8c Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server/crash-e13ccbc34d0fa5add0ee1eb69ed630dd2cdfb1ca differ diff --git a/test/fuzz/fuzz-dhcp-server/duplicate-input-data b/test/fuzz/fuzz-dhcp-server/duplicate-input-data new file mode 100644 index 000000000..3d1370599 Binary files /dev/null and b/test/fuzz/fuzz-dhcp-server/duplicate-input-data differ diff --git a/test/fuzz/fuzz-dhcp6-client/12ad30d317800d7f731c1c8bc0854e531d5ef928 b/test/fuzz/fuzz-dhcp6-client/12ad30d317800d7f731c1c8bc0854e531d5ef928 new file mode 100644 index 000000000..c140fc021 Binary files /dev/null and b/test/fuzz/fuzz-dhcp6-client/12ad30d317800d7f731c1c8bc0854e531d5ef928 differ diff --git a/test/fuzz/fuzz-dhcp6-client/crash-a93b8ba024ada36014c29c25cc90c668fd91ce7f b/test/fuzz/fuzz-dhcp6-client/crash-a93b8ba024ada36014c29c25cc90c668fd91ce7f new file mode 100644 index 000000000..2bf4027c0 Binary files /dev/null and b/test/fuzz/fuzz-dhcp6-client/crash-a93b8ba024ada36014c29c25cc90c668fd91ce7f differ diff --git a/test/fuzz/fuzz-dhcp6-client/f202c4dff34d15e41c032a66ed25d89154be1f6d b/test/fuzz/fuzz-dhcp6-client/f202c4dff34d15e41c032a66ed25d89154be1f6d new file mode 100644 index 000000000..9d8994dec Binary files /dev/null and b/test/fuzz/fuzz-dhcp6-client/f202c4dff34d15e41c032a66ed25d89154be1f6d differ diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link index 7586e35b4..d6c6cc6f2 100644 --- a/test/fuzz/fuzz-link-parser/directives.link +++ b/test/fuzz/fuzz-link-parser/directives.link @@ -5,12 +5,14 @@ OriginalName= Path= Driver= Type= +Kind= Property= Host= Virtualization= KernelCommandLine= KernelVersion= Architecture= +Firmware= [Link] Description= MACAddressPolicy= @@ -80,3 +82,15 @@ RxMaxCoalescedHighFrames= TxCoalesceHighSec= TxMaxCoalescedHighFrames= CoalescePacketRateSampleIntervalSec= +MDI= +SR-IOVVirtualFunctions= +[SR-IOV] +VirtualFunction= +MACSpoofCheck= +VLANId= +VLANProtocol= +QualityOfService= +QueryReceiveSideScaling= +Trust= +LinkState= +MACAddress= diff --git a/test/fuzz/fuzz-netdev-parser/directives.netdev b/test/fuzz/fuzz-netdev-parser/directives.netdev index f5fa2418f..d6c2e1846 100644 --- a/test/fuzz/fuzz-netdev-parser/directives.netdev +++ b/test/fuzz/fuzz-netdev-parser/directives.netdev @@ -95,6 +95,7 @@ IPv6RapidDeploymentPrefix= ERSPANIndex= SerializeTunneledPackets= ISATAP= +External= [VXLAN] UDP6ZeroChecksumRx= ARPProxy= @@ -245,3 +246,7 @@ RoutingAlgorithm= PartitionKey= Mode= IgnoreUserspaceMulticastGroups= +[WLAN] +PhysicalDevice= +Type= +WDS= diff --git a/test/fuzz/fuzz-network-parser/26-bridge-slave-interface-1.network b/test/fuzz/fuzz-network-parser/26-bridge-slave-interface-1.network index 81b372fb6..854ac5f44 100644 --- a/test/fuzz/fuzz-network-parser/26-bridge-slave-interface-1.network +++ b/test/fuzz/fuzz-network-parser/26-bridge-slave-interface-1.network @@ -7,6 +7,7 @@ Bridge=bridge99 [Bridge] Cost=400 HairPin = true +Isolated = true FastLeave = true UnicastFlood = true MulticastToUnicast = true diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 48f9ad6fb..276f3c930 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -2,6 +2,7 @@ Cost= UseBPDU= HairPin= +Isolated= UnicastFlood= FastLeave= Priority= @@ -16,6 +17,7 @@ MulticastRouter= [Match] KernelVersion= Type= +Kind= Driver= Architecture= Firmware= @@ -407,6 +409,9 @@ RelayAgentCircuitId= RelayAgentRemoteId= ServerAddress= UplinkInterface= +BootServerAddress= +BootServerName= +BootFilename= [DHCPServerStaticLease] MACAddress= Address= diff --git a/test/fuzz/fuzz-unit-file/directives-all.service b/test/fuzz/fuzz-unit-file/directives-all.service index 78ddaf5ec..487001b6b 100644 --- a/test/fuzz/fuzz-unit-file/directives-all.service +++ b/test/fuzz/fuzz-unit-file/directives-all.service @@ -217,6 +217,7 @@ RootImage= RootHash= RootHashSignature= RootVerity= +ExtensionDirectories= ExtensionImages= RuntimeMaxSec= SELinuxContextFromNet= @@ -385,6 +386,7 @@ AllowPortToBeRoot= AllowedIPs= Anonymize= Architecture= +Firmware= AutoJoin= AutoNegotiation= BindCarrier= @@ -450,6 +452,7 @@ Group= GroupForwardMask= GroupPolicyExtension= HairPin= +Isolated= MulticastToUnicast= HelloTimeSec= HomeAddress= @@ -734,6 +737,8 @@ LogLevel= LogLocation= LogTarget= RuntimeWatchdogSec= +RuntimeWatchdogPreSec= +RuntimeWatchdogPreGovernor= ShowStatus= RebootWatchdogSec= ShutdownWatchdogSec= diff --git a/test/fuzz/fuzz-unit-file/directives.mount b/test/fuzz/fuzz-unit-file/directives.mount index 67421444c..0a44328e5 100644 --- a/test/fuzz/fuzz-unit-file/directives.mount +++ b/test/fuzz/fuzz-unit-file/directives.mount @@ -40,6 +40,7 @@ DynamicUser= Environment= EnvironmentFile= ExecPaths= +ExtensionDirectories= ExtensionImages= FinalKillSignal= ForceUnmount= diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service index ca9959538..6be65062d 100644 --- a/test/fuzz/fuzz-unit-file/directives.service +++ b/test/fuzz/fuzz-unit-file/directives.service @@ -168,6 +168,7 @@ ExecStartPre= ExecStop= ExecStopPost= ExitType= +ExtensionDirectories= ExtensionImages= FailureAction= FileDescriptorStoreMax= diff --git a/test/fuzz/fuzz-unit-file/directives.socket b/test/fuzz/fuzz-unit-file/directives.socket index 865fd83ad..90358fc11 100644 --- a/test/fuzz/fuzz-unit-file/directives.socket +++ b/test/fuzz/fuzz-unit-file/directives.socket @@ -50,6 +50,7 @@ ExecStartPost= ExecStartPre= ExecStopPost= ExecStopPre= +ExtensionDirectories= ExtensionImages= FileDescriptorName= FinalKillSignal= diff --git a/test/fuzz/fuzz-unit-file/directives.swap b/test/fuzz/fuzz-unit-file/directives.swap index f538ba8b6..5d057fa63 100644 --- a/test/fuzz/fuzz-unit-file/directives.swap +++ b/test/fuzz/fuzz-unit-file/directives.swap @@ -39,6 +39,7 @@ DynamicUser= Environment= EnvironmentFile= ExecPaths= +ExtensionDirectories= ExtensionImages= FinalKillSignal= Group= diff --git a/test/fuzz/meson.build b/test/fuzz/meson.build index 30e26b09c..80362d415 100644 --- a/test/fuzz/meson.build +++ b/test/fuzz/meson.build @@ -7,18 +7,24 @@ sanitize_address_undefined = custom_target( project_source_root, '@OUTPUT@', 'fuzzers', - '-Dfuzz-tests=true -Db_lundef=false -Db_sanitize=address,undefined --optimization=@0@'.format(get_option('optimization')), + '-Dfuzz-tests=true -Db_lundef=false -Db_sanitize=address,undefined --optimization=@0@ @1@ -Dc_args=@2@ -Dcpp_args=@2@ -Dskip-deps=@3@'.format( + get_option('optimization'), + get_option('werror') ? '--werror' : '', + '-DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION', + get_option('skip-deps') + ), ' '.join(cc.cmd_array()), cxx_cmd]) sanitizers = [['address,undefined', sanitize_address_undefined]] -if git.found() +if git.found() and fs.exists(project_source_root / '.git') out = run_command(env, '-u', 'GIT_WORK_TREE', git, '--git-dir=@0@/.git'.format(project_source_root), - 'ls-files', ':/test/fuzz/*/*') + 'ls-files', ':/test/fuzz/*/*', + check: true) else - out = run_command(sh, '-c', 'ls @0@/test/fuzz/*/*'.format(project_source_root)) + out = run_command(sh, '-c', 'cd "@0@"; echo test/fuzz/*/*'.format(project_source_root), check: true) endif fuzz_regression_tests = [] diff --git a/test/meson.build b/test/meson.build index 8de1043e1..34f78d5c6 100644 --- a/test/meson.build +++ b/test/meson.build @@ -88,6 +88,7 @@ endif test_fstab_generator_sh = find_program('test-fstab-generator.sh') test_network_generator_conversion_sh = find_program('test-network-generator-conversion.sh') +test_systemctl_enable_sh = find_program('test-systemctl-enable.sh') test_systemd_tmpfiles_py = find_program('test-systemd-tmpfiles.py') hwdb_test_sh = find_program('hwdb-test.sh') @@ -116,16 +117,6 @@ endif ############################################################ -if conf.get('HAVE_SYSV_COMPAT') == 1 - sysv_generator_test_py = find_program('sysv-generator-test.py') - if want_tests != 'false' - test('sysv-generator-test', - sysv_generator_test_py) - endif -endif - -############################################################ - if install_tests install_data('run-unit-tests.py', install_mode : 'rwxr-xr-x', @@ -163,17 +154,35 @@ endif ############################################################ +rpm = find_program('rpm', required : false) +rpmspec = find_program('rpmspec', required : false) +test_rpm_macros = find_program('test-rpm-macros.sh') + +if rpm.found() and rpmspec.found() + if want_tests != 'false' + test('test-rpm-macros', + test_rpm_macros, + args : [project_build_root]) + endif +else + message('Skipping test-rpm-macros since rpm and/or rpmspec are not available') +endif + +############################################################ + if want_tests != 'false' and dmi_arches.contains(host_machine.cpu_family()) udev_dmi_memory_id_test = find_program('udev-dmi-memory-id-test.sh') - if git.found() + if git.found() and fs.exists(project_source_root / '.git') out = run_command( env, '-u', 'GIT_WORK_TREE', git, '--git-dir=@0@/.git'.format(project_source_root), - 'ls-files', ':/test/dmidecode-dumps/*.bin') + 'ls-files', ':/test/dmidecode-dumps/*.bin', + check: true) else out = run_command( - sh, '-c', 'ls @0@/test/dmidecode-dumps/*.bin'.format(project_source_root)) + sh, '-c', 'cd "@0@"; echo test/dmidecode-dumps/*.bin'.format(project_source_root), + check: true) endif foreach p : out.stdout().split() @@ -182,9 +191,10 @@ if want_tests != 'false' and dmi_arches.contains(host_machine.cpu_family()) test(name, udev_dmi_memory_id_test, - args : [udev_prog_paths['dmi_memory_id'], + args : [udev_prog_paths['dmi_memory_id'].full_path(), source, - source + '.txt']) + source + '.txt'], + depends : udev_prog_paths['dmi_memory_id']) endforeach endif diff --git a/test/networkd-test.py b/test/networkd-test.py index 60622077a..7faa12ef7 100755 --- a/test/networkd-test.py +++ b/test/networkd-test.py @@ -138,7 +138,7 @@ class NetworkdTestingUtilities: def read_attr(self, link, attribute): """Read a link attributed from the sysfs.""" - # Note we we don't want to check if interface `link' is managed, we + # Note we don't want to check if interface `link' is managed, we # want to evaluate link variable and pass the value of the link to # assert_link_states e.g. eth0=managed. self.assert_link_states(**{link:'managed'}) @@ -273,6 +273,7 @@ Priority=0 [Bridge] UnicastFlood=true HairPin=true +Isolated=true UseBPDU=true FastLeave=true AllowPortToBeRoot=true @@ -286,6 +287,7 @@ Priority=23 self.assertEqual(self.read_attr('port2', 'brport/priority'), '23') self.assertEqual(self.read_attr('port2', 'brport/hairpin_mode'), '1') + self.assertEqual(self.read_attr('port2', 'brport/isolated'), '1') self.assertEqual(self.read_attr('port2', 'brport/path_cost'), '555') self.assertEqual(self.read_attr('port2', 'brport/multicast_fast_leave'), '1') self.assertEqual(self.read_attr('port2', 'brport/unicast_flood'), '1') diff --git a/test/splash.bmp b/test/splash.bmp deleted file mode 100644 index 27247f7a2..000000000 Binary files a/test/splash.bmp and /dev/null differ diff --git a/test/test-efi-create-disk.sh b/test/test-efi-create-disk.sh deleted file mode 100755 index 46062e46e..000000000 --- a/test/test-efi-create-disk.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: LGPL-2.1-or-later -set -eo pipefail - -out="${1:?}" -systemd_efi="${2:?}" -boot_stub="${3:?}" -splash_bmp="${4:?}" - -efi_dir="$(bootctl -p)" -entry_dir="${efi_dir}/$(cat /etc/machine-id)/$(uname -r)" - -# create GPT table with EFI System Partition -rm -f "$out" -dd if=/dev/null of="$out" bs=1M seek=512 count=1 status=none -parted --script "$out" "mklabel gpt" "mkpart ESP fat32 1MiB 511MiB" "set 1 boot on" - -# create FAT32 file system -LOOP="$(losetup --show -f -P "$out")" -mkfs.vfat -F32 "${LOOP}p1" -mkdir -p mnt -mount "${LOOP}p1" mnt - -mkdir -p mnt/EFI/{BOOT,systemd} -cp "$systemd_efi" mnt/EFI/BOOT/BOOTX64.efi - -if [ -e /boot/shellx64.efi ]; then - cp /boot/shellx64.efi mnt/ -fi - -mkdir mnt/EFI/Linux -echo -n "foo=yes bar=no root=/dev/fakeroot debug rd.break=initqueue" >mnt/cmdline.txt -objcopy \ - --add-section .osrel=/etc/os-release --change-section-vma .osrel=0x20000 \ - --add-section .cmdline=mnt/cmdline.txt --change-section-vma .cmdline=0x30000 \ - --add-section .splash="$splash_bmp" --change-section-vma .splash=0x40000 \ - --add-section .linux="${entry_dir}/linux" --change-section-vma .linux=0x2000000 \ - --add-section .initrd="${entry_dir}/initrd" --change-section-vma .initrd=0x3000000 \ - "$boot_stub" mnt/EFI/Linux/linux-test.efi - -# install entries -mkdir -p mnt/loader/entries -echo -e "timeout 3\n" > mnt/loader/loader.conf -echo -e "title Test\nefi /test\n" > mnt/loader/entries/test.conf -echo -e "title Test2\nlinux /test2\noptions option=yes word number=1000 more\n" > mnt/loader/entries/test2.conf -echo -e "title Test3\nlinux /test3\n" > mnt/loader/entries/test3.conf -echo -e "title Test4\nlinux /test4\n" > mnt/loader/entries/test4.conf -echo -e "title Test5\nefi /test5\n" > mnt/loader/entries/test5.conf -echo -e "title Test6\nlinux /test6\n" > mnt/loader/entries/test6.conf - -sync -umount mnt -rmdir mnt -losetup -d "$LOOP" diff --git a/test/test-execute/exec-specifier-credentials-dir.service b/test/test-execute/exec-specifier-credentials-dir.service new file mode 100644 index 000000000..818619aca --- /dev/null +++ b/test/test-execute/exec-specifier-credentials-dir.service @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=Test for specifiers + +[Service] +Type=oneshot +Environment=TOP_SECRET=%d/very_top_secret +# Test if the specifier is resolved correctly both before and after LoadCredential= +ExecStart=test %d/very_top_secret = "${CREDENTIALS_DIRECTORY}/very_top_secret" +LoadCredential=very_top_secret +ExecStart=test %d/very_top_secret = "${CREDENTIALS_DIRECTORY}/very_top_secret" +ExecStart=sh -c 'test %d/very_top_secret = "$TOP_SECRET"' diff --git a/test/test-execute/exec-specifier.service b/test/test-execute/exec-specifier.service index 190ebe93e..ae8b2428b 100644 --- a/test/test-execute/exec-specifier.service +++ b/test/test-execute/exec-specifier.service @@ -20,6 +20,7 @@ ExecStart=test %L = /var/log ExecStart=test %E = /etc ExecStart=test %T = /tmp ExecStart=test %V = /var/tmp +ExecStart=test %d = %t/credentials/%n ExecStart=sh -c 'test %u = $$(id -un)' ExecStart=sh -c 'test %U = $$(id -u)' ExecStart=sh -c 'test %g = $$(id -gn)' diff --git a/test/test-functions b/test/test-functions index 8bff5d827..c734a15ca 100644 --- a/test/test-functions +++ b/test/test-functions @@ -141,6 +141,7 @@ BASICTOOLS=( echo env false + flock getconf getent getfacl @@ -1070,6 +1071,9 @@ install_compiled_systemd() { if get_bool "$IS_BUILT_WITH_COVERAGE"; then mkdir -p "${initdir}/${BUILD_DIR:?}/" rsync -am --include='*/' --include='*.gcno' --exclude='*' "${BUILD_DIR:?}/" "${initdir}/${BUILD_DIR:?}/" + # Set effective & default ACLs for the build dir so unprivileged + # processes can write gcda files with coverage stats + setfacl -R -m 'd:o:rwX' -m 'o:rwX' "${initdir}/${BUILD_DIR:?}/" fi } @@ -1154,17 +1158,38 @@ install_systemd() { # enable debug logging in PID1 echo LogLevel=debug >>"$initdir/etc/systemd/system.conf" + if [[ -n "$TEST_SYSTEMD_LOG_LEVEL" ]]; then + echo DefaultEnvironment=SYSTEMD_LOG_LEVEL="$TEST_SYSTEMD_LOG_LEVEL" >>"$initdir/etc/systemd/system.conf" + fi # store coredumps in journal echo Storage=journal >>"$initdir/etc/systemd/coredump.conf" # Propagate SYSTEMD_UNIT_PATH to user systemd managers mkdir "$initdir/etc/systemd/system/user@.service.d/" echo -e "[Service]\nPassEnvironment=SYSTEMD_UNIT_PATH\n" >"$initdir/etc/systemd/system/user@.service.d/override.conf" - # When built with gcov, disable ProtectSystem= and ProtectHome in the test - # images, since it prevents gcov to write the coverage reports (*.gcda files) + # When built with gcov, disable ProtectSystem= and ProtectHome= in the test + # images, since it prevents gcov to write the coverage reports (*.gcda + # files) if get_bool "$IS_BUILT_WITH_COVERAGE"; then mkdir -p "$initdir/etc/systemd/system/service.d/" - echo -e "[Service]\nProtectSystem=no\nProtectHome=no\n" >"$initdir/etc/systemd/system/service.d/gcov-override.conf" + echo -e "[Service]\nProtectSystem=no\nProtectHome=no\n" >"$initdir/etc/systemd/system/service.d/99-gcov-override.conf" + # Similarly, set ReadWritePaths= to the $BUILD_DIR in the test image + # to make the coverage work with units utilizing DynamicUser=yes. Do + # this only for services from TEST-20, as setting this system-wide + # has many undesirable side-effects + mkdir -p "$initdir/etc/systemd/system/test20-.service.d/" + echo -e "[Service]\nReadWritePaths=${BUILD_DIR:?}\n" >"$initdir/etc/systemd/system/test20-.service.d/99-gcov-rwpaths-override.conf" + fi + + # If we're built with -Dportabled=false, tests with systemd-analyze + # --profile will fail. Since we need just the profile (text) files, let's + # copy them into the image if they don't exist there. + local portable_dir="${initdir:?}${ROOTLIBDIR:?}/portable" + if [[ ! -d "$portable_dir/profile/strict" ]]; then + dinfo "Couldn't find portable profiles in the test image" + dinfo "Copying them directly from the source tree" + mkdir -p "$portable_dir" + cp -frv "${SOURCE_DIR:?}/src/portable/profile" "$portable_dir" fi } @@ -1189,7 +1214,7 @@ install_missing_libraries() { local lib path # A number of dependencies is now optional via dlopen, so the install # script will not pick them up, since it looks at linkage. - for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu libfido2 libbpf libelf libdw; do + for lib in libcryptsetup libidn libidn2 pwquality libqrencode tss2-esys tss2-rc tss2-mu tss2-tcti-device libfido2 libbpf libelf libdw; do ddebug "Searching for $lib via pkg-config" if pkg-config --exists "$lib"; then path="$(pkg-config --variable=libdir "$lib")" @@ -1253,7 +1278,7 @@ create_empty_image() { LOOPDEV=$(losetup --show -P -f "$IMAGE_PUBLIC") [ -b "$LOOPDEV" ] || return 1 sfdisk "$LOOPDEV" < 0x03 (No address assignment) - veth97: 0x08 - veth98: 0x09 - veth99: 0x10 - ''' + # Link Subnet IDs + # test1: 0x00 + # dummy97: 0x01 (The link will appear later) + # dummy98: 0x00 + # dummy99: auto -> 0x02 (No address assignment) + # veth97: 0x08 + # veth98: 0x09 + # veth99: 0x10 print('### ip -6 address show dev veth99 scope global') output = check_output('ip -6 address show dev veth99 scope global') @@ -5108,15 +5322,15 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): output = check_output('ip -6 address show dev dummy98 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # address in IA_PD (temporary) - self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]02:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') print('### ip -6 address show dev dummy99 scope global') output = check_output('ip -6 address show dev dummy99 scope global') print(output) # Assign=no - self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]03') + self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02') print('### ip -6 address show dev veth97 scope global') output = check_output('ip -6 address show dev veth97 scope global') @@ -5176,12 +5390,12 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): print('### ip -6 route show dev dummy98') output = check_output('ip -6 route show dev dummy98') print(output) - self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto kernel metric [0-9]* expires') + self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires') print('### ip -6 route show dev dummy99') output = check_output('ip -6 route show dev dummy99') print(output) - self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]03::/64 proto dhcp metric [0-9]* expires') + self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires') print('### ip -6 route show dev veth97') output = check_output('ip -6 route show dev veth97') @@ -5228,25 +5442,25 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): output = check_output('ip -6 address show dev dummy98 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # address in IA_PD (temporary) - self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]02:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address('dummy98', 'inet6 3ffe:501:ffff:[2-9a-f]00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') print('### ip -6 address show dev dummy99 scope global') output = check_output('ip -6 address show dev dummy99 scope global') print(output) # Assign=no - self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]03') + self.assertNotRegex(output, 'inet6 3ffe:501:ffff:[2-9a-f]02') print('### ip -6 route show dev dummy98') output = check_output('ip -6 route show dev dummy98') print(output) - self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto kernel metric [0-9]* expires') + self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]00::/64 proto kernel metric [0-9]* expires') print('### ip -6 route show dev dummy99') output = check_output('ip -6 route show dev dummy99') print(output) - self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]03::/64 proto dhcp metric [0-9]* expires') + self.assertRegex(output, '3ffe:501:ffff:[2-9a-f]02::/64 proto dhcp metric [0-9]* expires') def verify_dhcp4_6rd(self, tunnel_name): print('### ip -4 address show dev veth-peer scope global') @@ -5254,17 +5468,15 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): print(output) self.assertIn('inet 10.0.0.1/8 brd 10.255.255.255 scope global veth-peer', output) - ''' - Link Subnet IDs - test1: 0x00 - dummy97: 0x01 (The link will appear later) - dummy98: 0x02 - dummy99: auto -> 0x03 (No address assignment) - 6rd-XXX: auto -> 0x0[34] - veth97: 0x08 - veth98: 0x09 - veth99: 0x10 - ''' + # Link Subnet IDs + # test1: 0x00 + # dummy97: 0x01 (The link will appear later) + # dummy98: 0x00 + # dummy99: auto -> 0x0[23] (No address assignment) + # 6rd-XXX: auto -> 0x0[23] + # veth97: 0x08 + # veth98: 0x09 + # veth99: 0x10 print('### ip -4 address show dev veth99 scope global') output = check_output('ip -4 address show dev veth99 scope global') @@ -5294,15 +5506,15 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): output = check_output('ip -6 address show dev dummy98 scope global') print(output) # address in IA_PD (Token=static) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+02:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+00:1a:2b:3c:4d/64 (metric 256 |)scope global dynamic mngtmpaddr') # address in IA_PD (temporary) - self.wait_address('dummy98', 'inet6 2001:db8:6464:[0-9a-f]+02:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') + self.wait_address('dummy98', 'inet6 2001:db8:6464:[0-9a-f]+00:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global temporary dynamic', ipv='-6') print('### ip -6 address show dev dummy99 scope global') output = check_output('ip -6 address show dev dummy99 scope global') print(output) # Assign=no - self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[34]') + self.assertNotRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]') print('### ip -6 address show dev veth97 scope global') output = check_output('ip -6 address show dev veth97 scope global') @@ -5362,12 +5574,12 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): print('### ip -6 route show dev dummy98') output = check_output('ip -6 route show dev dummy98') print(output) - self.assertRegex(output, '2001:db8:6464:[0-9a-f]+02::/64 proto kernel metric [0-9]* expires') + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+00::/64 proto kernel metric [0-9]* expires') print('### ip -6 route show dev dummy99') output = check_output('ip -6 route show dev dummy99') print(output) - self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[34]::/64 proto dhcp metric [0-9]* expires') + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto dhcp metric [0-9]* expires') print('### ip -6 route show dev veth97') output = check_output('ip -6 route show dev veth97') @@ -5414,13 +5626,13 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): print('### ip -6 address show dev {}'.format(tunnel_name)) output = check_output('ip -6 address show dev {}'.format(tunnel_name)) print(output) - self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[34]:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global dynamic') + self.assertRegex(output, 'inet6 2001:db8:6464:[0-9a-f]+0[23]:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*:[0-9a-f]*/64 (metric 256 |)scope global dynamic') self.assertRegex(output, 'inet6 ::10.100.100.[0-9]+/96 scope global') print('### ip -6 route show dev {}'.format(tunnel_name)) output = check_output('ip -6 route show dev {}'.format(tunnel_name)) print(output) - self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[34]::/64 proto kernel metric [0-9]* expires') + self.assertRegex(output, '2001:db8:6464:[0-9a-f]+0[23]::/64 proto kernel metric [0-9]* expires') self.assertRegex(output, '::/96 proto kernel metric [0-9]*') print('### ip -6 route show default') @@ -5430,22 +5642,22 @@ class NetworkdDHCPPDTests(unittest.TestCase, Utilities): self.assertIn('via ::10.0.0.1 dev {}'.format(tunnel_name), output) def test_dhcp4_6rd(self): - copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp4-6rd-server.network', 'dhcp4-6rd-upstream.network', - '25-veth-downstream-veth97.netdev', 'dhcp-pd-downstream-veth97.network', 'dhcp-pd-downstream-veth97-peer.network', - '25-veth-downstream-veth98.netdev', 'dhcp-pd-downstream-veth98.network', 'dhcp-pd-downstream-veth98-peer.network', - '11-dummy.netdev', 'dhcp-pd-downstream-test1.network', - 'dhcp-pd-downstream-dummy97.network', - '12-dummy.netdev', 'dhcp-pd-downstream-dummy98.network', - '13-dummy.netdev', 'dhcp-pd-downstream-dummy99.network', + copy_unit_to_networkd_unit_path('25-veth.netdev', '25-dhcp4-6rd-server.network', '25-dhcp4-6rd-upstream.network', + '25-veth-downstream-veth97.netdev', '25-dhcp-pd-downstream-veth97.network', '25-dhcp-pd-downstream-veth97-peer.network', + '25-veth-downstream-veth98.netdev', '25-dhcp-pd-downstream-veth98.network', '25-dhcp-pd-downstream-veth98-peer.network', + '11-dummy.netdev', '25-dhcp-pd-downstream-test1.network', + '25-dhcp-pd-downstream-dummy97.network', + '12-dummy.netdev', '25-dhcp-pd-downstream-dummy98.network', + '13-dummy.netdev', '25-dhcp-pd-downstream-dummy99.network', '80-6rd-tunnel.network') start_networkd() self.wait_online(['veth-peer:routable']) - ''' - ipv4masklen: 8 - 6rd-prefix: 2001:db8::/32 - br-addresss: 10.0.0.1 - ''' + + # ipv4masklen: 8 + # 6rd-prefix: 2001:db8::/32 + # br-addresss: 10.0.0.1 + start_dnsmasq(additional_options='--dhcp-option=212,08:20:20:01:0d:b8:00:00:00:00:00:00:00:00:00:00:00:00:0a:00:00:01', ipv4_range='10.100.100.100,10.100.100.200', ipv4_router='10.0.0.1', lease_time='2m') self.wait_online(['veth99:routable', 'test1:routable', 'dummy98:routable', 'dummy99:degraded', 'veth97:routable', 'veth97-peer:routable', 'veth98:routable', 'veth98-peer:routable']) @@ -5497,10 +5709,10 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): units = [ '12-dummy.netdev', '25-veth.netdev', - 'ipv6ra-prefix-client-deny-list.network', - 'ipv6ra-prefix-client.network', - 'ipv6ra-prefix.network', - 'ipv6ra-uplink.network', + '25-ipv6ra-prefix-client-deny-list.network', + '25-ipv6ra-prefix-client.network', + '25-ipv6ra-prefix.network', + '25-ipv6ra-uplink.network', ] def setUp(self): @@ -5513,8 +5725,8 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): stop_networkd(show_logs=True) def test_ipv6_route_prefix(self): - copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client.network', 'ipv6ra-prefix.network', - '12-dummy.netdev', 'ipv6ra-uplink.network') + copy_unit_to_networkd_unit_path('25-veth.netdev', '25-ipv6ra-prefix-client.network', '25-ipv6ra-prefix.network', + '12-dummy.netdev', '25-ipv6ra-uplink.network') start_networkd() self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable']) @@ -5553,8 +5765,8 @@ class NetworkdIPv6PrefixTests(unittest.TestCase, Utilities): check_output(*networkctl_cmd, '--json=short', 'status', env=env) def test_ipv6_route_prefix_deny_list(self): - copy_unit_to_networkd_unit_path('25-veth.netdev', 'ipv6ra-prefix-client-deny-list.network', 'ipv6ra-prefix.network', - '12-dummy.netdev', 'ipv6ra-uplink.network') + copy_unit_to_networkd_unit_path('25-veth.netdev', '25-ipv6ra-prefix-client-deny-list.network', '25-ipv6ra-prefix.network', + '12-dummy.netdev', '25-ipv6ra-uplink.network') start_networkd() self.wait_online(['veth99:routable', 'veth-peer:routable', 'dummy98:routable']) @@ -5694,7 +5906,7 @@ if __name__ == '__main__': parser.add_argument('--lsan-options', help='LSAN options', dest='lsan_options') parser.add_argument('--ubsan-options', help='UBSAN options', dest='ubsan_options') parser.add_argument('--with-coverage', help='Loosen certain sandbox restrictions to make gcov happy', dest='with_coverage', type=bool, nargs='?', const=True, default=with_coverage) - ns, args = parser.parse_known_args(namespace=unittest) + ns, unknown_args = parser.parse_known_args(namespace=unittest) if ns.build_dir: if ns.networkd_bin or ns.resolved_bin or ns.udevd_bin or ns.wait_online_bin or ns.networkctl_bin or ns.resolvectl_bin or ns.timedatectl_bin: @@ -5749,6 +5961,5 @@ if __name__ == '__main__': if ubsan_options: env.update({ 'UBSAN_OPTIONS' : ubsan_options }) - sys.argv[1:] = args - unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, - verbosity=3)) + sys.argv[1:] = unknown_args + unittest.main(verbosity=3) diff --git a/test/test-resolve/selfsigned.cert b/test/test-resolve/selfsigned.cert new file mode 100644 index 000000000..456862c22 --- /dev/null +++ b/test/test-resolve/selfsigned.cert @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFmzCCA4OgAwIBAgIUZlvbV3+2YGjHJDTW+u0XL/ypvsowDQYJKoZIhvcNAQEL +BQAwXDELMAkGA1UEBhMCVVMxDzANBgNVBAgMBkRlbmlhbDEUMBIGA1UEBwwLU3By +aW5nZmllbGQxDDAKBgNVBAoMA0RpczEYMBYGA1UEAwwPd3d3LmV4YW1wbGUuY29t +MCAXDTIyMDEyMzE0MjYyMloYDzMwMjEwNTI2MTQyNjIyWjBcMQswCQYDVQQGEwJV +UzEPMA0GA1UECAwGRGVuaWFsMRQwEgYDVQQHDAtTcHJpbmdmaWVsZDEMMAoGA1UE +CgwDRGlzMRgwFgYDVQQDDA93d3cuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQC14826tEEHKICM/AKOKsyBUDaa6Z6KqS927ifb43LJ +fxtg8vW+vX9OGtje2qVAoI1UMSu4yttItSd9cjrnAFLPFvQGC8dFhn436ehLWiKP +AP5KvhIQ3equ5fTicn+Hxdm7C3Um2SEEE347I/vArfzuNE7PIJ57sh7KeBHGCrU3 +6iPl1DkkUilbqJAcgFoepozx1SbPq4h8LsdqJDKg+XUtvtuUS850D5Hb5ErTc8NL +VrA+urSIr+yIX4jAeLXbktNLGuAc3+cJTjwuWdDvP51yS0qpj459dFaRWQE9gu0b +DkRwYLF7Mpel66B8TBkHAWBhSs+oLNnv//Zv945wbUkTK29N2VtSEI4pd/47nTX9 +MwGn4q/ZAjhI7JUN3LcRDsrLdVAUabbK/U+xkL/lOlRRBK/1iuLELkaJlMUiuqZh +q3DvNjqeT5yY8GTU5iXoBcvY0lac3+zYaemTgD5cZfF4gpTflGfc5Gf+he6U3Dol +TT+4JfMrw0YdbqsH4oyEtmLBfMvvp+PQysiOELSFbSAphZOOcy8QSzoRrniNynPd +kM7kIM+0t2XUaz0lKtNuZSo9DnhTMvTLPnnbk5aJt5nPxPprcdqhcJhrHw7gVhBo +EceYJmXGiJJMLYuBNymZ4u7YrBg0e0qO+Fi9a4Kfh/QNMq/6VrpWvycb9LtCLhU+ +qQIDAQABo1MwUTAdBgNVHQ4EFgQU3ugK1HtfPaq90JC5Qf5ekrn4uUcwHwYDVR0j +BBgwFoAU3ugK1HtfPaq90JC5Qf5ekrn4uUcwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAgEATzNvQP+VNLY0qK5b/8j8C5PHB0WKsTHSVNOhITr0Bq67 +AeOq5Mh2qZvJd/QAPTb/Cg60802RLQxp6uhCcfMdxTXkz1mxq6dKEeDAu/zAZzXk +WSpJl/VORnibjvXf2OS6ucb4KPOxfkYiD328CdSYBJapmQbnUmwZph2SO0bpY7K3 +EbTY9fIyabGMrjbXL5EGRvqA0NSnJHVUYx1h3b32PYKHrQKu6syCE4OrMY0yjdLH +1WnHeC3iB02AFy7TTfmeUiMTxaiPXAPjBDDIQtv1GHt8GR7WHSD3seIhAu6Lzbyr +0zrxk52C9v0YP1lgOwnvmQfbUSpWc29yhrXFkqkZToqbmYjNO55gPN8JA/2GrWan +s8gQwQ8z+yWAqNJQA5S+9+hNlBlcq659gCjIxoyCmkol4EepwR1WWdZjs2I00FHk +mQL1ig6H81rg/Bh2SraKR1tGdmjCNFi4RfWHsxCBcd1cGFeUIN+ygNmjXmzXJDFP +5vUXL9J5iu+WD1rnwB2gPRSvZUrmKUZnOGk0/kt1RpgbcFdOza+6vZmB51fXZYpD +YyvXHTbuHVOyXA160/Fmg6BNy5BfrTuXaZ3YVeZmvDf+ywVl2BFDQZDoLLQMIHzl +L2DdMuhVmgITqx8ZtrSxqBxW0DQXFZiMT+sv81+o2SO5nDzSYjoXfQv/Xkgpx44= +-----END CERTIFICATE----- diff --git a/test/test-resolve/selfsigned.key b/test/test-resolve/selfsigned.key new file mode 100644 index 000000000..44a09829e --- /dev/null +++ b/test/test-resolve/selfsigned.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQC14826tEEHKICM +/AKOKsyBUDaa6Z6KqS927ifb43LJfxtg8vW+vX9OGtje2qVAoI1UMSu4yttItSd9 +cjrnAFLPFvQGC8dFhn436ehLWiKPAP5KvhIQ3equ5fTicn+Hxdm7C3Um2SEEE347 +I/vArfzuNE7PIJ57sh7KeBHGCrU36iPl1DkkUilbqJAcgFoepozx1SbPq4h8Lsdq +JDKg+XUtvtuUS850D5Hb5ErTc8NLVrA+urSIr+yIX4jAeLXbktNLGuAc3+cJTjwu +WdDvP51yS0qpj459dFaRWQE9gu0bDkRwYLF7Mpel66B8TBkHAWBhSs+oLNnv//Zv +945wbUkTK29N2VtSEI4pd/47nTX9MwGn4q/ZAjhI7JUN3LcRDsrLdVAUabbK/U+x +kL/lOlRRBK/1iuLELkaJlMUiuqZhq3DvNjqeT5yY8GTU5iXoBcvY0lac3+zYaemT +gD5cZfF4gpTflGfc5Gf+he6U3DolTT+4JfMrw0YdbqsH4oyEtmLBfMvvp+PQysiO +ELSFbSAphZOOcy8QSzoRrniNynPdkM7kIM+0t2XUaz0lKtNuZSo9DnhTMvTLPnnb +k5aJt5nPxPprcdqhcJhrHw7gVhBoEceYJmXGiJJMLYuBNymZ4u7YrBg0e0qO+Fi9 +a4Kfh/QNMq/6VrpWvycb9LtCLhU+qQIDAQABAoICAEbiyfm6aCFnCnpneIN5cIvw +++bxpyT4/JOICyaqBME8dSoaZeV5KpUA54Yqhf6i05F9PEHfZQh3+TTtgMEoIh2t +H1r/2iBhYu1djndXYGKFC5WLb7T9F4oj+oUKBGOgmtNHiteiBTj2c9qOkn2sEQew +gQo99yXT7CYSFzMsVyW8bVMTm1VpY87h6ZACAZ0yYXmaDW8ftahX/sWB5+1Oavly +CVdJF+OpcbnVxceUtQa2eSdpUhR3I2KegMgqAw3YsdnyVmdKZ1r8D34s6L1k+HJj +n2xnkyuXXGl224HidY92xvtY47JUrD8wjjIC4joVsj8YjcdH+4OKKLvIKc3s+W4C ++fF6Uhxe7V0kg6bw+UE5eGC0CYeQww4Ruwd7Y8MHpF+6QAK2QsVWGCrhvCgXf9ix +s4iJBjaGG3fEMVyTrMeaJ/IhI4x6awjN8f/viqXfpwU5a06/JpILghgNTigHTCi2 ++Bpv4pXNmdn5AdLbGU+H+XVannY1obM2TV3XK5xMDpEblHG/Y2uNuOGa1YvSUlnl +AkYCV9pk39Hpnp0TWZ0MQmV4gG0VqyS7t6rh+3xehK+dxD5O9T+BSeB2H5hRrN5V +qImdsRvJMjj1WMQcREfC+9DTY/YOBlMdymtm7cix0JhnlK4UlvInQYvrnPasYvUA +wUVINpxGc87HxFEWeqSBAoIBAQDv8f5vS2w1yZNnz1cQwCPvUmLEevruCeO0IR71 +zNGRncoseMr8la4QOzAFCdosAqrfMlIuHK6Y1LVGgyEK4RYsU3zpOWHMzBalhChJ +dXeZoP/5a7p/Hxrh1YoZCCenTQne/KHIzhyOUzkYn2YltibkOR+sVUeYx8ZgDXxV +WwJmeMRV9Y19sE3fGYEQAo4gd1/8kObWlT49VwqDuUAjTtwwFQKs2b6UF77cAOgt +U1rclYg9LyB+liGof1TMA1S0z63keOYBX2S3154rB/91vXJdWPx0q1kCjyFfBlAp +ckxa7pygcWluGlUUqQlKglEEzQrA32OXRBa1loy2bzeU+px5AoIBAQDCD2HfzCzE +gl91ZgPnmTK7vDz3Dc+7nGP9ZVFBJory5zC/aCasaQ2FndpSQiC1kzVKdrW1h+U/ +yY2Bg8KrxyV6xP/yR2l0dM4tdjhZRJtCHIua2/K9w8aC713ak1ZbjVQtQPdX0tNy +zQADb/WaI3cLoXBul9vtEqdILqsMe3RsaTsAtswheCuT8n4ySbgdepavNOufTvh8 +TJVZq4+V0Vas+jYt4+yODHqfswGG1Ud2kZ4rucv8XsIOkA24eDsAgzBQormbeSXJ +KS37LRT3Swf+jA7WajfwrKV6cO7Y9mwOPMWZzFz3ES/qFYbK9s1KftkcudiYRc25 +KJZslS5xVcexAoIBAHjAKtAtf65t2/2xDVrDpxHoPwYr8Z3bYjkjNeZzBcAnTTgm +LdkBJpDKiHbwp1fgm8cpFsxX6NHGsddjZDyKW9NAzKq+Euayim8PXArjz6WDrW4C +9d7Fc4zVHuNMBFCgZ2hNcMmSWDKT1Tb7+LbfvSC7UqIyZI6Rctah0sFNxJ53Bi9Q +HL10/StaNWYuMwJJsQd0kIbKooDSDduOXaWnKQ4VdLwx9EOo04b5+d3dheteYSqR +TeQGf7fBJJZq0rUPkq5Y3T8xl4khPFrhcoD5LtWlU58PIAM2ro+YqLzC5YQZcr8X +c/xRyiFUk/VoMYed/Fxlz0Ovo1INCpFA1RLnL9kCggEAX/0923Zh+n3GfAqPCeME +bkpJGacSRumvp+qSy5gmCMqEmVkKMCPylVIkaKXfChGbvY6EiRuEMQ4gWZz0EQX7 +qwOA2rWqGvmf9mrQqo8+APCfuWTsaCNLsP53vSM+ByEcLxpAfoeBIfr287xQjwLV +4sHjHEEvfs/IQPMclpsGVo2iqtLAnBmV7KN4+qTuVl6J5HZXykBEty8mfOlYp7GZ +nwxQ+lgQbZ8MlKv1qF0c8TBMPbK0jMvOT2e/8aw++xzpLCmhh57gKuWcoe6FvWC2 +vplGyZZWv0yWub7c1iLmBhDXaSDmJyuwOKiXORPlLeEawZPH6GI2xUynQ2RzSYo1 +sQKCAQAbVhs1HcP5PAOTF5jAUdMbx+LeLUgKjO3Nx+YUeQCKVOgypd8w7N9k7WPi +BvTu7nkMtiK5UCix+UGthUFYyMClD2wnQ1h6nhVVz/D98cukksr1awNu6ms9M2ol +u6U7tfViEJhPxL+1pdAnFmqAoQx8fGpiyZQbb9DAcVBrIqQEjCRr4yZ8XaHOcTL0 +OeQO6ZCgxYOO5ac4snc1PDnRrlLs++b6tyaunLsFRSBkuzkMugXgUc+y3xgzBUQf +LOb/QIZvtqyF6s/YJZtLjLC8vdoe0ZqINh5Dq1xoGvtI1/QMgWraom999w9liFWs +VULYeUwXocBKk6rBSgDlsFF5LW22 +-----END PRIVATE KEY----- diff --git a/test/test-rpm-macros.sh b/test/test-rpm-macros.sh new file mode 100755 index 000000000..5843b7234 --- /dev/null +++ b/test/test-rpm-macros.sh @@ -0,0 +1,162 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# This test makes some basic checks that RPM macros work correctly. +# RPM is a simple C program available on different Linux distros, not only RPM-based ones, +# and even BSD systems, so it must not be a problem to require it. +# rpmspec utility is required (so this test will work with RPM 4 but won't work with RPM 5). +set -eu + +BUILD_DIR="${1:?Missing argument: build directory}" +RPM_MACROS_FILE="${BUILD_DIR:?}/src/rpm/macros.systemd" + +if ! command -v rpm >/dev/null || ! command -v rpmspec >/dev/null; then + echo >&2 "Missing necessary utilities (rpm, rpmspec), can't continue" + exit 1 +fi + +if [[ ! -f "${RPM_MACROS_FILE:?}" ]]; then + echo "RPM macros file not found in $RPM_MACROS_FILE!" + exit 1 +fi + +at_exit() { + if [[ -v WORK_DIR && -d "$WORK_DIR" ]]; then + rm -frv "$WORK_DIR" + fi +} + +trap at_exit EXIT + +WORK_DIR="$(mktemp -d)" +RPM_SPEC="$(mktemp "$WORK_DIR/systemd-test-rpm-macros-XXX.spec")" +TEMP_LOG="$(mktemp "$WORK_DIR/out-XXX.log")" + +die() { + echo >&2 "${1:?}" + exit 1 +} + +mk_mini_spec() { + cat >"${RPM_SPEC:?}" <>"$RPM_SPEC" +: >"$TEMP_LOG" +rpmspec --parse "$RPM_SPEC" | tee "$TEMP_LOG" +for i in post preun postun; do + echo "== Requires($i) ==" + grep "^Requires($i): systemd$" "$TEMP_LOG" +done + +echo "=== Test %systemd_ordering ===" +mk_mini_spec +echo "%systemd_ordering" >>"$RPM_SPEC" +: >"$TEMP_LOG" +rpmspec --parse "$RPM_SPEC" | tee "$TEMP_LOG" +for i in post preun postun; do + echo "== OrderWithRequires($i) ==" + grep "^OrderWithRequires($i): systemd$" "$TEMP_LOG" +done + +echo "=== Test macros requiring an argument without specifying such argument ===" +for i in \ + systemd_post \ + systemd_preun \ + systemd_postun \ + systemd_postun_with_restart \ + systemd_user_preun \ + systemd_user_postun \ + systemd_user_postun_with_restart \ + tmpfiles_create \ + tmpfiles_create_package \ + sysusers_create \ + sysusers_create_package +do + echo "== Macro: $i ==" + mk_mini_spec + echo "%${i}" >>"$RPM_SPEC" + if rpmspec --parse "$RPM_SPEC"; then + die "Unexpected pass with macro $i (no arguments)" + fi +done + +echo "=== Test macros requiring two arguments ===" +for i in \ + tmpfiles_create_package \ + sysusers_create_package +do + echo "== Macro: $i ==" + # Test with an incorrect number of arguments (0, 1, 3) + for args in "" "arg1" "arg1 arg2 arg3"; do + mk_mini_spec + echo "%${i} $args" >>"$RPM_SPEC" + if rpmspec --parse "$RPM_SPEC"; then + die "Unexpected pass with macro $i (arguments: $args)" + fi + done + + # Test with the correct number of arguments (2) + mk_mini_spec + echo "%${i} arg1 arg2" >>"$RPM_SPEC" + if ! rpmspec --parse "$RPM_SPEC"; then + die "Unexpected fail with macro $i (arguments: $args)" + fi +done + + +# Test that: +# - *_create_package macros do work correctly +# - shell syntax is correct (https://github.com/systemd/systemd/commit/93406fd37) +# - RPM macros, loaded from macros.in, are actually expanded +echo "=== Test %*_create_package macros ===" +for i in sysusers tmpfiles; do + echo "== Macro: ${i}_create_package ==" + + PKG_DATA_FILE="$(mktemp "$WORK_DIR/pkg-data-XXX")" + EXP_OUT="$(mktemp "$WORK_DIR/exp-out-XXX.log")" + CONF_DIR="$(pkg-config --variable="${i}dir" systemd)" + EXTRA_ARGS=() + + if [[ "$i" == tmpfiles ]]; then + EXTRA_ARGS+=("--create") + fi + + echo "TEST_DATA" >"$PKG_DATA_FILE" + mk_mini_spec + echo "%${i}_create_package TEST_NAME ${PKG_DATA_FILE}" >>"$RPM_SPEC" + + cat >"$EXP_OUT" <"$TEMP_LOG" + rpmspec --parse "$RPM_SPEC" | tee "$TEMP_LOG" + diff "$EXP_OUT" <(grep -A1 -B1 '^TEST_DATA$' "$TEMP_LOG") + + rm -f "$PKG_DATA_FILE" +done diff --git a/test/test-shutdown.py b/test/test-shutdown.py new file mode 100755 index 000000000..d34e22494 --- /dev/null +++ b/test/test-shutdown.py @@ -0,0 +1,114 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: LGPL-2.1-or-later +# + +import argparse +import logging +import pexpect +import sys + + +def run(args): + + ret = 1 + logger = logging.getLogger("test-shutdown") + + logger.info("spawning test") + console = pexpect.spawn(args.command, args.arg, env={ + "TERM": "linux", + }, encoding='utf-8', timeout=30) + + if args.verbose: + console.logfile = sys.stdout + + logger.debug("child pid %d" % console.pid) + + try: + logger.info("waiting for login prompt") + console.expect('H login: ', 10) + + logger.info("log in and start screen") + console.sendline('root') + console.expect('bash.*# ', 10) + console.sendline('screen') + console.expect('screen0 ', 10) + console.sendcontrol('a') + console.send('c') + console.expect('screen1 ', 10) + +# console.interact() + + console.sendline('tty') + console.expect(r'/dev/(pts/\d+)') + pty = console.match.group(1) + logger.info("window 1 at line %s", pty) + + logger.info("schedule reboot") + console.sendline('shutdown -r') + console.expect("Reboot scheduled for (?P.*), use 'shutdown -c' to cancel", 2) + date = console.match.group('date') + logger.info("reboot scheduled for %s", date) + + console.sendcontrol('a') + console.send('0') + logger.info("verify broadcast message") + console.expect('Broadcast message from root@H on %s' % pty, 2) + console.expect('The system is going down for reboot at %s' % date, 2) + + logger.info("check show output") + console.sendline('shutdown --show') + console.expect("Reboot scheduled for %s, use 'shutdown -c' to cancel" % date, 2) + + logger.info("cancel shutdown") + console.sendline('shutdown -c') + console.sendcontrol('a') + console.send('1') + console.expect('The system shutdown has been cancelled', 2) + + logger.info("call for reboot") + console.sendline('sleep 10; shutdown -r now') + console.sendcontrol('a') + console.send('0') + console.expect("The system is going down for reboot NOW!", 12) + + logger.info("waiting for reboot") + + console.expect('H login: ', 10) + console.sendline('root') + console.expect('bash.*# ', 10) + + console.sendline('> /testok') + + logger.info("power off") + console.sendline('poweroff') + + logger.info("expect termination now") + console.expect(pexpect.EOF) + + ret = 0 + except Exception as e: + logger.error(e) + logger.info("killing child pid %d" % console.pid) + console.terminate() + + return ret + + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='test logind shutdown feature') + parser.add_argument("-v", "--verbose", action="store_true", help="verbose") + parser.add_argument("command", help="command to run") + parser.add_argument("arg", nargs='*', help="args for command") + + args = parser.parse_args() + + if args.verbose: + level = logging.DEBUG + else: + level = logging.INFO + + logging.basicConfig(level=level) + + sys.exit(run(args)) + +# vim: sw=4 et diff --git a/test/test-systemctl-enable.sh b/test/test-systemctl-enable.sh new file mode 100644 index 000000000..f40831cf8 --- /dev/null +++ b/test/test-systemctl-enable.sh @@ -0,0 +1,694 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex + +# Silence warning from running_in_chroot_or_offline() +export SYSTEMD_IGNORE_CHROOT=1 + +systemctl=${1:-systemctl} +systemd_id128=${2:-systemd-id128} + +unset root +cleanup() { + [ -n "$root" ] && rm -rf "$root" +} +trap cleanup exit +root=$(mktemp -d --tmpdir systemctl-test.XXXXXX) + +islink() { + test -h "$1" || return 1 + test "$(readlink "$1")" = "$2" || return 2 +} + +: '------enable nonexistent------------------------------------' +( ! "$systemctl" --root="$root" enable test1.service ) + +: '------basic enablement--------------------------------------' +mkdir -p "$root/etc/systemd/system" +cat >"$root/etc/systemd/system/test1.service" <>"$root/etc/systemd/system/test1.service" <"$root/etc/systemd/system/test1a.service" <"$root/etc/systemd/system/test2.socket" <"$root/etc/systemd/system/test2.service" <"$root/link1.path" <"$root/link3.suffix" <"$root/etc/systemd/system/link5-also.service" <"$root/etc/systemd/system/myown.d/link5.service" <"$root/etc/systemd/system/templ1@.service" <"$root/etc/systemd/system/templ1@.service" <"$root/etc/systemd/system/templ2@.service" <"$root/etc/systemd/system/link4.service" <"$root/etc/systemd/system/link4.service" <"$root/etc/systemd/system/link5.service" <"$root/link5copy.service" <"$root/etc/systemd/system/link5@.path" <"$root/etc/systemd/system/multilink.mount" <"$root/etc/systemd/system/some-some-link6@.socket" <"$root/etc/os-release" <"$root/etc/os-release" <"$root/etc/machine-id" +check_alias m "$(cat "$root/etc/machine-id")" + +check_alias n 'some-some-link6@.socket' +check_alias N 'some-some-link6@' + +check_alias p 'some-some-link6' + +uname -r | grep -q '[^a-zA-Z0-9_.\\-]' || \ + check_alias v "$(uname -r)" + +# % is not legal in unit name +( ! check_alias % '%' ) + +# %z is not defined +( ! check_alias z 'z' ) + +: '-------specifiers in WantedBy-------------------------------' +# We don't need to repeat all the tests. Let's do a basic check that specifier +# expansion is performed. + +cat >"$root/etc/systemd/system/some-some-link7.socket" <"$root/etc/os-release2" <>/etc/udev/udev.conf - echo "timeout_signal=SIGABRT" >>/etc/udev/udev.conf + cat >>/etc/udev/udev.conf <"$TMPDIR"/monitor.txt & + KILL_PID="$!" - for _ in {1..20}; do + SYSTEMD_LOG_LEVEL=debug udevadm trigger --verbose --action add /dev/null + + for _ in {1..40}; do if coredumpctl --since "$since" --no-legend --no-pager | grep /bin/udevadm ; then + kill "$KILL_PID" + KILL_PID= + + cat "$TMPDIR"/monitor.txt + grep -q 'UDEV_WORKER_FAILED=1' "$TMPDIR"/monitor.txt + grep -q 'UDEV_WORKER_SIGNAL=6' "$TMPDIR"/monitor.txt + grep -q 'UDEV_WORKER_SIGNAL_NAME=ABRT' "$TMPDIR"/monitor.txt return 0 fi sleep .5 diff --git a/test/units/testsuite-29.sh b/test/units/testsuite-29.sh index 5dccad04f..47fd31b9b 100755 --- a/test/units/testsuite-29.sh +++ b/test/units/testsuite-29.sh @@ -71,24 +71,36 @@ portablectl list | grep -q -F "No images." portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_0.raw app0 systemctl is-active app0.service +status="$(portablectl is-attached --extension app0 minimal_0)" +[[ "${status}" == "running-runtime" ]] portablectl "${ARGS[@]}" reattach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0 systemctl is-active app0.service +status="$(portablectl is-attached --extension app0 minimal_1)" +[[ "${status}" == "running-runtime" ]] portablectl detach --now --runtime --extension /usr/share/app0.raw /usr/share/minimal_1.raw app0 portablectl "${ARGS[@]}" attach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_0.raw app1 systemctl is-active app1.service +status="$(portablectl is-attached --extension app1 minimal_0)" +[[ "${status}" == "running-runtime" ]] + +# Ensure that adding or removing a version to the image doesn't break reattaching +cp /usr/share/app1.raw /tmp/app1_2.raw +portablectl "${ARGS[@]}" reattach --now --runtime --extension /tmp/app1_2.raw /usr/share/minimal_1.raw app1 + +systemctl is-active app1.service +status="$(portablectl is-attached --extension app1_2 minimal_1)" +[[ "${status}" == "running-runtime" ]] portablectl "${ARGS[@]}" reattach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1 systemctl is-active app1.service - -portablectl inspect --cat --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1 | grep -F "MARKER=2" -portablectl inspect --cat --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1 | grep -F "PORTABLE_PREFIXES=app1" -portablectl inspect --cat --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1 | grep -F "ExecStart=/opt/script1.sh" +status="$(portablectl is-attached --extension app1 minimal_1)" +[[ "${status}" == "running-runtime" ]] portablectl detach --now --runtime --extension /usr/share/app1.raw /usr/share/minimal_1.raw app1 @@ -100,7 +112,8 @@ grep -q -F baz "${state_directory}/app1/foo" # portablectl also works with directory paths rather than images -mkdir /tmp/rootdir /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc +mkdir /tmp/rootdir /tmp/app0 /tmp/app1 /tmp/overlay /tmp/os-release-fix /tmp/os-release-fix/etc +mount /usr/share/app0.raw /tmp/app0 mount /usr/share/app1.raw /tmp/app1 mount /usr/share/minimal_0.raw /tmp/rootdir @@ -121,7 +134,22 @@ systemctl is-active app1.service portablectl detach --now --runtime overlay app1 umount /tmp/overlay + +portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 + +systemctl is-active app0.service +systemctl is-active app1.service + +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/rootdir/usr/lib/os-release +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/extension-release.d/extension-release.app0 +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/extension-release.d/extension-release.app2 +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app1/usr/lib/systemd/system/app1.service +portablectl inspect --cat --extension app0 --extension app1 rootdir app0 app1 | grep -q -f /tmp/app0/usr/lib/systemd/system/app0.service + +portablectl detach --now --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1 + umount /tmp/rootdir +umount /tmp/app0 umount /tmp/app1 echo OK >/testok diff --git a/test/units/testsuite-36.sh b/test/units/testsuite-36.sh index b6c00c484..f9dfd0810 100755 --- a/test/units/testsuite-36.sh +++ b/test/units/testsuite-36.sh @@ -181,7 +181,7 @@ else echo "PID1 NUMAPolicy support - Bind policy w/o mask" writePID1NUMAPolicy "bind" pid1ReloadWithJournal - grep "Failed to set NUMA memory policy: Invalid argument" "$journalLog" + grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" echo "PID1 NUMAPolicy support - Bind policy w/ mask" writePID1NUMAPolicy "bind" "0" @@ -191,7 +191,7 @@ else echo "PID1 NUMAPolicy support - Interleave policy w/o mask" writePID1NUMAPolicy "interleave" pid1ReloadWithJournal - grep "Failed to set NUMA memory policy: Invalid argument" "$journalLog" + grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" echo "PID1 NUMAPolicy support - Interleave policy w/ mask" writePID1NUMAPolicy "interleave" "0" @@ -202,7 +202,7 @@ else writePID1NUMAPolicy "preferred" pid1ReloadWithJournal # Preferred policy with empty node mask is actually allowed and should reset allocation policy to default - grep "Failed to set NUMA memory policy: Invalid argument" "$journalLog" && { echo >&2 "unexpected pass"; exit 1; } + grep "Failed to set NUMA memory policy, ignoring: Invalid argument" "$journalLog" && { echo >&2 "unexpected pass"; exit 1; } echo "PID1 NUMAPolicy support - Preferred policy w/ mask" writePID1NUMAPolicy "preferred" "0" diff --git a/test/units/testsuite-43.sh b/test/units/testsuite-43.sh index 9c0f93b51..fec936e99 100755 --- a/test/units/testsuite-43.sh +++ b/test/units/testsuite-43.sh @@ -15,6 +15,8 @@ runas() { runas testuser systemd-run --wait --user --unit=test-private-users \ -p PrivateUsers=yes -P echo hello +runas testuser systemctl --user log-level debug + runas testuser systemd-run --wait --user --unit=test-private-tmp-innerfile \ -p PrivateUsers=yes -p PrivateTmp=yes \ -P touch /tmp/innerfile.txt @@ -66,6 +68,82 @@ runas testuser systemd-run --wait --user --unit=test-group-fail \ -P true \ && { echo 'unexpected success'; exit 1; } +# Check that with a new user namespace we can bind mount +# files and use a different root directory +runas testuser systemd-run --wait --user --unit=test-bind-mount \ + -p PrivateUsers=yes -p BindPaths=/dev/null:/etc/os-release \ + test ! -s /etc/os-release + +runas testuser systemd-run --wait --user --unit=test-read-write \ + -p PrivateUsers=yes -p ReadOnlyPaths=/ \ + -p ReadWritePaths="/var /run /tmp" \ + -p NoExecPaths=/ -p ExecPaths=/usr \ + test ! -w /etc/os-release + +runas testuser systemd-run --wait --user --unit=test-caps \ + -p PrivateUsers=yes -p AmbientCapabilities=CAP_SYS_ADMIN \ + -p CapabilityBoundingSet=CAP_SYS_ADMIN \ + test -s /etc/os-release + +runas testuser systemd-run --wait --user --unit=test-devices \ + -p PrivateUsers=yes -p PrivateDevices=yes -p PrivateIPC=yes \ + sh -c "ls -1 /dev/ | wc -l | grep -q -F 18" + +# Same check as test/test-execute/exec-privatenetwork-yes.service +runas testuser systemd-run --wait --user --unit=test-network \ + -p PrivateUsers=yes -p PrivateNetwork=yes \ + /bin/sh -x -c '! ip link | grep -E "^[0-9]+: " | grep -Ev ": (lo|(erspan|gre|gretap|ip_vti|ip6_vti|ip6gre|ip6tnl|sit|tunl)0@.*):"' + +runas testuser systemd-run --wait --user --unit=test-hostname \ + -p PrivateUsers=yes -p ProtectHostname=yes \ + hostnamectl hostname foo \ + && { echo 'unexpected success'; exit 1; } + +runas testuser systemd-run --wait --user --unit=test-clock \ + -p PrivateUsers=yes -p ProtectClock=yes \ + timedatectl set-time "2012-10-30 18:17:16" \ + && { echo 'unexpected success'; exit 1; } + +runas testuser systemd-run --wait --user --unit=test-kernel-tunable \ + -p PrivateUsers=yes -p ProtectKernelTunables=yes \ + sh -c "echo 0 > /proc/sys/user/max_user_namespaces" \ + && { echo 'unexpected success'; exit 1; } + +runas testuser systemd-run --wait --user --unit=test-kernel-mod \ + -p PrivateUsers=yes -p ProtectKernelModules=yes \ + sh -c "modprobe -r overlay && modprobe overlay" \ + && { echo 'unexpected success'; exit 1; } + +if sysctl kernel.dmesg_restrict=0; then + runas testuser systemd-run --wait --user --unit=test-kernel-log \ + -p PrivateUsers=yes -p ProtectKernelLogs=yes -p LogNamespace=yes \ + dmesg \ + && { echo 'unexpected success'; exit 1; } +fi + +unsquashfs -no-xattrs -d /tmp/img /usr/share/minimal_0.raw +runas testuser systemd-run --wait --user --unit=test-root-dir \ + -p PrivateUsers=yes -p RootDirectory=/tmp/img \ + grep MARKER=1 /etc/os-release + +mkdir /tmp/img_bind +mount --bind /tmp/img /tmp/img_bind +runas testuser systemd-run --wait --user --unit=test-root-dir-bind \ + -p PrivateUsers=yes -p RootDirectory=/tmp/img_bind -p MountFlags=private \ + grep MARKER=1 /etc/os-release +umount /tmp/img_bind + +# Unprivileged overlayfs was added to Linux 5.11, so try to detect it first +mkdir -p /tmp/a /tmp/b /tmp/c +if unshare --mount --user --map-root-user mount -t overlay overlay /tmp/c -o lowerdir=/tmp/a:/tmp/b; then + unsquashfs -no-xattrs -d /tmp/app2 /usr/share/app1.raw + runas testuser systemd-run --wait --user --unit=test-extension-dir \ + -p PrivateUsers=yes -p ExtensionDirectories=/tmp/app2 \ + -p TemporaryFileSystem=/run -p RootDirectory=/tmp/img \ + -p MountAPIVFS=yes \ + grep PORTABLE_PREFIXES=app1 /usr/lib/extension-release.d/extension-release.app2 +fi + systemd-analyze log-level info echo OK >/testok diff --git a/test/units/testsuite-46.sh b/test/units/testsuite-46.sh index c3e57cec9..d0bedc63d 100755 --- a/test/units/testsuite-46.sh +++ b/test/units/testsuite-46.sh @@ -153,20 +153,13 @@ if ! systemd-detect-virt -cq ; then inspect test-user2 fi -wait_for_state test-user inactive PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz -wait_for_state test-user inactive PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz \ && { echo 'unexpected success'; exit 1; } -wait_for_state test-user inactive PASSWORD=xEhErW0ndafV4s homectl with test-user -- touch /home/test-user/xyz -wait_for_state test-user inactive PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz -wait_for_state test-user inactive PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz -wait_for_state test-user inactive PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz -wait_for_state test-user inactive PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz \ && { echo 'unexpected success'; exit 1; } diff --git a/test/units/testsuite-50.sh b/test/units/testsuite-50.sh index 793795efd..6aabbd139 100755 --- a/test/units/testsuite-50.sh +++ b/test/units/testsuite-50.sh @@ -102,10 +102,16 @@ elif [ "${machine}" = "ia64" ]; then verity_guid=86ed10d5-b607-45bb-8957-d350f23d0571 signature_guid=e98b36ee-32ba-4882-9b12-0ce14655f46a architecture="ia64" +elif [ "${machine}" = "s390x" ]; then + root_guid=5eead9a9-fe09-4a1e-a1d7-520d00531306 + verity_guid=b325bfbe-c7be-4ab8-8357-139e652d2f6b + signature_guid=c80187a5-73a3-491a-901a-017c3fa953e9 + architecture="s390x" elif [ "${machine}" = "ppc64le" ]; then - # There's no support of PPC in the discoverable partitions specification yet, so skip the rest for now - echo OK >/testok - exit 0 + root_guid=c31c45e6-3f39-412e-80fb-4809c4980599 + verity_guid=906bd944-4589-4aae-a4e4-dd983917446a + signature_guid=d4a236e7-e873-4c07-bf1d-bf6cf7f1c3c6 + architecture="ppc64-le" else echo "Unexpected uname -m: ${machine} in testsuite-50.sh, please fix me" exit 1 @@ -316,6 +322,37 @@ EOF systemctl start testservice-50e.service systemctl is-active testservice-50e.service +# ExtensionDirectories will set up an overlay +mkdir -p "${image_dir}/app0" "${image_dir}/app1" +systemd-run -P --property ExtensionDirectories="${image_dir}/nonexistent" --property RootImage="${image}.raw" cat /opt/script0.sh && { echo 'unexpected success'; exit 1; } +systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh && { echo 'unexpected success'; exit 1; } +systemd-dissect --mount /usr/share/app0.raw "${image_dir}/app0" +systemd-dissect --mount /usr/share/app1.raw "${image_dir}/app1" +systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0" +systemd-run -P --property ExtensionDirectories="${image_dir}/app0" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1" +systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script0.sh | grep -q -F "extension-release.app0" +systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/some_file | grep -q -F "MARKER=1" +systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /opt/script1.sh | grep -q -F "extension-release.app2" +systemd-run -P --property ExtensionDirectories="${image_dir}/app0 ${image_dir}/app1" --property RootImage="${image}.raw" cat /usr/lib/systemd/system/other_file | grep -q -F "MARKER=1" +cat >/run/systemd/system/testservice-50f.service </testok exit 0 diff --git a/test/units/testsuite-51-repro-3.service b/test/units/testsuite-51-repro-3.service new file mode 100644 index 000000000..c68f93d11 --- /dev/null +++ b/test/units/testsuite-51-repro-3.service @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=Issue 22257 Repro with Restart=always + +[Service] +Type=simple +Restart=always +ExecCondition=/bin/false +ExecStart=sleep 100 +RestartSec=1 diff --git a/test/units/testsuite-51.sh b/test/units/testsuite-51.sh index fbe9693f3..e603d953a 100755 --- a/test/units/testsuite-51.sh +++ b/test/units/testsuite-51.sh @@ -5,9 +5,11 @@ set -o pipefail systemctl start testsuite-51-repro-1 systemctl start testsuite-51-repro-2 +systemctl start testsuite-51-repro-3 sleep 5 # wait a bit in case there are restarts so we can count them below [[ "$(systemctl show testsuite-51-repro-1 -P NRestarts)" == "0" ]] [[ "$(systemctl show testsuite-51-repro-2 -P NRestarts)" == "0" ]] +[[ "$(systemctl show testsuite-51-repro-3 -P NRestarts)" == "0" ]] touch /testok diff --git a/test/units/testsuite-54.sh b/test/units/testsuite-54.sh index 6fc32b0a5..c8b685b67 100755 --- a/test/units/testsuite-54.sh +++ b/test/units/testsuite-54.sh @@ -28,8 +28,25 @@ systemd-run -p LoadCredential=passwd:/etc/passwd \ rm '${CREDENTIALS_DIRECTORY}/passwd' \ && { echo 'unexpected success'; exit 1; } -# Now test encrypted credentials (only supported when built with OpenSSL though) +# Check directory-based loading +mkdir -p /tmp/ts54-creds/sub +echo -n a >/tmp/ts54-creds/foo +echo -n b >/tmp/ts54-creds/bar +echo -n c >/tmp/ts54-creds/baz +echo -n d >/tmp/ts54-creds/sub/qux +systemd-run -p LoadCredential=cred:/tmp/ts54-creds \ + -p DynamicUser=1 \ + --wait \ + --pipe \ + cat '${CREDENTIALS_DIRECTORY}/cred_foo' \ + '${CREDENTIALS_DIRECTORY}/cred_bar' \ + '${CREDENTIALS_DIRECTORY}/cred_baz' \ + '${CREDENTIALS_DIRECTORY}/cred_sub_qux' >/tmp/ts54-concat +( echo -n abcd ) | cmp /tmp/ts54-concat +rm /tmp/ts54-concat +rm -rf /tmp/ts54-creds +# Now test encrypted credentials (only supported when built with OpenSSL though) if systemctl --version | grep -q -- +OPENSSL ; then echo -n $RANDOM >/tmp/test-54-plaintext systemd-creds encrypt --name=test-54 /tmp/test-54-plaintext /tmp/test-54-ciphertext diff --git a/test/units/testsuite-55.sh b/test/units/testsuite-55.sh index 379ea9e56..009933e66 100755 --- a/test/units/testsuite-55.sh +++ b/test/units/testsuite-55.sh @@ -22,13 +22,23 @@ fi rm -rf /etc/systemd/system/testsuite-55-testbloat.service.d -echo "DefaultMemoryPressureDurationSec=2s" >>/etc/systemd/oomd.conf +# Configure oomd explicitly to avoid conflicts with distro dropins +mkdir -p /etc/systemd/oomd.conf.d/ +echo -e "[OOM]\nDefaultMemoryPressureDurationSec=2s" >/etc/systemd/oomd.conf.d/99-oomd-test.conf +mkdir -p /etc/systemd/system/-.slice.d/ +echo -e "[Slice]\nManagedOOMSwap=auto" >/etc/systemd/system/-.slice.d/99-oomd-test.conf +mkdir -p /etc/systemd/system/user@.service.d/ +echo -e "[Service]\nManagedOOMMemoryPressure=auto\nManagedOOMMemoryPressureLimit=0%" >/etc/systemd/system/user@.service.d/99-oomd-test.conf mkdir -p /etc/systemd/system/systemd-oomd.service.d/ echo -e "[Service]\nEnvironment=SYSTEMD_LOG_LEVEL=debug" >/etc/systemd/system/systemd-oomd.service.d/debug.conf systemctl daemon-reload +# enable the service to ensure dbus-org.freedesktop.oom1.service exists +# and D-Bus activation works +systemctl enable systemd-oomd.service + # if oomd is already running for some reasons, then restart it to make sure the above settings to be applied if systemctl is-active systemd-oomd.service; then systemctl restart systemd-oomd.service diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh index 42c31b71b..cef7d094f 100755 --- a/test/units/testsuite-58.sh +++ b/test/units/testsuite-58.sh @@ -11,6 +11,60 @@ fi export SYSTEMD_LOG_LEVEL=debug export PAGER=cat +machine="$(uname -m)" +if [ "${machine}" = "x86_64" ]; then + root_guid=4f68bce3-e8cd-4db1-96e7-fbcaf984b709 + root_uuid=60F33797-1D71-4DCB-AA6F-20564F036CD0 + usr_guid=8484680c-9521-48c6-9c11-b0720656f69e + usr_uuid=7E3369DD-D653-4513-ADF5-B993A9F20C16 + architecture="x86-64" +elif [ "${machine}" = "i386" ] || [ "${machine}" = "i686" ] || [ "${machine}" = "x86" ]; then + root_guid=44479540-f297-41b2-9af7-d131d5f0458a + root_uuid=02b4253f-29a4-404e-8972-1669d3b03c87 + usr_guid=75250d76-8cc6-458e-bd66-bd47cc81a812 + usr_uuid=7b42ffb0-b0e1-4395-b20b-c78f4a571648 + architecture="x86" +elif [ "${machine}" = "aarch64" ] || [ "${machine}" = "aarch64_be" ] || [ "${machine}" = "armv8b" ] || [ "${machine}" = "armv8l" ]; then + root_guid=b921b045-1df0-41c3-af44-4c6f280d3fae + root_uuid=055d0227-53a6-4033-85c3-9a5973eff483 + usr_guid=b0e01050-ee5f-4390-949a-9101b17104e9 + usr_uuid=fce3c75e-d6a4-44c0-87f0-4c105183fb1f + architecture="arm64" +elif [ "${machine}" = "arm" ]; then + root_guid=69dad710-2ce4-4e3c-b16c-21a1d49abed3 + root_uuid=567da89e-8de2-4499-8d10-18f212dff034 + usr_guid=7d0359a3-02b3-4f0a-865c-654403e70625 + usr_uuid=71e93dc2-5073-42cb-8a84-a354e64d8966 + architecture="arm" +elif [ "${machine}" = "loongarch64" ]; then + root_guid=77055800-792c-4f94-b39a-98c91b762bb6 + root_uuid=d8efc2d2-0133-41e4-bdcb-3b9f4cfddde8 + usr_guid=e611c702-575c-4cbe-9a46-434fa0bf7e3f + usr_uuid=031ffa75-00bb-49b6-a70d-911d2d82a5b7 + architecture="loongarch64" +elif [ "${machine}" = "ia64" ]; then + root_guid=993d8d3d-f80e-4225-855a-9daf8ed7ea97 + root_uuid=dcf33449-0896-4ea9-bc24-7d58aeef522d + usr_guid=4301d2a6-4e3b-4b2a-bb94-9e0b2c4225ea + usr_uuid=bc2bcce7-80d6-449a-85cc-637424ce5241 + architecture="ia64" +elif [ "${machine}" = "s390x" ]; then + root_guid=5eead9a9-fe09-4a1e-a1d7-520d00531306 + root_uuid=7ebe0c85-e27e-48ec-b164-f4807606232e + usr_guid=8a4f5770-50aa-4ed3-874a-99b710db6fea + usr_uuid=51171d30-35cf-4a49-b8b5-9478b9b796a5 + architecture="s390x" +elif [ "${machine}" = "ppc64le" ]; then + root_guid=c31c45e6-3f39-412e-80fb-4809c4980599 + root_uuid=061e67a1-092f-482f-8150-b525d50d6654 + usr_guid=15bb03af-77e7-4d4a-b12b-c0d084f7491c + usr_uuid=c0d0823b-8040-4c7c-a629-026248e297fb + architecture="ppc64-le" +else + echo "Unexpected uname -m: ${machine} in testsuite-58.sh, please fix me" + exit 1 +fi + rm -f /var/tmp/testsuite-58.img /var/tmp/testsuite-58.2.img /tmp/testsuite-58.dump mkdir -p /tmp/testsuite-58-defs/ @@ -25,7 +79,7 @@ EOF cat >/tmp/testsuite-58-defs/usr.conf </tmp/testsuite-58-defs/root.conf </tmp/testsuite-58-defs/usr.conf </tmp/testsuite-58-defs/root.conf </tmp/testsuite-58.3-defs/root.conf </tmp/testsuite-58-issue-21817-defs/test.conf < /tmp/testsuite-58-sector/a.conf < /tmp/testsuite-58-sector/b.conf < /tmp/testsuite-58-sector/c.conf <= 512 and <= PAGE_SIZE, and +# must be powers of 2. Which leaves exactly four different ones to test on +# typical hardware +testsector 512 +testsector 1024 +testsector 2048 +testsector 4096 + echo OK >/testok exit 0 diff --git a/test/units/testsuite-64.sh b/test/units/testsuite-64.sh index dc8b263b1..58b6157e6 100755 --- a/test/units/testsuite-64.sh +++ b/test/units/testsuite-64.sh @@ -148,6 +148,42 @@ helper_wait_for_pvscan() { return 1 } +# Generate an `flock` command line for a device list +# +# This is useful mainly for mkfs.btrfs, which doesn't hold the lock on each +# device for the entire duration of mkfs.btrfs, causing weird races between udev +# and mkfs.btrfs. This function creates an array of chained flock calls to take +# the lock of all involved devices, which can be then used in combination with +# mkfs.btrfs to mitigate the issue. +# +# For example, calling: +# helper_generate_flock_cmdline my_array /dev/loop1 /dev/loop2 /dev/loop3 +# +# will result in "${my_array[@]}" containing: +# flock -x /dev/loop1 flock -x /dev/loop2 flock -x /dev/loop3 +# +# Note: the array will be CLEARED before the first assignment +# +# Arguments: +# $1 - NAME of an array in which the commands/argument will be stored +# $2-$n - path to devices +helper_generate_flock_cmdline() { + # Create a name reference to the array passed as the first argument + # (requires bash 4.3+) + local -n cmd_array="${1:?}" + shift + + if [[ $# -eq 0 ]]; then + echo >&2 "Missing argument(s): device path(s)" + return 1 + fi + + cmd_array=() + for dev in "$@"; do + cmd_array+=("flock" "-x" "$dev") + done +} + testcase_megasas2_basic() { lsblk -S [[ "$(lsblk --scsi --noheadings | wc -l)" -ge 128 ]] @@ -388,6 +424,7 @@ testcase_lvm_basic() { testcase_btrfs_basic() { local dev_stub i label mpoint uuid + local flock_cmd=() local devices=( /dev/disk/by-id/ata-foobar_deadbeefbtrfs{0..3} ) @@ -397,7 +434,8 @@ testcase_btrfs_basic() { echo "Single device: default settings" uuid="deadbeef-dead-dead-beef-000000000000" label="btrfs_root" - mkfs.btrfs -L "$label" -U "$uuid" "${devices[0]}" + helper_generate_flock_cmdline flock_cmd "${devices[0]}" + "${flock_cmd[@]}" mkfs.btrfs -L "$label" -U "$uuid" "${devices[0]}" udevadm settle btrfs filesystem show test -e "/dev/disk/by-uuid/$uuid" @@ -416,7 +454,9 @@ name="diskpart3", size=85M name="diskpart4", size=85M EOF udevadm settle - mkfs.btrfs -d single -m raid1 -L "$label" -U "$uuid" /dev/disk/by-partlabel/diskpart{1..4} + # We need to flock only the device itself, not its partitions + helper_generate_flock_cmdline flock_cmd "${devices[0]}" + "${flock_cmd[@]}" mkfs.btrfs -d single -m raid1 -L "$label" -U "$uuid" /dev/disk/by-partlabel/diskpart{1..4} udevadm settle btrfs filesystem show test -e "/dev/disk/by-uuid/$uuid" @@ -427,7 +467,8 @@ EOF echo "Multiple devices: using disks, data: raid10, metadata: raid10, mixed mode" uuid="deadbeef-dead-dead-beef-000000000002" label="btrfs_mdisk" - mkfs.btrfs -M -d raid10 -m raid10 -L "$label" -U "$uuid" "${devices[@]}" + helper_generate_flock_cmdline flock_cmd "${devices[@]}" + "${flock_cmd[@]}" mkfs.btrfs -M -d raid10 -m raid10 -L "$label" -U "$uuid" "${devices[@]}" udevadm settle btrfs filesystem show test -e "/dev/disk/by-uuid/$uuid" @@ -464,7 +505,8 @@ EOF # Check if we have all necessary DM devices ls -l /dev/mapper/encbtrfs{0..3} # Create a multi-device btrfs filesystem on the LUKS devices - mkfs.btrfs -M -d raid1 -m raid1 -L "$label" -U "$uuid" /dev/mapper/encbtrfs{0..3} + helper_generate_flock_cmdline flock_cmd /dev/mapper/encbtrfs{0..3} + "${flock_cmd[@]}" mkfs.btrfs -M -d raid1 -m raid1 -L "$label" -U "$uuid" /dev/mapper/encbtrfs{0..3} udevadm settle btrfs filesystem show test -e "/dev/disk/by-uuid/$uuid" diff --git a/test/units/testsuite-68.service b/test/units/testsuite-68.service new file mode 100644 index 000000000..2d86e1fed --- /dev/null +++ b/test/units/testsuite-68.service @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=TEST-68-PROPAGATE-EXIT-STATUS + +[Service] +Type=oneshot +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh diff --git a/test/units/testsuite-68.sh b/test/units/testsuite-68.sh new file mode 100755 index 000000000..fefb03035 --- /dev/null +++ b/test/units/testsuite-68.sh @@ -0,0 +1,217 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex +set -o pipefail + +# Wait for a service to enter a state within a timeout period, if it doesn't +# enter the desired state within the timeout period then this function will +# exit the test case with a non zero exit code. +wait_on_state_or_fail () { + service=$1 + expected_state=$2 + timeout=$3 + + state=$(systemctl show "$service" --property=ActiveState --value) + while [ "$state" != "$expected_state" ]; do + if [ "$timeout" = "0" ]; then + systemd-analyze log-level info + exit 1 + fi + timeout=$((timeout - 1)) + sleep 1 + state=$(systemctl show "$service" --property=ActiveState --value) + done +} + +systemd-analyze log-level debug + +cat >/run/systemd/system/testservice-failure-68.service </run/systemd/system/testservice-failure-68-template.service </run/systemd/system/testservice-success-68.service </run/systemd/system/testservice-success-68-template.service </tmp/check_on_success.sh <<"EOF" +#!/bin/sh + +set -ex +env | sort +if [ "$MONITOR_SERVICE_RESULT" != "success" ]; then + echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'success'" + exit 1 +fi + +if [ "$MONITOR_EXIT_CODE" != "exited" ]; then + echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'" + exit 1 +fi + +if [ "$MONITOR_EXIT_STATUS" != "0" ]; then + echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '0'" + exit 1 +fi + +if [ -z "$MONITOR_INVOCATION_ID" ]; then + echo "MONITOR_INVOCATION_ID unset" + exit 1 +fi + +if [ "$MONITOR_UNIT" != "testservice-success-68.service" ] && + [ "$MONITOR_UNIT" != "testservice-success-68-template.service" ] && + [ "$MONITOR_UNIT" != "testservice-transient-success-68.service" ]; then + + echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-success-68[-template].service'" + exit 1 +fi + +exit 0 +EOF +chmod +x /tmp/check_on_success.sh + +cat >/run/systemd/system/testservice-success-exit-handler-68.service </run/systemd/system/testservice-success-exit-handler-68-template@.service </tmp/check_on_failure.sh <<"EOF" +#!/bin/sh + +set -ex +env | sort +if [ "$MONITOR_SERVICE_RESULT" != "exit-code" ]; then + echo "MONITOR_SERVICE_RESULT was '$MONITOR_SERVICE_RESULT', expected 'exit-code'" + exit 1 +fi + +if [ "$MONITOR_EXIT_CODE" != "exited" ]; then + echo "MONITOR_EXIT_CODE was '$MONITOR_EXIT_CODE', expected 'exited'" + exit 1 +fi + +if [ "$MONITOR_EXIT_STATUS" != "1" ]; then + echo "MONITOR_EXIT_STATUS was '$MONITOR_EXIT_STATUS', expected '1'" + exit 1 +fi + +if [ -z "$MONITOR_INVOCATION_ID" ]; then + echo "MONITOR_INVOCATION_ID unset" + exit 1 +fi + +if [ "$MONITOR_UNIT" != "testservice-failure-68.service" ] && + [ "$MONITOR_UNIT" != "testservice-failure-68-template.service" ] && + [ "$MONITOR_UNIT" != "testservice-transient-failure-68.service" ]; then + + echo "MONITOR_UNIT was '$MONITOR_UNIT', expected 'testservice[-transient]-failure-68[-template].service'" + exit 1 +fi + +exit 0 +EOF +chmod +x /tmp/check_on_failure.sh + + +cat >/run/systemd/system/testservice-failure-exit-handler-68.service </run/systemd/system/testservice-failure-exit-handler-68-template@.service </testok + +exit 0 diff --git a/test/units/testsuite-69.service b/test/units/testsuite-69.service new file mode 100644 index 000000000..7aa0664b8 --- /dev/null +++ b/test/units/testsuite-69.service @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=TEST-69-SHUTDOWN + +[Service] +Type=oneshot +ExecStart=/bin/true diff --git a/test/units/testsuite-70.service b/test/units/testsuite-70.service new file mode 100644 index 000000000..c13c2d51a --- /dev/null +++ b/test/units/testsuite-70.service @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=TEST-70-TPM2 + +[Service] +Type=oneshot +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh diff --git a/test/units/testsuite-70.sh b/test/units/testsuite-70.sh new file mode 100755 index 000000000..f395ef4e5 --- /dev/null +++ b/test/units/testsuite-70.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +set -ex + +export SYSTEMD_LOG_LEVEL=debug + + +# Prepare fresh disk image +img="/var/tmp/test.img" +dd if=/dev/zero of=$img bs=1024k count=20 status=none +echo -n passphrase >/tmp/passphrase +cryptsetup luksFormat -q --use-urandom $img /tmp/passphrase + +# Enroll unlock with default PCR policy +env PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto $img +/usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 +/usr/lib/systemd/systemd-cryptsetup detach test-volume + +# Check with wrong PCR +tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000 +/usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && { echo 'unexpected success'; exit 1; } + +# Enroll unlock with PCR+PIN policy +systemd-cryptenroll --wipe-slot=tpm2 $img +env PASSWORD=passphrase NEWPIN=123456 systemd-cryptenroll --tpm2-device=auto --tpm2-with-pin=true $img +env PIN=123456 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 +/usr/lib/systemd/systemd-cryptsetup detach test-volume + +# Check failure with wrong PIN +env PIN=123457 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && { echo 'unexpected success'; exit 1; } + +# Check failure with wrong PCR (and correct PIN) +tpm2_pcrextend 7:sha256=0000000000000000000000000000000000000000000000000000000000000000 +env PIN=123456 /usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && { echo 'unexpected success'; exit 1; } + +# Enroll unlock with PCR 0+7 +systemd-cryptenroll --wipe-slot=tpm2 $img +env PASSWORD=passphrase systemd-cryptenroll --tpm2-device=auto --tpm2-pcrs=0+7 $img +/usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 +/usr/lib/systemd/systemd-cryptsetup detach test-volume + +# Check with wrong PCR 0 +tpm2_pcrextend 0:sha256=0000000000000000000000000000000000000000000000000000000000000000 +/usr/lib/systemd/systemd-cryptsetup attach test-volume $img - tpm2-device=auto,headless=1 && exit 1 + +echo OK >/testok + +exit 0 diff --git a/test/units/testsuite-72.service b/test/units/testsuite-72.service new file mode 100644 index 000000000..164035057 --- /dev/null +++ b/test/units/testsuite-72.service @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +[Unit] +Description=TEST-72-SYSUPDATE + +[Service] +ExecStartPre=rm -f /failed /testok +ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh +Type=oneshot diff --git a/test/units/testsuite-72.sh b/test/units/testsuite-72.sh new file mode 100755 index 000000000..9effc982b --- /dev/null +++ b/test/units/testsuite-72.sh @@ -0,0 +1,170 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh +set -eux +set -o pipefail + +SYSUPDATE=/lib/systemd/systemd-sysupdate + +if ! test -x "$SYSUPDATE"; then + echo "no systemd-sysupdate" >/skipped + exit 0 +fi + +export SYSTEMD_PAGER=cat +export SYSTEMD_LOG_LEVEL=debug + +rm -f /var/tmp/72-joined.raw +truncate -s 10M /var/tmp/72-joined.raw + +sfdisk /var/tmp/72-joined.raw </var/tmp/72-defs/01-first.conf <<"EOF" +[Source] +Type=regular-file +Path=/var/tmp/72-source +MatchPattern=part1-@v.raw + +[Target] +Type=partition +Path=/var/tmp/72-joined.raw +MatchPattern=part1-@v +MatchPartitionType=root-x86-64 +EOF + +cat >/var/tmp/72-defs/02-second.conf <<"EOF" +[Source] +Type=regular-file +Path=/var/tmp/72-source +MatchPattern=part2-@v.raw.gz + +[Target] +Type=partition +Path=/var/tmp/72-joined.raw +MatchPattern=part2-@v +MatchPartitionType=root-x86-64-verity +EOF + +cat >/var/tmp/72-defs/03-third.conf <<"EOF" +[Source] +Type=directory +Path=/var/tmp/72-source +MatchPattern=dir-@v + +[Target] +Type=directory +Path=/var/tmp/72-dirs +CurrentSymlink=/var/tmp/72-dirs/current +MatchPattern=dir-@v +InstancesMax=3 +EOF + +rm -rf /var/tmp/72-source +mkdir -p /var/tmp/72-source + +new_version() { + # Create a pair of random partition payloads, and compress one + dd if=/dev/urandom of="/var/tmp/72-source/part1-$1.raw" bs=1024 count=1024 + dd if=/dev/urandom of="/var/tmp/72-source/part2-$1.raw" bs=1024 count=1024 + gzip -k -f "/var/tmp/72-source/part2-$1.raw" + + mkdir -p "/var/tmp/72-source/dir-$1" + echo $RANDOM >"/var/tmp/72-source/dir-$1/foo.txt" + echo $RANDOM >"/var/tmp/72-source/dir-$1/bar.txt" + + tar --numeric-owner -C "/var/tmp/72-source/dir-$1/" -czf "/var/tmp/72-source/dir-$1.tar.gz" . + + ( cd /var/tmp/72-source/ && sha256sum part* dir-*.tar.gz >SHA256SUMS ) +} + +update_now() { + # Update to newest version. First there should be an update ready, then we + # do the update, and then there should not be any ready anymore + + "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no check-new + "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no update + ( ! "$SYSUPDATE" --definitions=/var/tmp/72-defs --verify=no check-new ) +} + +verify_version() { + # Expects: version ID + sector offset of both partitions to compare + dd if=/var/tmp/72-joined.raw bs=1024 skip="$2" count=1024 | cmp "/var/tmp/72-source/part1-$1.raw" + dd if=/var/tmp/72-joined.raw bs=1024 skip="$3" count=1024 | cmp "/var/tmp/72-source/part2-$1.raw" + cmp "/var/tmp/72-source/dir-$1/foo.txt" /var/tmp/72-dirs/current/foo.txt + cmp "/var/tmp/72-source/dir-$1/bar.txt" /var/tmp/72-dirs/current/bar.txt +} + +# Install initial version and verify +new_version v1 +update_now +verify_version v1 1024 3072 + +# Create second version, update and verify that it is added +new_version v2 +update_now +verify_version v2 2048 4096 + +# Create third version, update and verify it replaced the first version +new_version v3 +update_now +verify_version v3 1024 3072 + +# Create fourth version, and update through a file:// URL. This should be +# almost as good as testing HTTP, but is simpler for us to set up. file:// is +# abstracted in curl for us, and since our main goal is to test our own code +# (and not curl) this test should be quite good even if not comprehensive. This +# will test the SHA256SUMS logic at least (we turn off GPG validation though, +# see above) +new_version v4 + +cat >/var/tmp/72-defs/02-second.conf <<"EOF" +[Source] +Type=url-file +Path=file:///var/tmp/72-source +MatchPattern=part2-@v.raw.gz + +[Target] +Type=partition +Path=/var/tmp/72-joined.raw +MatchPattern=part2-@v +MatchPartitionType=root-x86-64-verity +EOF + +cat >/var/tmp/72-defs/03-third.conf <<"EOF" +[Source] +Type=url-tar +Path=file:///var/tmp/72-source +MatchPattern=dir-@v.tar.gz + +[Target] +Type=directory +Path=/var/tmp/72-dirs +CurrentSymlink=/var/tmp/72-dirs/current +MatchPattern=dir-@v +InstancesMax=3 +EOF + +update_now +verify_version v4 2048 4096 + +rm /var/tmp/72-joined.raw +rm -r /var/tmp/72-dirs /var/tmp/72-defs /var/tmp/72-source + +echo OK >/testok + +exit 0 diff --git a/tmpfiles.d/etc.conf.in b/tmpfiles.d/etc.conf.in index ebdc699c2..2fdd498da 100644 --- a/tmpfiles.d/etc.conf.in +++ b/tmpfiles.d/etc.conf.in @@ -12,6 +12,7 @@ L+ /etc/mtab - - - - ../proc/self/mounts {% if HAVE_SMACK_RUN_LABEL %} t /etc/mtab - - - - security.SMACK64=_ {% endif %} +C! /etc/locale.conf - - - - C! /etc/nsswitch.conf - - - - {% if HAVE_PAM %} C! /etc/pam.d - - - - diff --git a/tools/dbus_exporter.py b/tools/dbus_exporter.py index 4da8b82af..f94f261e0 100755 --- a/tools/dbus_exporter.py +++ b/tools/dbus_exporter.py @@ -4,58 +4,39 @@ from argparse import ArgumentParser from pathlib import Path from subprocess import run, PIPE - def extract_interfaces_xml(output_dir, executable): - list_interfaces_process = run( + proc = run( args=[executable.absolute(), '--bus-introspect', 'list'], stdout=PIPE, check=True, - universal_newlines=True, - ) + universal_newlines=True) - interfaces_lines = list_interfaces_process.stdout.splitlines() - - interface_names = [x.split()[1] for x in interfaces_lines] + interface_names = (x.split()[1] for x in proc.stdout.splitlines()) for interface_name in interface_names: - interface_introspection_run = run( + proc = run( args=[executable.absolute(), '--bus-introspect', interface_name], stdout=PIPE, check=True, - universal_newlines=True, - ) + universal_newlines=True) interface_file_name = output_dir / (interface_name + '.xml') - with open(interface_file_name, mode='w') as f: - f.write(interface_introspection_run.stdout) + interface_file_name.write_text(proc.stdout) interface_file_name.chmod(0o644) - -def iterate_executables(output_dir, executables): - output_dir.mkdir(mode=0o755, exist_ok=True) - - for exe in executables: - extract_interfaces_xml(output_dir, exe) - - def main(): parser = ArgumentParser() - - parser.add_argument( - 'output', - type=Path, - ) - - parser.add_argument( - 'executables', - type=Path, - nargs='+', - ) + parser.add_argument('output', + type=Path) + parser.add_argument('executables', + nargs='+', + type=Path) args = parser.parse_args() - iterate_executables(args.output, args.executables) - + args.output.mkdir(exist_ok=True) + for exe in args.executables: + extract_interfaces_xml(args.output, exe) if __name__ == '__main__': main() diff --git a/tools/debug-sd-boot.sh b/tools/debug-sd-boot.sh new file mode 100755 index 000000000..1bd2cc406 --- /dev/null +++ b/tools/debug-sd-boot.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later + +set -e + +if [[ $# -lt 2 ]]; then + echo "Usage: ${0} TARGET INPUT [GDBSCRIPT]" + echo "Debug systemd-boot/stub in QEMU." + echo + echo "TARGET should point to the EFI binary to be examined inside the" + echo "build directory (systemd-boot\$ARCH.efi or linux\$arch.efi.stub)." + echo + echo "INPUT should point to the QEMU serial output pipe. This is used to" + echo "extract the location of the symbols. For this to work, QEMU must" + echo "be run with '-s -serial pipe:PATH'. Note that QEMU will append" + echo ".in/.out to the path, while this script expects the out pipe directly." + echo + echo "If GDBSCRIPT is empty, gdb is run directly attached to the boot" + echo "loader, otherwise a script is generated in the given path that allows" + echo "attaching manually like this:" + echo " (gdb) source GDBSCRIPT" + echo " (gdb) target remote :1234" + echo + echo "Example usage:" + echo " mkfifo /tmp/sdboot.{in,out}" + echo " qemu-system-x86_64 [...] -s -serial pipe:/tmp/sdboot" + echo " ./tools/debug-sd-boot.sh ./build/src/boot/efi/systemd-bootx64.efi \\" + echo " /tmp/sdboot.out" + exit 1 +fi + +binary=$(realpath "${1}") +if [[ "${1}" =~ systemd-boot([[:alnum:]]+).efi ]]; then + target="systemd-boot" + symbols=$(realpath "${1%efi}elf") +elif [[ "${1}" =~ linux([[:alnum:]]+).efi.stub ]]; then + target="systemd-stub" + symbols=$(realpath "${1%efi.stub}elf.stub") +else + echo "Cannot detect EFI binary '${1}'." + exit 1 +fi + +case "${BASH_REMATCH[1]}" in + ia32) arch="i386";; + x64) arch="i386:x86-64";; + aa64) arch="aarch64";; + arm|riscv64) arch="${BASH_REMATCH[1]}";; + *) + echo "Unknown EFI arch '${BASH_REMATCH[1]}'." + exit 1 +esac + +# system-boot will print out a line like this to inform us where gdb is supposed to +# look for .text and .data section: +# systemd-boot@0x0,0x0 +while read -r line; do + if [[ "${line}" =~ ${target}@(0x[[:xdigit:]]+),(0x[[:xdigit:]]+) ]]; then + text="${BASH_REMATCH[1]}" + data="${BASH_REMATCH[2]}" + break + fi +done < "${2}" + +if [[ -z "${text}" || -z "${data}" ]]; then + echo "Could not determine text and data location." + exit 1 +fi + +if [[ -z "${3}" ]]; then + gdb_script=$(mktemp /tmp/debug-sd-boot.XXXXXX.gdb) + trap 'rm -f "${gdb_script}"' EXIT +else + gdb_script="${3}" +fi + +echo "file ${binary} +add-symbol-file ${symbols} ${text} -s .data ${data} +set architecture ${arch}" > "${gdb_script}" + +if [[ -z "${3}" ]]; then + gdb -x "${gdb_script}" -ex "target remote :1234" +else + echo "GDB script written to '${gdb_script}'." +fi diff --git a/tools/git-contrib.sh b/tools/git-contrib.sh index f0756b4e4..cde1ecd22 100755 --- a/tools/git-contrib.sh +++ b/tools/git-contrib.sh @@ -3,7 +3,8 @@ set -eu tag="$(git describe --abbrev=0 --match 'v[0-9][0-9][0-9]')" -git log --pretty=tformat:%aN --author=noreply@weblate.org --invert-grep -s "${tag}.." | \ +git log --pretty=tformat:%aN -s "${tag}.." | + grep -v noreply@weblate.org | sed 's/ / /g; s/--/-/g; s/.*/\0,/' | - sort -u | tr '\n' ' ' | sed -e "s/^/Contributions from: /g" -e "s/,\s*$/\n/g" | fold -w 72 -s | \ + sort -u | tr '\n' ' ' | sed -e "s/^/Contributions from: /g" -e "s/,\s*$/\n/g" | fold -w 72 -s | sed -e "s/^/ /g" -e "s/\s*$//g" diff --git a/tools/oss-fuzz.sh b/tools/oss-fuzz.sh index 8a19da665..8ff3abefb 100755 --- a/tools/oss-fuzz.sh +++ b/tools/oss-fuzz.sh @@ -10,7 +10,7 @@ export CXX=${CXX:-clang++} clang_version="$($CC --version | sed -nr 's/.*version ([^ ]+?) .*/\1/p' | sed -r 's/-$//')" SANITIZER=${SANITIZER:-address -fsanitize-address-use-after-scope} -flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER" +flags="-O1 -fno-omit-frame-pointer -g -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER" clang_lib="/usr/lib64/clang/${clang_version}/lib/linux" [ -d "$clang_lib" ] || clang_lib="/usr/lib/clang/${clang_version}/lib/linux" @@ -35,7 +35,14 @@ else apt-get update apt-get install -y gperf m4 gettext python3-pip \ libcap-dev libmount-dev libkmod-dev \ - pkg-config wget python3-jinja2 + pkg-config wget python3-jinja2 zipmerge + + # gnu-efi is installed here to enable -Dgnu-efi behind which fuzz-bcd + # is hidden. It isn't linked against efi. It doesn't + # even include "efi.h" because "bcd.c" can work in "unit test" mode + # where it isn't necessary. + apt-get install -y gnu-efi zstd + pip3 install -r .github/workflows/requirements.txt --require-hashes # https://github.com/google/oss-fuzz/issues/6868 @@ -43,7 +50,8 @@ else export PYTHONPATH="$ORIG_PYTHONPATH:/usr/lib/python3/dist-packages/" if [[ "$SANITIZER" == undefined ]]; then - UBSAN_FLAGS="-fsanitize=pointer-overflow -fno-sanitize-recover=pointer-overflow" + additional_ubsan_checks=pointer-overflow,alignment + UBSAN_FLAGS="-fsanitize=$additional_ubsan_checks -fno-sanitize-recover=$additional_ubsan_checks" CFLAGS="$CFLAGS $UBSAN_FLAGS" CXXFLAGS="$CXXFLAGS $UBSAN_FLAGS" fi @@ -56,9 +64,23 @@ fi ninja -v -C "$build" fuzzers +# Compressed BCD files are kept in test/test-bcd so let's unpack them +# and put them all in the seed corpus. +bcd=$(mktemp -d) +for i in test/test-bcd/*.zst; do + unzstd "$i" -o "$bcd/$(basename "${i%.zst}")"; +done +zip -jqr "$OUT/fuzz-bcd_seed_corpus.zip" "$bcd" +rm -rf "$bcd" + +hosts=$(mktemp) +wget -O "$hosts" https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts +zip -jq "$OUT/fuzz-etc-hosts_seed_corpus.zip" "$hosts" +rm -rf "$hosts" + # The seed corpus is a separate flat archive for each fuzzer, # with a fixed name ${fuzzer}_seed_corpus.zip. -for d in "$(dirname "$0")/../test/fuzz/fuzz-"*; do +for d in test/fuzz/fuzz-*; do zip -jqr "$OUT/$(basename "$d")_seed_corpus.zip" "$d" done @@ -67,10 +89,24 @@ df="$build/dns-fuzzing" git clone --depth 1 https://github.com/CZ-NIC/dns-fuzzing "$df" zip -jqr "$OUT/fuzz-dns-packet_seed_corpus.zip" "$df/packet" -install -Dt "$OUT/src/shared/" "$build"/src/shared/libsystemd-shared-*.so +install -Dt "$OUT/src/shared/" \ + "$build"/src/shared/libsystemd-shared-*.so \ + "$build"/src/core/libsystemd-core-*.so wget -O "$OUT/fuzz-json.dict" https://raw.githubusercontent.com/rc0r/afl-fuzz/master/dictionaries/json.dict find "$build" -maxdepth 1 -type f -executable -name "fuzz-*" -exec mv {} "$OUT" \; find src -type f -name "fuzz-*.dict" -exec cp {} "$OUT" \; cp src/fuzz/*.options "$OUT" + +if [[ "$MERGE_WITH_OSS_FUZZ_CORPORA" == "yes" ]]; then + for f in "$OUT/"fuzz-*; do + [[ -x "$f" ]] || continue + fuzzer=$(basename "$f") + t=$(mktemp) + if wget -O "$t" "https://storage.googleapis.com/systemd-backup.clusterfuzz-external.appspot.com/corpus/libFuzzer/systemd_${fuzzer}/public.zip"; then + zipmerge "$OUT/${fuzzer}_seed_corpus.zip" "$t" + fi + rm -rf "$t" + done +fi diff --git a/tools/update-man-rules.py b/tools/update-man-rules.py index 31ed91c43..3a8c31dc3 100755 --- a/tools/update-man-rules.py +++ b/tools/update-man-rules.py @@ -3,9 +3,10 @@ from __future__ import print_function import collections +import glob import sys +from pathlib import Path import pprint -from os.path import basename from xml_helper import xml_parse def man(page, number): @@ -56,7 +57,8 @@ manpages = [''' MESON_FOOTER = '''\ ] -# Really, do not edit.''' +# Really, do not edit. +''' def make_mesonfile(rules, dist_files): # reformat rules as @@ -76,13 +78,20 @@ def make_mesonfile(rules, dist_files): return '\n'.join((MESON_HEADER, pprint.pformat(lines)[1:-1], MESON_FOOTER)) if __name__ == '__main__': - pages = sys.argv[1:] + source_glob = sys.argv[1] + target = Path(sys.argv[2]) + + pages = glob.glob(source_glob) pages = (p for p in pages - if basename(p) not in { + if Path(p).name not in { 'systemd.directives.xml', 'systemd.index.xml', 'directives-template.xml'}) rules = create_rules(pages) - dist_files = (basename(p) for p in pages) - print(make_mesonfile(rules, dist_files)) + dist_files = (Path(p).name for p in pages) + text = make_mesonfile(rules, dist_files) + + tmp = target.with_suffix('.tmp') + tmp.write_text(text) + tmp.rename(target) diff --git a/units/factory-reset.target b/units/factory-reset.target index 99f383d6b..d2c35ee03 100644 --- a/units/factory-reset.target +++ b/units/factory-reset.target @@ -8,5 +8,5 @@ # (at your option) any later version. [Unit] -Description=Target that triggers factory reset. Does nothing by default. +Description=Factory Reset Documentation=man:systemd.special(7) diff --git a/units/meson.build b/units/meson.build index a9bf28f6d..8a3bd0da5 100644 --- a/units/meson.build +++ b/units/meson.build @@ -140,6 +140,8 @@ units = [ ['systemd-reboot.service', ''], ['systemd-rfkill.socket', 'ENABLE_RFKILL'], ['systemd-sysext.service', 'ENABLE_SYSEXT'], + ['systemd-sysupdate.timer', 'ENABLE_SYSUPDATE'], + ['systemd-sysupdate-reboot.timer', 'ENABLE_SYSUPDATE'], ['systemd-sysusers.service', 'ENABLE_SYSUSERS', 'sysinit.target.wants/'], ['systemd-tmpfiles-clean.service', 'ENABLE_TMPFILES'], @@ -220,9 +222,9 @@ in_units = [ ['systemd-network-generator.service', ''], ['systemd-networkd.service', 'ENABLE_NETWORKD'], ['systemd-networkd-wait-online.service', 'ENABLE_NETWORKD'], + ['systemd-networkd-wait-online@.service','ENABLE_NETWORKD'], ['systemd-nspawn@.service', ''], - ['systemd-oomd.service', 'ENABLE_OOMD', - 'dbus-org.freedesktop.oom1.service'], + ['systemd-oomd.service', 'ENABLE_OOMD'], ['systemd-portabled.service', 'ENABLE_PORTABLED', 'dbus-org.freedesktop.portable1.service'], ['systemd-userdbd.service', 'ENABLE_USERDB'], @@ -236,6 +238,8 @@ in_units = [ ['systemd-suspend.service', ''], ['systemd-sysctl.service', '', 'sysinit.target.wants/'], + ['systemd-sysupdate.service', 'ENABLE_SYSUPDATE'], + ['systemd-sysupdate-reboot.service', 'ENABLE_SYSUPDATE'], ['systemd-timedated.service', 'ENABLE_TIMEDATED', 'dbus-org.freedesktop.timedate1.service'], ['systemd-timesyncd.service', 'ENABLE_TIMESYNCD'], diff --git a/units/systemd-networkd-wait-online@.service.in b/units/systemd-networkd-wait-online@.service.in new file mode 100644 index 000000000..949695f53 --- /dev/null +++ b/units/systemd-networkd-wait-online@.service.in @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Wait for Network Interface %i to be Configured +Documentation=man:systemd-networkd-wait-online.service(8) +DefaultDependencies=no +Conflicts=shutdown.target +Requires=systemd-networkd.service +After=systemd-networkd.service +Before=network-online.target shutdown.target + +[Service] +Type=oneshot +ExecStart={{ROOTLIBEXECDIR}}/systemd-networkd-wait-online -i %i +RemainAfterExit=yes + +[Install] +WantedBy=network-online.target diff --git a/units/systemd-nspawn@.service.in b/units/systemd-nspawn@.service.in index d7bae83ac..e82575fb5 100644 --- a/units/systemd-nspawn@.service.in +++ b/units/systemd-nspawn@.service.in @@ -13,7 +13,7 @@ Documentation=man:systemd-nspawn(1) Wants=modprobe@tun.service modprobe@loop.service modprobe@dm-mod.service PartOf=machines.target Before=machines.target -After=network.target systemd-resolved.service modprobe@tun.service modprobe@loop.service modprobe@dm-mod.service +After=network.target modprobe@tun.service modprobe@loop.service modprobe@dm-mod.service RequiresMountsFor=/var/lib/machines/%i [Service] diff --git a/units/systemd-resolved.service.in b/units/systemd-resolved.service.in index 00812fb26..621fe3422 100644 --- a/units/systemd-resolved.service.in +++ b/units/systemd-resolved.service.in @@ -15,8 +15,8 @@ Documentation=https://www.freedesktop.org/wiki/Software/systemd/writing-network- Documentation=https://www.freedesktop.org/wiki/Software/systemd/writing-resolver-clients DefaultDependencies=no -After=systemd-sysusers.service systemd-networkd.service -Before=network.target nss-lookup.target shutdown.target +After=systemd-sysusers.service +Before=sysinit.target network.target nss-lookup.target shutdown.target Conflicts=shutdown.target Wants=nss-lookup.target @@ -54,5 +54,5 @@ User=systemd-resolve {{SERVICE_WATCHDOG}} [Install] -WantedBy=multi-user.target +WantedBy=sysinit.target Alias=dbus-org.freedesktop.resolve1.service diff --git a/units/systemd-sysupdate-reboot.service.in b/units/systemd-sysupdate-reboot.service.in new file mode 100644 index 000000000..9d7b7d165 --- /dev/null +++ b/units/systemd-sysupdate-reboot.service.in @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Reboot Automatically After System Update +Documentation=man:systemd-sysupdate-reboot.service(8) +ConditionVirtualization=!container + +[Service] +Type=oneshot +ExecStart={{ROOTLIBEXECDIR}}/systemd-sysupdate reboot + +[Install] +Also=systemd-sysupdate-reboot.timer diff --git a/units/systemd-sysupdate-reboot.timer b/units/systemd-sysupdate-reboot.timer new file mode 100644 index 000000000..95a44bfea --- /dev/null +++ b/units/systemd-sysupdate-reboot.timer @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Reboot Automatically After System Update +Documentation=man:systemd-sysupdate-reboot.service(8) +ConditionVirtualization=!container + +[Timer] +OnCalendar=4:10 +RandomizedDelaySec=30min + +[Install] +WantedBy=timers.target diff --git a/units/systemd-sysupdate.service.in b/units/systemd-sysupdate.service.in new file mode 100644 index 000000000..085a9c4a2 --- /dev/null +++ b/units/systemd-sysupdate.service.in @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Automatic System Update +Documentation=man:systemd-sysupdate.service(8) +Wants=network-online.target +After=network-online.target +ConditionVirtualization=!container + +[Service] +Type=simple +NotifyAccess=main +ExecStart={{ROOTLIBEXECDIR}}/systemd-sysupdate update +CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD CAP_SETFCAP CAP_SYS_ADMIN CAP_SETPCAP CAP_DAC_OVERRIDE CAP_LINUX_IMMUTABLE +NoNewPrivileges=yes +MemoryDenyWriteExecute=yes +ProtectHostname=yes +RestrictRealtime=yes +RestrictNamespaces=net +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +SystemCallFilter=@system-service @mount +SystemCallErrorNumber=EPERM +SystemCallArchitectures=native +LockPersonality=yes + +[Install] +Also=systemd-sysupdate.timer diff --git a/units/systemd-sysupdate.timer b/units/systemd-sysupdate.timer new file mode 100644 index 000000000..6ecd98d13 --- /dev/null +++ b/units/systemd-sysupdate.timer @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Automatic System Update +Documentation=man:systemd-sysupdate.service(8) + +# For containers we assume that the manager will handle updates. And we likely +# can't even access our backing block device anyway. +ConditionVirtualization=!container + +[Timer] +# Trigger the update 15min after boot, and then – on average – every 6h, but +# randomly distributed in a 2h…6h interval. In addition trigger things +# persistently once on each Saturday, to ensure that even on systems that are +# never booted up for long we have a chance to to do the update. +OnBootSec=15min +OnUnitActiveSec=2h +OnCalendar=Sat +RandomizedDelaySec=4h +Persistent=yes + +[Install] +WantedBy=timers.target diff --git a/units/systemd-udev-trigger.service b/units/systemd-udev-trigger.service index 3d0f7b4f2..5e9199506 100644 --- a/units/systemd-udev-trigger.service +++ b/units/systemd-udev-trigger.service @@ -19,5 +19,4 @@ ConditionPathIsReadWrite=/sys [Service] Type=oneshot RemainAfterExit=yes -ExecStart=-udevadm trigger --type=subsystems --action=add -ExecStart=-udevadm trigger --type=devices --action=add +ExecStart=-udevadm trigger --type=all --action=add --prioritized-subsystem=block,tpmrm diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in index d042bfb0d..990119827 100644 --- a/units/systemd-udevd.service.in +++ b/units/systemd-udevd.service.in @@ -16,6 +16,7 @@ Before=sysinit.target ConditionPathIsReadWrite=/sys [Service] +Delegate=pids DeviceAllow=block-* rwm DeviceAllow=char-* rwm Type=notify